]> Git Repo - VerusCoin.git/blame - src/pbaas/pbaas.cpp
Currency launch fixes and better algorithm for selecting multi-reserve inputs
[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
56fe75cb 13#include "main.h"
b2a98c42 14#include "rpc/pbaasrpc.h"
b2a98c42 15#include "base58.h"
85c51d62 16#include "timedata.h"
c3250dcd 17#include "transaction_builder.h"
b2a98c42 18
b2a98c42
MT
19CConnectedChains ConnectedChains;
20
21bool IsVerusActive()
22{
23 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
24}
25
56fe75cb 26bool IsVerusMainnetActive()
27{
28 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0);
29}
30
b2a98c42
MT
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 {
56fe75cb 53 const CBaseChainObject *retPtr;
54 const CChainObject<CBlockHeaderAndProof> *pNewHeader;
55 const CChainObject<CPartialTransactionProof> *pNewTx;
56 const CChainObject<CBlockHeaderProof> *pNewHeaderRef;
1fa4454d 57 const CChainObject<CPriorBlocksCommitment> *pPriors;
56fe75cb 58 const CChainObject<uint256> *pNewProofRoot;
989b1de1 59 const CChainObject<CReserveTransfer> *pExport;
34d1aa13 60 const CChainObject<CCrossChainProof> *pCrossChainProof;
56fe75cb 61 const CChainObject<CCompositeChainObject> *pCompositeChainObject;
b2a98c42
MT
62 };
63
64 retPtr = &bo;
65
66 switch(bo.objectType)
67 {
68 case CHAINOBJ_HEADER:
69 return pNewHeader->GetHash();
70
56fe75cb 71 case CHAINOBJ_TRANSACTION_PROOF:
b2a98c42
MT
72 return pNewTx->GetHash();
73
b2a98c42
MT
74 case CHAINOBJ_HEADER_REF:
75 return pNewHeaderRef->GetHash();
76
1fa4454d
MT
77 case CHAINOBJ_PRIORBLOCKS:
78 return pPriors->GetHash();
989b1de1 79
56fe75cb 80 case CHAINOBJ_PROOF_ROOT:
81 return pNewProofRoot->object;
82
989b1de1
MT
83 case CHAINOBJ_RESERVETRANSFER:
84 return pExport->GetHash();
34d1aa13
MT
85
86 case CHAINOBJ_CROSSCHAINPROOF:
87 return pCrossChainProof->GetHash();
88
56fe75cb 89 case CHAINOBJ_COMPOSITEOBJECT:
90 return pCrossChainProof->GetHash();
91
b2a98c42
MT
92 }
93 return uint256();
94}
95
96// used to export coins from one chain to another, if they are not native, they are represented on the other
97// chain as tokens
4ecaf167 98bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
99{
100 return true;
101}
d727514f 102
78819397 103bool IsCrossChainExportInput(const CScript &scriptSig)
b2a98c42 104{
9ec65f90 105 return true;
b2a98c42
MT
106}
107
108// used to validate import of coins from one chain to another. if they are not native and are supported,
109// they are represented o the chain as tokens
4ecaf167 110bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397 111{
d727514f 112 return true;
78819397
MT
113}
114bool IsCrossChainImportInput(const CScript &scriptSig)
b2a98c42 115{
9ec65f90 116 return true;
b2a98c42
MT
117}
118
119// used to validate a specific service reward based on the spending transaction
4ecaf167 120bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 121{
13ed2980
MT
122 // for each type of service reward, we need to check and see if the spender is
123 // correctly formatted to be a valid spend of the service reward. for notarization
124 // we ensure that the notarization and its outputs are valid and that the spend
125 // applies to the correct billing period
126 return true;
b2a98c42 127}
78819397
MT
128bool IsServiceRewardInput(const CScript &scriptSig)
129{
9ec65f90 130 return true;
78819397 131}
b2a98c42
MT
132
133// used as a proxy token output for a reserve currency on its fractional reserve chain
4ecaf167 134bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 135{
78819397
MT
136 return true;
137}
138bool IsReserveOutputInput(const CScript &scriptSig)
139{
9ec65f90 140 return true;
b2a98c42
MT
141}
142
4ecaf167 143bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
144{
145 return true;
146}
147bool IsReserveTransferInput(const CScript &scriptSig)
b2a98c42 148{
9ec65f90 149 return true;
78819397 150}
b2a98c42 151
4ecaf167 152bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
153{
154 return true;
155}
156bool IsReserveDepositInput(const CScript &scriptSig)
157{
9ec65f90 158 return true;
b2a98c42
MT
159}
160
4ecaf167 161bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 162{
78819397
MT
163 return true;
164}
165bool IsCurrencyStateInput(const CScript &scriptSig)
166{
9ec65f90 167 return true;
78819397 168}
b2a98c42 169
78819397 170// used to convert a fractional reserve currency into its reserve and back
4ecaf167 171bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
172{
173 return true;
b2a98c42 174}
d305d656 175bool IsReserveExchangeInput(const CScript &scriptSig)
176{
9ec65f90 177 return true;
d305d656 178}
179
b2a98c42
MT
180
181/*
182 * Verifies that the input objects match the hashes and returns the transaction.
183 *
184 * If the opRetTx has the op ret, this calculates based on the actual transaction and
185 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
186 * by ensuring that all objects are present on this chain, composing the opRet, and
187 * ensuring that the transaction then hashes to the correct txid.
188 *
189 */
190bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
191{
192 // enumerate through the objects and validate that they are objects of the expected type that hash
193 // to the value expected. return true if so
9ec65f90 194 return true;
b2a98c42
MT
195}
196
56fe75cb 197int8_t ObjTypeCode(const CBlockHeaderProof &obj)
b2a98c42
MT
198{
199 return CHAINOBJ_HEADER;
200}
201
56fe75cb 202int8_t ObjTypeCode(const uint256 &obj)
b2a98c42 203{
56fe75cb 204 return CHAINOBJ_PROOF_ROOT;
b2a98c42
MT
205}
206
56fe75cb 207int8_t ObjTypeCode(const CPartialTransactionProof &obj)
b2a98c42 208{
56fe75cb 209 return CHAINOBJ_TRANSACTION_PROOF;
b2a98c42
MT
210}
211
56fe75cb 212int8_t ObjTypeCode(const CBlockHeaderAndProof &obj)
b2a98c42
MT
213{
214 return CHAINOBJ_HEADER_REF;
215}
216
1fa4454d 217int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
b2a98c42 218{
1fa4454d 219 return CHAINOBJ_PRIORBLOCKS;
b2a98c42
MT
220}
221
989b1de1
MT
222int8_t ObjTypeCode(const CReserveTransfer &obj)
223{
224 return CHAINOBJ_RESERVETRANSFER;
225}
226
34d1aa13
MT
227int8_t ObjTypeCode(const CCrossChainProof &obj)
228{
229 return CHAINOBJ_CROSSCHAINPROOF;
230}
231
56fe75cb 232int8_t ObjTypeCode(const CCompositeChainObject &obj)
233{
234 return CHAINOBJ_COMPOSITEOBJECT;
235}
236
b2a98c42 237// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
56fe75cb 238CScript StoreOpRetArray(const std::vector<CBaseChainObject *> &objPtrs)
b2a98c42
MT
239{
240 CScript vData;
241 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
0b806be9 242 s << (int32_t)OPRETTYPE_OBJECTARR;
b2a98c42
MT
243 bool error = false;
244
245 for (auto pobj : objPtrs)
246 {
d48d49b6
MT
247 try
248 {
249 if (!DehydrateChainObject(s, pobj))
250 {
251 error = true;
252 break;
253 }
254 }
255 catch(const std::exception& e)
b2a98c42 256 {
d48d49b6 257 std::cerr << e.what() << '\n';
b2a98c42
MT
258 error = true;
259 break;
260 }
261 }
262
05578402
MT
263 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
264 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
6278cf9c 265
b2a98c42 266 std::vector<unsigned char> vch(s.begin(), s.end());
d48d49b6 267 return error ? CScript() : CScript() << OP_RETURN << vch;
b2a98c42
MT
268}
269
a8c2cb11
MT
270void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
271{
272 for (auto pobj : ora)
273 {
274 switch(pobj->objectType)
275 {
276 case CHAINOBJ_HEADER:
277 {
56fe75cb 278 delete (CChainObject<CBlockHeaderAndProof> *)pobj;
a8c2cb11
MT
279 break;
280 }
281
56fe75cb 282 case CHAINOBJ_TRANSACTION_PROOF:
a8c2cb11 283 {
56fe75cb 284 delete (CChainObject<CPartialTransactionProof> *)pobj;
a8c2cb11
MT
285 break;
286 }
287
56fe75cb 288 case CHAINOBJ_PROOF_ROOT:
a8c2cb11 289 {
56fe75cb 290 delete (CChainObject<uint256> *)pobj;
a8c2cb11
MT
291 break;
292 }
293
294 case CHAINOBJ_HEADER_REF:
295 {
56fe75cb 296 delete (CChainObject<CBlockHeaderProof> *)pobj;
a8c2cb11
MT
297 break;
298 }
299
300 case CHAINOBJ_PRIORBLOCKS:
301 {
302 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
303 break;
304 }
305
989b1de1
MT
306 case CHAINOBJ_RESERVETRANSFER:
307 {
308 delete (CChainObject<CReserveTransfer> *)pobj;
309 break;
310 }
311
34d1aa13
MT
312 case CHAINOBJ_CROSSCHAINPROOF:
313 {
314 delete (CChainObject<CCrossChainProof> *)pobj;
315 break;
316 }
317
56fe75cb 318 case CHAINOBJ_COMPOSITEOBJECT:
319 {
320 delete (CChainObject<CCompositeChainObject> *)pobj;
321 break;
322 }
323
a8c2cb11
MT
324 default:
325 delete pobj;
326 }
327 }
5d13ad1e 328 ora.clear();
a8c2cb11
MT
329}
330
b2a98c42
MT
331std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
332{
333 std::vector<unsigned char> vch;
334 std::vector<CBaseChainObject *> vRet;
335 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
336 {
337 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
338
1fa4454d
MT
339 int32_t opRetType;
340
341 try
342 {
343 s >> opRetType;
344 if (opRetType == OPRETTYPE_OBJECTARR)
345 {
346 CBaseChainObject *pobj;
347 while (!s.empty() && (pobj = RehydrateChainObject(s)))
348 {
349 vRet.push_back(pobj);
350 }
aeca4678
MT
351 if (!s.empty())
352 {
2d8cedc7 353 printf("failed to load all objects in opret");
ba256d3a 354 DeleteOpRetObjects(vRet);
2d8cedc7 355 vRet.clear();
aeca4678 356 }
1fa4454d
MT
357 }
358 }
359 catch(const std::exception& e)
b2a98c42 360 {
1fa4454d 361 std::cerr << e.what() << '\n';
ba256d3a 362 DeleteOpRetObjects(vRet);
1fa4454d 363 vRet.clear();
b2a98c42
MT
364 }
365 }
366 return vRet;
367}
368
56fe75cb 369CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
b2a98c42 370{
b2a98c42
MT
371 nVersion = PBAAS_VERSION_INVALID;
372 for (auto out : tx.vout)
373 {
13ed2980
MT
374 COptCCParams p;
375 if (IsPayToCryptoCondition(out.scriptPubKey, p))
b2a98c42 376 {
56fe75cb 377 // always take the first for now
378 if (p.evalCode == EVAL_SERVICEREWARD)
b2a98c42 379 {
56fe75cb 380 FromVector(p.vData[0], *this);
381 break;
b2a98c42
MT
382 }
383 }
384 }
385
386 if (validate)
387 {
388
389 }
390}
391
56fe75cb 392CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
13ed2980 393{
56fe75cb 394 int32_t _ccxOutputNum = 0;
395 int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
396
397 for (int i = 0; i < tx.vout.size(); i++)
13ed2980
MT
398 {
399 COptCCParams p;
56fe75cb 400 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
401 p.IsValid() &&
402 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
13ed2980 403 {
56fe75cb 404 FromVector(p.vData[0], *this);
405 ccxOutputNum = i;
406 break;
13ed2980
MT
407 }
408 }
13ed2980
MT
409}
410
cd853aa1 411CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
41f170fd 412{
56fe75cb 413 nVersion = PBAAS_VERSION_INVALID;
cd853aa1 414 COptCCParams p;
415 if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
416 {
417 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
418 {
419 FromVector(p.vData[0], *this);
420 }
421 }
422}
423
424std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
425{
426 std::vector<CCurrencyDefinition> retVal;
2f416b17 427 for (auto &out : tx.vout)
41f170fd 428 {
cd853aa1 429 CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
430 if (oneCur.IsValid())
41f170fd 431 {
cd853aa1 432 retVal.push_back(oneCur);
41f170fd
MT
433 }
434 }
cd853aa1 435 return retVal;
41f170fd
MT
436}
437
f8f61a6d 438#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
344a051d 439extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
70c48c13 440extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
344a051d 441extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
f8f61a6d 442extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
f2d873d0 443extern std::string VERUS_CHAINNAME;
f7917c6b 444extern uint160 VERUS_CHAINID;
f8f61a6d 445
b2a98c42
MT
446// ensures that the chain definition is valid and that there are no other definitions of the same name
447// that have been confirmed.
4ecaf167 448bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42
MT
449{
450 // the chain definition output can be spent when the chain is at the end of its life and only then
451 // TODO
452 return false;
453}
454
455// ensures that the chain definition is valid and that there are no other definitions of the same name
456// that have been confirmed.
cd853aa1 457bool CheckChainDefinitionOutputs(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
b2a98c42
MT
458{
459 // checked before a chain definition output script is accepted as a valid transaction
460
461 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
462 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
463 // 2) notarization output with conformant values
464 // 3) finalization output
465 // 3) notarization funding
466 //
467
468 // get the source transaction
469 uint256 blkHash;
470 CTransaction thisTx;
6c5d1151 471 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
b2a98c42
MT
472 {
473 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
474 return false;
475 }
476
cd853aa1 477 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
56fe75cb 478 CPBaaSNotarization notarization(thisTx);
479 CNotarizationFinalization finalization(thisTx);
cd853aa1 480 bool isVerusActive = IsVerusActive();
b2a98c42 481
cd853aa1 482 if (!notarization.IsValid() || !finalization.IsValid())
b2a98c42 483 {
cd853aa1 484 LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
b2a98c42
MT
485 return false;
486 }
487
cd853aa1 488 std::set<uint160> allCurrencyIDs;
489 for (auto &curPair : ConnectedChains.ReserveCurrencies())
b2a98c42 490 {
cd853aa1 491 allCurrencyIDs.insert(curPair.first);
492 }
493 allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
494 if (!isVerusActive)
495 {
496 allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
b2a98c42
MT
497 }
498
cd853aa1 499 bool isCoinbase = thisTx.IsCoinBase();
500 bool isVerified = false;
501 CIdentity activatedID(thisTx);
502
503 // currency definitions can be valid as follows:
504 // 1. original definition in a transaction that simultaneously sets the active currency bit on the identity of the same
505 // name and ID.
506 // 2. outputs of a coinbase transaction in block 1 that defines the parent currency, new currency, and any reserve currencies
507 // 3. currency import from the defining chain of the currency, which has not been implemented as of this comment
508 if (activatedID.IsValid() &&
509 activatedID.HasActiveCurrency() &&
510 chainDefs.size() == 1 &&
511 activatedID.parent == ASSETCHAINS_CHAINID &&
512 activatedID.GetID() == chainDefs[0].GetID())
513 {
514 isVerified = true;
515 }
516 else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
517 {
518 int32_t height1 = 1;
519 CScript expect = CScript() << height1;
520 opcodetype opcode = (opcodetype)*expect.begin();
521
522 if (opcode >= OP_1 && opcode <= OP_16)
523 {
524 isVerified = (thisTx.vin[0].scriptSig.size() >= 1 && CScript::DecodeOP_N(opcode) == height1) ||
525 (thisTx.vin[0].scriptSig.size() >= 2 && thisTx.vin[0].scriptSig[0] == OP_PUSHDATA1 && (int)thisTx.vin[0].scriptSig[1] == height1);
526 }
527 else
528 {
529 isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
530 }
531
532 for (auto &chainDef : chainDefs)
533 {
534 uint160 chainID = chainDef.GetID();
535 if (!chainDef.IsValid())
536 {
537 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
538 return false;
539 }
540 if (!allCurrencyIDs.count(chainID))
541 {
542 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
543 return false;
544 }
545 allCurrencyIDs.erase(chainID);
546 }
547 if (allCurrencyIDs.size())
548 {
549 LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
550 return false;
551 }
552 }
553
554 return isVerified;
b2a98c42
MT
555}
556
815a42a6 557CCurrencyValueMap CCrossChainExport::CalculateExportFee(const CCurrencyValueMap &fees, int numIn)
989b1de1 558{
56fe75cb 559 CCurrencyValueMap retVal;
560
815a42a6 561 if (numIn > MAX_EXPORT_INPUTS)
989b1de1 562 {
56fe75cb 563 return retVal;
989b1de1
MT
564 }
565 static const arith_uint256 satoshis(100000000);
566
815a42a6 567 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
56fe75cb 568
815a42a6 569 for (auto &feePair : fees.valueMap)
56fe75cb 570 {
571 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
572 }
573 return retVal;
574}
575
815a42a6 576CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
577{
578 return CalculateExportFee(totalFees, numInputs);
579}
580
56fe75cb 581CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
582{
583 CCurrencyValueMap retVal;
989b1de1 584
56fe75cb 585 for (auto &feePair : CalculateExportFee().valueMap)
586 {
587 CAmount feeAmount = feePair.second;
588 auto it = totalFees.valueMap.find(feePair.first);
589 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
590 }
591 return retVal;
989b1de1
MT
592}
593
b2a98c42
MT
594bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
595{
2fd1f0fb 596 bool retval = false;
b2a98c42 597 LOCK(cs_mergemining);
23d61f0a 598
3016973b 599 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 600
b2a98c42
MT
601 auto chainIt = mergeMinedChains.find(chainID);
602 if (chainIt != mergeMinedChains.end())
603 {
604 arith_uint256 target;
605 target.SetCompact(chainIt->second.block.nBits);
606 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
607 {
608 // make sure we don't just match by target
56fe75cb 609 if (removeRange.first->second->GetID() == chainID)
b2a98c42
MT
610 {
611 mergeMinedTargets.erase(removeRange.first);
612 break;
613 }
614 }
615 mergeMinedChains.erase(chainID);
2fd1f0fb 616 dirty = retval = true;
b2a98c42
MT
617
618 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 619 //if (!mergeMinedChains.size())
620 //{
621 // sem_submitthread.post();
622 //}
b2a98c42 623 }
2fd1f0fb 624 return retval;
b2a98c42
MT
625}
626
627// remove merge mined chains added and not updated since a specific time
9ec65f90 628void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
b2a98c42
MT
629{
630 vector<uint160> toRemove;
631
632 LOCK(cs_mergemining);
633 for (auto blkData : mergeMinedChains)
634 {
635 if (blkData.second.block.nTime < pruneBefore)
636 {
637 toRemove.push_back(blkData.first);
638 }
639 }
640
641 for (auto id : toRemove)
642 {
3016973b 643 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
644 RemoveMergedBlock(id);
645 }
646}
647
648// adds or updates merge mined blocks
649// returns false if failed to add
650bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
651{
b2a98c42
MT
652 // determine if we should replace one or add to the merge mine vector
653 {
654 LOCK(cs_mergemining);
655
2fd1f0fb 656 arith_uint256 target;
56fe75cb 657 uint160 cID = blkData.GetID();
b2a98c42
MT
658 auto it = mergeMinedChains.find(cID);
659 if (it != mergeMinedChains.end())
660 {
1fa4454d 661 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 662 }
1fa4454d 663 target.SetCompact(blkData.block.nBits);
23d61f0a 664
3016973b 665 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 666
1fa4454d 667 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 668 dirty = true;
b2a98c42 669 }
bce52b27 670 return true;
b2a98c42
MT
671}
672
673bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
674{
675 {
676 LOCK(cs_mergemining);
677 auto chainIt = mergeMinedChains.find(chainID);
678 if (chainIt != mergeMinedChains.end())
679 {
680 rpcChainData = (CRPCChainData)chainIt->second;
681 return true;
682 }
683 return false;
684 }
685}
686
687// this returns a pointer to the data without copy and assumes the lock is held
688CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
689{
690 {
691 auto chainIt = mergeMinedChains.find(chainID);
692 if (chainIt != mergeMinedChains.end())
693 {
694 return &chainIt->second;
695 }
696 return NULL;
697 }
698}
699
9ec65f90 700void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
e771a884 701{
3016973b 702 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 703 {
704 LOCK(cs_mergemining);
cff3c5ad 705
2830db29 706 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
707 }
e771a884 708 sem_submitthread.post();
709}
710
0ab273d2 711void CConnectedChains::CheckImports()
712{
713 sem_submitthread.post();
714}
715
f8f61a6d 716// get the latest block header and submit one block at a time, returning after there are no more
717// matching blocks to be found
718vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
719{
720 std::set<uint160> inHeader;
f8f61a6d 721 bool submissionFound;
722 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
723 vector<pair<string, UniValue>> results;
724
f8f61a6d 725 CBlockHeader bh;
726 arith_uint256 lastHash;
b2a98c42
MT
727 CPBaaSBlockHeader pbh;
728
02560af7 729 do
b2a98c42 730 {
02560af7 731 submissionFound = false;
b2a98c42 732 {
02560af7 733 LOCK(cs_mergemining);
734 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
735 // common, merge mined headers for notarization, drop out on any submission
736 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 737 {
02560af7 738 // add the PBaaS chain ids from this header to a set for search
739 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 740 {
02560af7 741 inHeader.insert(pbh.chainID);
742 }
b2a98c42 743
33d6f38a 744 uint160 chainID;
02560af7 745 // now look through all targets that are equal to or above the hash of this header
746 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
747 {
56fe75cb 748 chainID = chainIt->second->GetID();
02560af7 749 if (inHeader.count(chainID))
2830db29 750 {
02560af7 751 // first, check that the winning header matches the block that is there
752 CPBaaSPreHeader preHeader(chainIt->second->block);
753 preHeader.SetBlockData(headerIt->second);
754
755 // check if the block header matches the block's specific data, only then can we create a submission from this block
756 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 757 {
02560af7 758 // save block as is, remove the block from merged headers, replace header, and submit
759 chainData = *chainIt->second;
760
761 *(CBlockHeader *)&chainData.block = headerIt->second;
762
02560af7 763 submissionFound = true;
f8f61a6d 764 }
3016973b
MT
765 //else // not an error condition. code is here for debugging
766 //{
767 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
768 //}
cff3c5ad 769 }
3016973b
MT
770 //else // not an error condition. code is here for debugging
771 //{
772 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
773 //}
02560af7 774 }
2830db29 775
02560af7 776 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
777 if (submissionFound)
778 {
779 // once it is going to be submitted, remove block from this chain until a new one is added again
780 RemoveMergedBlock(chainID);
781 break;
782 }
783 else
02560af7 784 {
785 qualifiedHeaders.erase(headerIt);
b2a98c42 786 }
f8f61a6d 787 }
02560af7 788 }
789 if (submissionFound)
790 {
791 // submit one block and loop again. this approach allows multiple threads
792 // to collectively empty the submission queue, mitigating the impact of
793 // any one stalled daemon
794 UniValue submitParams(UniValue::VARR);
795 submitParams.push_back(EncodeHexBlk(chainData.block));
796 UniValue result, error;
797 try
f8f61a6d 798 {
02560af7 799 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
800 result = find_value(result, "result");
801 error = find_value(result, "error");
b2a98c42 802 }
02560af7 803 catch (exception e)
804 {
805 result = UniValue(e.what());
806 }
807 results.push_back(make_pair(chainData.chainDefinition.name, result));
808 if (result.isStr() || !error.isNull())
809 {
810 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());
811 }
812 else
813 {
814 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
815 }
816 }
817 } while (submissionFound);
b2a98c42
MT
818 return results;
819}
820
f8f61a6d 821// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
822uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
823{
824 vector<uint160> inHeader;
825 vector<UniValue> toCombine;
826 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 827 arith_uint256 target(0);
b2a98c42
MT
828
829 CPBaaSBlockHeader pbh;
830
b2a98c42 831 {
f8f61a6d 832 LOCK(cs_mergemining);
b2a98c42 833
f8f61a6d 834 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
835
836 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 837 {
f8f61a6d 838 if (bh.GetPBaaSHeader(pbh, i))
839 {
840 inHeader.push_back(pbh.chainID);
841 }
842 }
843
844 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 845 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 846 for (uint32_t i = 0; i < inHeader.size(); i++)
847 {
848 auto it = mergeMinedChains.find(inHeader[i]);
849 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
850 {
851 bh.DeletePBaaSHeader(i);
852 }
b2a98c42 853 }
b2a98c42 854
b2a98c42
MT
855 for (auto chain : mergeMinedChains)
856 {
857 // get the native PBaaS header for each chain and put it into the
858 // header we are given
326f5f88 859 // it must have itself in as a PBaaS header
56fe75cb 860 uint160 cid = chain.second.GetID();
3a2a1777 861 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
862 {
863 if (!bh.AddUpdatePBaaSHeader(pbh))
864 {
865 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 866 break;
867 }
868 else
869 {
870 arith_uint256 t;
871 t.SetCompact(chain.second.block.nBits);
872 if (t > target)
873 {
874 target = t;
875 }
b2a98c42
MT
876 }
877 }
326f5f88
MT
878 else
879 {
880 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
881 }
b2a98c42 882 }
2830db29 883 dirty = false;
b2a98c42 884 }
326f5f88 885
f8f61a6d 886 return target.GetCompact();
b2a98c42
MT
887}
888
ccac80a3 889bool CConnectedChains::IsVerusPBaaSAvailable()
890{
56fe75cb 891 return notaryChainVersion >= "0.6.4";
ccac80a3 892}
893
f8f61a6d 894extern string PBAAS_HOST, PBAAS_USERPASS;
895extern int32_t PBAAS_PORT;
6bb9fdbc 896bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 897{
6bb9fdbc 898 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 899 {
6bb9fdbc 900 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 901 if (uniVer.isStr())
902 {
2156d3d0 903 LOCK(cs_mergemining);
f8f61a6d 904 notaryChainVersion = uni_get_str(uniVer);
6bb9fdbc 905 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
56fe75cb 906 CCurrencyDefinition chainDef(chainDefUni);
f8f61a6d 907 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
908 }
9f0c14b2 909 }
ccac80a3 910 return IsVerusPBaaSAvailable();
9f0c14b2
MT
911}
912
56fe75cb 913uint32_t CConnectedChains::NotaryChainHeight()
914{
915 LOCK(cs_mergemining);
916 return notaryChainHeight;
917}
918
2299bd95 919bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2
MT
920{
921 if (IsVerusActive())
922 {
ccac80a3 923 notaryChainVersion = "";
9f0c14b2
MT
924 }
925 else
926 {
927 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 928 // tolerate only 15 second timeout
9a891caf 929 UniValue chainInfo, chainDef;
930 try
931 {
932 UniValue params(UniValue::VARR);
2fd1f0fb 933 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 934 if (!chainInfo.isNull())
935 {
936 params.push_back(VERUS_CHAINNAME);
56fe75cb 937 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
9a891caf 938
939 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
940 {
56fe75cb 941 // if we have not past block 1 yet, store the best known update of our current state
942 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
943 {
944 bool success = false;
945 params.clear();
946 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
947 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
948 if (!chainDef.isNull())
949 {
950 CCurrencyDefinition currencyDef(chainDef);
951 if (currencyDef.IsValid())
952 {
953 thisChain = currencyDef;
954 if (NotaryChainHeight() >= thisChain.startBlock)
955 {
956 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
957 }
958 success = true;
959 }
960 }
961 return success;
962 }
9a891caf 963 return true;
964 }
965 }
966 } catch (exception e)
4fa3b13d 967 {
56fe75cb 968 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
4fa3b13d 969 }
9f0c14b2 970 }
9a891caf 971 notaryChainVersion = "";
4fa3b13d 972 return false;
9f0c14b2
MT
973}
974
56fe75cb 975int CConnectedChains::GetThisChainPort() const
976{
977 int port;
978 string host;
979 for (auto node : defaultPeerNodes)
980 {
981 SplitHostPort(node.networkAddress, port, host);
982 if (port)
983 {
984 return port;
985 }
986 }
987 return 0;
988}
989
41f170fd
MT
990CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
991{
992 CCoinbaseCurrencyState currencyState;
993 CBlock block;
994 LOCK(cs_main);
767f8e36 995 bool isVerusActive = IsVerusActive();
996 if (!isVerusActive &&
f37d48ca 997 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
41f170fd
MT
998 height != 0 &&
999 height <= chainActive.Height() &&
1000 chainActive[height] &&
88d014d0 1001 ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1b1a31e7 1002 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
41f170fd 1003 {
1b1a31e7 1004 return currencyState;
41f170fd 1005 }
7d8fcc31 1006 else
1007 {
56fe75cb 1008 return GetInitialCurrencyState(thisChain);
7d8fcc31 1009 }
41f170fd
MT
1010}
1011
a4f9bc97 1012bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
c3250dcd
MT
1013{
1014 LOCK(cs_mergemining);
b7c685b8 1015
a4f9bc97 1016 if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1017 {
1018 return false;
c3250dcd
MT
1019 }
1020 latestMiningOutputs = minerOutputs;
1021 latestDestination = firstDestinationOut;
bb6c3482 1022 return true;
c3250dcd
MT
1023}
1024
09551a1c 1025CCurrencyDefinition &CConnectedChains::GetCachedCurrency(const uint160 &currencyID)
1026{
1027 LOCK(cs_main);
1028 CCurrencyDefinition currencyDef;
1029 auto it = currencyDefCache.find(currencyID);
1030 if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
1031 (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef)))
1032 {
1033 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1034 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1035 }
1036 if (it == currencyDefCache.end())
1037 {
1038 currencyDefCache[currencyID] = currencyDef;
1039 }
1040 return currencyDefCache[currencyID];
1041}
1042
c3250dcd
MT
1043void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1044{
1045 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1046 {
1047 if (!nHeight)
1048 {
1049 return;
1050 }
1051
56fe75cb 1052 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
c3250dcd 1053
9ace9b55 1054 LOCK(cs_main);
1055
56fe75cb 1056 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1057
c3250dcd
MT
1058 // get all available transfer outputs to aggregate into export transactions
1059 if (GetUnspentChainTransfers(transferOutputs))
1060 {
bb6c3482 1061 if (!transferOutputs.size())
1062 {
1063 return;
1064 }
815a42a6 1065
1066 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1067 uint160 bookEnd({uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff"))});
1068 uint160 lastChain = bookEnd;
1069 transferOutputs.insert(std::make_pair(bookEnd, std::make_pair(CInputDescriptor(), CReserveTransfer())));
1070 CCurrencyDefinition lastChainDef;
1071
56fe75cb 1072 for (auto &output : transferOutputs)
1073 {
f7917c6b 1074 CCurrencyDefinition sourceDef, destDef, systemDef;
f7917c6b 1075
815a42a6 1076 if (output.first != bookEnd)
56fe75cb 1077 {
815a42a6 1078 auto it = currencyDefCache.find(output.second.second.currencyID);
1079 if ((it != currencyDefCache.end() && !(sourceDef = it->second).IsValid()) ||
1080 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.currencyID, sourceDef)))
a041b57e 1081 {
815a42a6 1082 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1083 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1084 continue;
a041b57e 1085 }
815a42a6 1086 if (it == currencyDefCache.end())
cd853aa1 1087 {
815a42a6 1088 currencyDefCache[output.second.second.currencyID] = sourceDef;
cd853aa1 1089 }
815a42a6 1090
1091 it = currencyDefCache.find(output.second.second.destCurrencyID);
1092 if ((it != currencyDefCache.end() && !(destDef = it->second).IsValid()) ||
1093 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.destCurrencyID, destDef)))
cd853aa1 1094 {
815a42a6 1095 printf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1096 LogPrintf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1097 continue;
cd853aa1 1098 }
815a42a6 1099 if (it == currencyDefCache.end())
1100 {
1101 currencyDefCache[output.second.second.destCurrencyID] = destDef;
1102 }
1103
f7917c6b 1104 it = currencyDefCache.find(destDef.systemID);
1105 if ((it != currencyDefCache.end() && !(systemDef = it->second).IsValid()) ||
1106 (it == currencyDefCache.end() && !GetCurrencyDefinition(destDef.systemID, systemDef)))
1107 {
1108 printf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1109 LogPrintf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1110 continue;
1111 }
1112 if (it == currencyDefCache.end())
1113 {
1114 currencyDefCache[destDef.systemID] = systemDef;
1115 }
56fe75cb 1116
815a42a6 1117 // if destination is a token on the current chain, consider it its own system
1118 if (destDef.systemID == thisChainID)
1119 {
1120 systemDef = destDef;
1121 }
56fe75cb 1122 }
c3250dcd 1123
78a36746 1124 // get chain target and see if it is the same
815a42a6 1125 if (lastChain == bookEnd || output.first == lastChain)
c3250dcd 1126 {
815a42a6 1127 txInputs.push_back(output.second);
78a36746 1128 }
1129 else
1130 {
1131 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
56fe75cb 1132 // we need an unspent export output to export, or use the last one of it is an export to the same
1133 // system
815a42a6 1134 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1135 lastChainDef = currencyDefCache[lastChain];
1136
1137 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
c3250dcd 1138 {
78a36746 1139 auto lastExport = *exportOutputs.begin();
815a42a6 1140 bool oneFullSize = txInputs.size() >= CCrossChainExport::MIN_INPUTS;
c3250dcd 1141
815a42a6 1142 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || oneFullSize)
c3250dcd
MT
1143 {
1144 boost::optional<CTransaction> oneExport;
1145
1146 // make one or more transactions that spends the last export and all possible cross chain transfers
1147 while (txInputs.size())
1148 {
1149 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1150
56fe75cb 1151 int inputsLeft = txInputs.size();
815a42a6 1152 int numInputs = inputsLeft > CCrossChainExport::MAX_EXPORT_INPUTS ? CCrossChainExport::MAX_EXPORT_INPUTS : inputsLeft;
c3250dcd 1153
56fe75cb 1154 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1155 {
1156 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1157 }
1158 inputsLeft = txInputs.size() - numInputs;
815a42a6 1159
1160 // if we have already made one and don't have enough to make another
1161 // without going under the input minimum, wait until next time for the others
1162 if (numInputs > 0 && numInputs < CCrossChainExport::MIN_INPUTS && oneFullSize)
c3250dcd 1163 {
815a42a6 1164 break;
c3250dcd
MT
1165 }
1166
1167 // each time through, we make one export transaction with the remainder or a subset of the
1168 // reserve transfer inputs. inputs can be:
1169 // 1. transfers of reserve for fractional reserve chains
1170 // 2. pre-conversions for pre-launch participation in the premine
1171 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1172 //
1173 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1174 // or reserve token inputs.
1175 //
1176 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1177 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1178 // as part of the import process
1179 //
1180 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1181 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
5cd8b21a 1182 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1183 // Verus chain.
c3250dcd 1184 //
56fe75cb 1185 CCurrencyValueMap totalTxFees;
1186 CCurrencyValueMap totalAmounts;
fe9545ee 1187 CAmount exportOutVal = 0;
c3250dcd
MT
1188 std::vector<CBaseChainObject *> chainObjects;
1189
1190 // first, we must add the export output from the current export thread to this chain
56fe75cb 1191 if (oneExport.is_initialized())
c3250dcd
MT
1192 {
1193 // spend the last export transaction output
1194 CTransaction &tx = oneExport.get();
1195 COptCCParams p;
1196 int j;
1197 for (j = 0; j < tx.vout.size(); j++)
1198 {
1199 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1200 {
1201 break;
1202 }
1203 }
1204
1205 // had to be found and valid if we made the tx
1206 assert(j < tx.vout.size() && p.IsValid());
1207
1208 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
fe9545ee 1209 exportOutVal = tx.vout[j].nValue;
c3250dcd
MT
1210 }
1211 else
1212 {
1213 // spend the recentExportIt output
78a36746 1214 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
fe9545ee 1215 exportOutVal = lastExport.second.second.nValue;
c3250dcd
MT
1216 }
1217
715182a4 1218 COptCCParams p;
56fe75cb 1219 std::vector<int> toRemove;
815a42a6 1220
c3250dcd
MT
1221 for (int j = 0; j < numInputs; j++)
1222 {
1223 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
56fe75cb 1224 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1225 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1226
f7917c6b 1227 // TODO: make fee currency calculation more flexible on conversion
0ab273d2 1228 // rules should be pay fee in native currency of destination system
1229 // if source is same currency
56fe75cb 1230 CCurrencyValueMap newTransferOutput;
815a42a6 1231 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue + txInputs[j].second.nFees;
1232
1233 //printf("input:\n%s\n", newTransferInput.ToUniValue().write().c_str());
1234 //printf("output:\n%s\n", newTransferOutput.ToUniValue().write().c_str());
56fe75cb 1235
1236 if ((newTransferInput - newTransferOutput).HasNegative())
81fc0174 1237 {
dd1b8052 1238 // if this transfer is invalid and claims to carry more funds than it does, we consume it since it won't properly verify as a transfer, and
5cd8b21a 1239 // it is too expensive to let it force evaluation repeatedly. this condition should not get by normal checks, but just in case, don't let it slow transfers
1240 // we should formalize this into a chain contribution or amount adjustment.
56fe75cb 1241 printf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1242 LogPrintf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1243 toRemove.push_back(j);
81fc0174 1244 }
1245 else
1246 {
0ab273d2 1247 totalTxFees += txInputs[j].second.CalculateFee(txInputs[j].second.flags, txInputs[j].second.nValue);
815a42a6 1248 totalAmounts += newTransferInput;
f7917c6b 1249 CReserveTransfer rt(txInputs[j].second);
f7917c6b 1250 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(rt), rt));
81fc0174 1251 }
1252 }
815a42a6 1253
56fe75cb 1254 // remove in reverse order so one removal does not affect the position of the next
1255 for (int j = toRemove.size() - 1; j >= 0; j--)
1256 {
1257 txInputs.erase(txInputs.begin() + toRemove[j]);
1258 numInputs--;
1259 }
815a42a6 1260
1261 // this logic may cause us to create a tx that will get rejected, but we will never wait too long
1262 if (!numInputs || (oneFullSize && (nHeight - lastExport.second.first) < CCrossChainExport::MIN_BLOCKS && numInputs < CCrossChainExport::MIN_INPUTS))
81fc0174 1263 {
81fc0174 1264 continue;
c3250dcd
MT
1265 }
1266
815a42a6 1267 //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
1268
1269 CCrossChainExport ccx(lastChain, numInputs, totalAmounts, totalTxFees);
56fe75cb 1270
1271 // make extra outputs for fees in each currency
1272 for (auto &outPair : ccx.CalculateExportFee().valueMap)
1273 {
1274 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
815a42a6 1275 outPair.first, outPair.second, 0, outPair.first, DestinationToTransferDestination(feeOutput));
56fe75cb 1276 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1277 }
c3250dcd 1278
410af2c6 1279 // do a preliminary check
1280 CReserveTransactionDescriptor rtxd;
56fe75cb 1281 std::vector<CTxOut> vOutputs;
9ace9b55 1282
56fe75cb 1283 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, chainObjects, vOutputs))
410af2c6 1284 {
8bb25a86 1285 DeleteOpRetObjects(chainObjects);
1286
410af2c6 1287 printf("%s: failed to create valid exports\n", __func__);
1288 LogPrintf("%s: failed to create valid exports\n", __func__);
9ace9b55 1289
1290 // debugging output
8bb25a86 1291 printf("%s: failed to export outputs:\n", __func__);
56fe75cb 1292 for (auto oneout : vOutputs)
9ace9b55 1293 {
9ace9b55 1294 UniValue uniOut;
1295 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1296 printf("%s\n", uniOut.write(true, 2).c_str());
1297 }
410af2c6 1298 }
8bb25a86 1299 else
410af2c6 1300 {
56fe75cb 1301 CCcontract_info CC;
1302 CCcontract_info *cp;
1303
1304 // debugging out
58b4e7b5 1305 // printf("%s: exported outputs:\n", __func__);
56fe75cb 1306 for (auto &oneout : chainObjects)
8bb25a86 1307 {
56fe75cb 1308 if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1309 {
1310 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
58b4e7b5 1311 // printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
56fe75cb 1312 }
8bb25a86 1313 }
c3250dcd 1314
8bb25a86 1315 CScript opRet = StoreOpRetArray(chainObjects);
1316 DeleteOpRetObjects(chainObjects);
c3250dcd 1317
815a42a6 1318 // now send transferred currencies to a reserve deposit
56fe75cb 1319 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1320 for (auto &oneCurrencyOut : ccx.totalAmounts.valueMap)
1321 {
1322 CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
815a42a6 1323
1324 // if the destination is the not the source currency, and
1325 // the destination is not another blockchain that controls the source currency, store in reserve
1326 if (!(oneCurrencyOut.first == lastChain ||
1327 (lastChainDef.systemID != ASSETCHAINS_CHAINID && oneDef.systemID == lastChainDef.systemID)))
56fe75cb 1328 {
1329 CAmount nativeOut = oneDef.GetID() == ASSETCHAINS_CHAINID ? oneCurrencyOut.second : 0;
c3250dcd 1330
815a42a6 1331 // send the entire amount to a reserve deposit output of the specific chain
1332 // we receive our fee on the other chain, when it comes back, or if a token,
1333 // when it gets imported back to the chain
0ab273d2 1334 std::vector<CTxDestination> indexDests({CKeyID(lastChainDef.GetConditionID(EVAL_RESERVE_DEPOSIT))});
56fe75cb 1335 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
c3250dcd 1336
0ab273d2 1337 CTokenOutput ro;
1338
1339 if (!nativeOut)
1340 {
1341 ro = CTokenOutput(oneCurrencyOut.first, oneCurrencyOut.second);
1342 }
c3250dcd 1343
56fe75cb 1344 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_DEPOSIT, dests, 1, &ro), &indexDests),
1345 nativeOut);
1346 }
1347 }
c3250dcd 1348
56fe75cb 1349 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
c3250dcd 1350
56fe75cb 1351 // send native amount of zero to a cross chain export output of the specific chain
0ab273d2 1352 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(lastChainDef.GetConditionID(EVAL_CROSSCHAIN_EXPORT))});
1353 if (lastChain != lastChainDef.systemID)
1354 {
1355 indexDests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_CROSSCHAIN_EXPORT)));
1356 }
56fe75cb 1357 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
c3250dcd 1358
56fe75cb 1359 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests),
1360 exportOutVal);
c3250dcd 1361
8bb25a86 1362 tb.AddOpRet(opRet);
1363 tb.SetFee(0);
c3250dcd 1364
88d014d0 1365 TransactionBuilderResult buildResult(tb.Build());
c3250dcd 1366
88d014d0 1367 if (!buildResult.IsError() && buildResult.IsTx())
c3250dcd 1368 {
8bb25a86 1369 // replace the last one only if we have a valid new one
88d014d0 1370 CTransaction tx = buildResult.GetTxOrThrow();
8bb25a86 1371
1372 LOCK2(cs_main, mempool.cs);
1373 static int lastHeight = 0;
1374 // remove conflicts, so that we get in
1375 std::list<CTransaction> removed;
1376 mempool.removeConflicts(tx, removed);
1377
1378 // add to mem pool, prioritize according to the fee we will get, and relay
1379 printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1380 LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1381 if (myAddtomempool(tx))
1382 {
1383 uint256 hash = tx.GetHash();
56fe75cb 1384 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1385 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
8bb25a86 1386 RelayTransaction(tx);
1387 }
1388 }
1389 else
1390 {
1391 // we can't do any more useful work for this chain if we failed here
88d014d0 1392 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1393 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
8bb25a86 1394 break;
c3250dcd
MT
1395 }
1396 }
8bb25a86 1397
c3250dcd
MT
1398 // erase the inputs we've attempted to spend
1399 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1400 }
1401 }
1402 }
1403 }
815a42a6 1404 lastChain = output.first;
c3250dcd 1405 }
0ab273d2 1406 CheckImports();
1407 }
1408 }
1409}
1410
1411void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
1412{
1413 uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
1414 LOCK2(cs_main, mempool.cs);
1415
1416 // sign and commit the transactions
1417 for (auto &tx : transactions)
1418 {
09551a1c 1419 //DEBUGGING
58b4e7b5 1420 // extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
1421 // UniValue jsonTX(UniValue::VOBJ);
1422 // TxToJSON(tx, uint256(), jsonTX);
1423 // printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
09551a1c 1424
0ab273d2 1425 CMutableTransaction newTx(tx);
1426
1427 // sign the transaction and submit
1428 bool signSuccess;
1429 for (int i = 0; i < tx.vin.size(); i++)
1430 {
1431 SignatureData sigdata;
1432 CAmount value;
1433 CScript outputScript;
1434
1435 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
1436 {
1437 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
1438 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
1439 }
1440 else
1441 {
1442 CCoinsViewCache view(pcoinsTip);
1443 CCoins coins;
1444 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
1445 {
1446 fprintf(stderr,"%s: cannot get input coins from tx: %s, output: %d\n", __func__, tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
1447 LogPrintf("%s: cannot get input coins from tx: %s, output: %d\n", __func__, tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
1448 break;
1449 }
1450 value = coins.vout[tx.vin[i].prevout.n].nValue;
1451 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
1452 }
1453
1454 signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
1455
1456 if (!signSuccess)
1457 {
1458 fprintf(stderr,"%s: failure to sign refund transaction\n", __func__);
1459 LogPrintf("%s: failure to sign refund transaction\n", __func__);
1460 break;
1461 } else {
1462 UpdateTransaction(newTx, i, sigdata);
1463 }
1464 }
1465
1466 if (signSuccess)
1467 {
1468 // push to local node and sync with wallets
1469 CValidationState state;
1470 bool fMissingInputs;
1471 CTransaction signedTx(newTx);
09551a1c 1472
1473 //DEBUGGING
58b4e7b5 1474 //TxToJSON(tx, uint256(), jsonTX);
1475 //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
09551a1c 1476
0ab273d2 1477 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
1478 if (state.IsInvalid()) {
1479 fprintf(stderr,"%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1480 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1481 } else {
1482 if (fMissingInputs) {
1483 fprintf(stderr,"%s: missing inputs\n", __func__);
1484 LogPrintf("%s: missing inputs\n", __func__);
1485 }
1486 else
1487 {
1488 fprintf(stderr,"%s: rejected by memory pool for\n", __func__);
1489 LogPrintf("%s: rejected by memory pool for\n", __func__);
1490 }
1491 }
1492 break;
1493 }
1494 else
1495 {
1496 RelayTransaction(signedTx);
1497 }
c3250dcd
MT
1498 }
1499 }
1500}
1501
0ab273d2 1502// process token related, local imports and exports
1503void CConnectedChains::ProcessLocalImports()
c3250dcd 1504{
0ab273d2 1505 // first determine all export threads on the current chain that are valid to import
1506 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1507 std::multimap<uint160, CTransaction> importThreads;
1508 uint160 thisChainID = thisChain.GetID();
1509
1510 LOCK2(cs_main, mempool.cs);
1511 uint32_t nHeight = chainActive.Height();
1512
1513 if (GetUnspentChainExports(thisChainID, exportOutputs) && exportOutputs.size())
1514 {
1515 for (auto &exportThread : exportOutputs)
1516 {
1517 CCurrencyDefinition exportDef;
1518 int32_t defHeight;
1519 if (!GetCurrencyDefinition(exportThread.first, exportDef, &defHeight))
1520 {
1521 printf("%s: definition for export currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(exportThread.first)).c_str());
1522 LogPrintf("%s: definition for export currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(exportThread.first)).c_str());
1523 continue;
1524 }
1525 currencyDefCache[exportThread.first] = exportDef;
1526
1527 // if chain hasn't started, it isn't controlled by us, imports are by chain ID only, or no exports, skip
1528 if (exportDef.startBlock > nHeight ||
1529 exportDef.systemID != thisChainID ||
1530 exportDef.proofProtocol != CCurrencyDefinition::PROOF_PBAASMMR ||
1531 defHeight == exportThread.second.first)
1532 {
1533 continue;
1534 }
1535
1536 // get the first import for each of the remaining export threads
1537 CTransaction lastImportTx;
1538
1539 // we need to find the last unspent import transaction
1540 std::vector<CAddressUnspentDbEntry> unspentOutputs;
1541
1542 bool found = false;
1543
1544 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(exportThread.first, EVAL_CROSSCHAIN_IMPORT)), 1, unspentOutputs))
1545 {
1546 // if one spends the prior one, get the one that is not spent
1547 for (auto txidx : unspentOutputs)
1548 {
1549 uint256 blkHash;
1550 CTransaction itx;
1551 CCrossChainImport oneCCI;
1552 if (myGetTransaction(txidx.first.txhash, lastImportTx, blkHash) &&
1553 (oneCCI = CCrossChainImport(lastImportTx)).IsValid() &&
1554 oneCCI.systemID == exportDef.GetID())
1555 {
1556 importThreads.insert(std::make_pair(exportThread.first, lastImportTx));
1557 break;
1558 }
1559 }
1560 }
1561 }
1562 }
1563
1564 CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
1565 for (auto &oneIT : importThreads)
1566 {
1567 std::vector<CTransaction> importTxes;
1568 int32_t importOutNum;
1569 CCrossChainImport oneImportInput(oneIT.second, &importOutNum);
1570 if (oneImportInput.IsValid())
1571 {
1572 std::vector<CAddressUnspentDbEntry> reserveDeposits;
1573 GetAddressUnspent(currencyDefCache[oneIT.first].GetConditionID(EVAL_RESERVE_DEPOSIT), CScript::P2CC, reserveDeposits);
1574 CCurrencyValueMap tokenImportAvailable;
1575 CAmount nativeImportAvailable = 0;
1576 for (auto &oneOut : reserveDeposits)
1577 {
1578 nativeImportAvailable += oneOut.second.satoshis;
1579 tokenImportAvailable += oneOut.second.script.ReserveOutValue();
1580 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
1581 }
1582 nativeImportAvailable += oneIT.second.vout[importOutNum].nValue;
1583 tokenImportAvailable += oneIT.second.vout[importOutNum].ReserveOutValue();
1584 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
1585 if (CreateLatestImports(currencyDefCache[oneIT.first], oneIT.second, txTemplate, CTransaction(), tokenImportAvailable, nativeImportAvailable, importTxes))
1586 {
1587 // fund the first import transaction with all reserveDeposits
1588 // change amounts are passed through on the import thread
1589 //
1590 // TODO: manage when this becomes to large by splitting reserve deposits over the
1591 // transactions
1592 if (importTxes.size())
1593 {
1594 CMutableTransaction firstImport = importTxes[0];
1595 int32_t outNum;
1596 CCrossChainImport cci(importTxes[0], &outNum);
1597 if (cci.IsValid())
1598 {
56a7b665 1599 // add the reserve deposit inputs to the first transaction
1600 // the outputs should have been automatically propagated through
0ab273d2 1601 for (auto &oneOut : reserveDeposits)
1602 {
1603 firstImport.vin.push_back(CTxIn(oneOut.first.txhash, oneOut.first.index));
1604 }
1605 importTxes[0] = firstImport;
1606 SignAndCommitImportTransactions(oneIT.second, importTxes);
1607 }
1608 }
1609 }
1610 }
1611 }
c3250dcd
MT
1612}
1613
b2a98c42
MT
1614void CConnectedChains::SubmissionThread()
1615{
1616 try
1617 {
1618 arith_uint256 lastHash;
687e93d5
MT
1619 int64_t lastImportTime = 0;
1620 uint32_t lastHeight = 0;
b2a98c42 1621
a82942e4 1622 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
1623 while (true)
1624 {
c3250dcd
MT
1625 boost::this_thread::interruption_point();
1626
9f0c14b2 1627 if (IsVerusActive())
b2a98c42 1628 {
23d61f0a 1629 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 1630 //printf("SubmissionThread: pruning\n");
c3250dcd 1631 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 1632 bool submit = false;
b2a98c42 1633 {
2fd1f0fb 1634 LOCK(cs_mergemining);
bea3e6a2
MT
1635 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1636 {
326f5f88 1637 qualifiedHeaders.clear();
bea3e6a2 1638 }
2fd1f0fb 1639 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 1640
9014248c 1641 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 1642 }
1643 if (submit)
1644 {
9014248c 1645 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 1646 SubmitQualifiedBlocks();
b2a98c42 1647 }
9f0c14b2
MT
1648 else
1649 {
0ab273d2 1650 ProcessLocalImports();
2fd1f0fb 1651 sem_submitthread.wait();
9f0c14b2 1652 }
b2a98c42
MT
1653 }
1654 else
1655 {
9f0c14b2 1656 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
5a72525a 1657 if (CheckVerusPBaaSAvailable())
2830db29 1658 {
5a72525a 1659 // check to see if we have recently earned a block with an earned notarization that qualifies for
1660 // submitting an accepted notarization
1661 if (earnedNotarizationHeight)
572c763f 1662 {
5a72525a 1663 CBlock blk;
1664 int32_t txIndex = -1, height;
572c763f 1665 {
5a72525a 1666 LOCK(cs_mergemining);
1667 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1668 {
1669 blk = earnedNotarizationBlock;
1670 earnedNotarizationBlock = CBlock();
1671 txIndex = earnedNotarizationIndex;
1672 height = earnedNotarizationHeight;
1673 earnedNotarizationHeight = 0;
1674 }
572c763f 1675 }
572c763f 1676
5a72525a 1677 if (txIndex != -1)
d6bc5de8 1678 {
5a72525a 1679 //printf("SubmissionThread: testing notarization\n");
1680 CTransaction lastConfirmed;
1681 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1682
1683 if (!txId.IsNull())
1684 {
1685 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1686 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1687 }
d6bc5de8 1688 }
572c763f 1689 }
687e93d5 1690
5a72525a 1691 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1692 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1693 {
1694 lastImportTime = GetAdjustedTime();
1695 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
687e93d5 1696
5a72525a 1697 // see if our notary has a confirmed notarization for us
1698 UniValue params(UniValue::VARR);
1699 UniValue result;
687e93d5 1700
5a72525a 1701 params.push_back(thisChain.name);
687e93d5 1702
5a72525a 1703 try
1704 {
1705 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1706 } catch (exception e)
1707 {
1708 result = NullUniValue;
1709 }
687e93d5 1710
5a72525a 1711 if (!result.isNull())
1712 {
1713 auto txUniStr = find_value(result, "lastimporttransaction");
1714 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1715 auto txTemplateStr = find_value(result, "importtxtemplate");
56fe75cb 1716 CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
1717 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
687e93d5 1718
5a72525a 1719 CTransaction lastImportTx, lastConfirmedTx, templateTx;
687e93d5 1720
5a72525a 1721 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1722 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1723 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1724 DecodeHexTx(templateTx, txTemplateStr.get_str()))
687e93d5 1725 {
5a72525a 1726 std::vector<CTransaction> importTxes;
56fe75cb 1727 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
687e93d5 1728 {
5a72525a 1729 for (auto importTx : importTxes)
687e93d5 1730 {
5a72525a 1731 UniValue txResult;
70506d95 1732 params.setArray();
5a72525a 1733 params.push_back(EncodeHexTx(importTx));
1734
1735 try
687e93d5 1736 {
5a72525a 1737 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
fa6b4d41 1738 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
5a72525a 1739 {
fa6b4d41 1740 params.setArray();
5a72525a 1741 params.push_back(txResult);
1742 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1743 }
1744 else
1745 {
1746 txResult = NullUniValue;
1747 }
1748
1749 } catch (exception e)
687e93d5
MT
1750 {
1751 txResult = NullUniValue;
1752 }
5a72525a 1753 uint256 testId;
1754 if (txResult.isStr())
1755 {
1756 testId.SetHex(txResult.get_str());
1757 }
1758 if (testId.IsNull())
1759 {
1760 break;
1761 }
687e93d5
MT
1762 }
1763 }
1764 }
1765 }
1766 }
1767 }
1768 sleep(3);
b2a98c42 1769 }
b2a98c42
MT
1770 boost::this_thread::interruption_point();
1771 }
1772 }
1773 catch (const boost::thread_interrupted&)
1774 {
1775 LogPrintf("Verus merge mining thread terminated\n");
1776 }
1777}
9f0c14b2 1778
b2a98c42
MT
1779void CConnectedChains::SubmissionThreadStub()
1780{
1781 ConnectedChains.SubmissionThread();
1782}
1783
572c763f
MT
1784void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1785{
1786 // called after winning a block that contains an earned notarization
1787 // the earned notarization and its height are queued for processing by the submission thread
1788 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1789 // kept
1790 LOCK(cs_mergemining);
1791
94a210aa 1792 // we only care about the last
572c763f 1793 earnedNotarizationHeight = height;
94a210aa
MT
1794 earnedNotarizationBlock = blk;
1795 earnedNotarizationIndex = txIndex;
572c763f
MT
1796}
1797
68b309c0
MT
1798bool IsChainDefinitionInput(const CScript &scriptSig)
1799{
1800 uint32_t ecode;
56fe75cb 1801 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
68b309c0 1802}
13ed2980 1803
This page took 0.422486 seconds and 4 git commands to generate.