]> Git Repo - VerusCoin.git/blame - src/pbaas/pbaas.cpp
VRSC and VRSCTEST import/export thread at PBaaS activation
[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
56fe75cb 411CCurrencyDefinition::CCurrencyDefinition(const CTransaction &tx)
41f170fd 412{
56fe75cb 413 bool definitionFound = false;
414 nVersion = PBAAS_VERSION_INVALID;
41f170fd
MT
415 for (auto out : tx.vout)
416 {
417 COptCCParams p;
418 if (IsPayToCryptoCondition(out.scriptPubKey, p))
419 {
56fe75cb 420 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
41f170fd 421 {
56fe75cb 422 if (definitionFound)
423 {
424 nVersion = PBAAS_VERSION_INVALID;
425 }
426 else
427 {
428 FromVector(p.vData[0], *this);
429 definitionFound = true;
430 }
41f170fd
MT
431 }
432 }
433 }
434}
435
56fe75cb 436uint160 CCurrencyDefinition::GetID(const std::string &Name, uint160 &Parent)
b2a98c42 437{
56fe75cb 438 return CIdentity::GetID(Name, Parent);
b2a98c42
MT
439}
440
56fe75cb 441uint160 CCurrencyDefinition::GetConditionID(int32_t condition) const
b2a98c42 442{
57055854 443 return CCrossChainRPCData::GetConditionID(name, condition);
b2a98c42
MT
444}
445
f8f61a6d 446#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
344a051d 447extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
70c48c13 448extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
344a051d 449extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
f8f61a6d 450extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
f2d873d0 451extern std::string VERUS_CHAINNAME;
f8f61a6d 452
b2a98c42
MT
453// ensures that the chain definition is valid and that there are no other definitions of the same name
454// that have been confirmed.
4ecaf167 455bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42
MT
456{
457 // the chain definition output can be spent when the chain is at the end of its life and only then
458 // TODO
459 return false;
460}
461
462// ensures that the chain definition is valid and that there are no other definitions of the same name
463// that have been confirmed.
464bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
465{
466 // checked before a chain definition output script is accepted as a valid transaction
467
468 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
469 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
470 // 2) notarization output with conformant values
471 // 3) finalization output
472 // 3) notarization funding
473 //
474
475 // get the source transaction
476 uint256 blkHash;
477 CTransaction thisTx;
6c5d1151 478 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
b2a98c42
MT
479 {
480 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
481 return false;
482 }
483
56fe75cb 484 CCurrencyDefinition chainDef(thisTx);
485 CPBaaSNotarization notarization(thisTx);
486 CNotarizationFinalization finalization(thisTx);
b2a98c42
MT
487
488 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
489 {
490 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
491 return false;
492 }
493
56fe75cb 494 CCurrencyDefinition prior;
b2a98c42 495 // this ensures that there is no other definition of the same name already on the blockchain
56fe75cb 496 if (!GetCurrencyDefinition(chainDef.name, prior))
b2a98c42
MT
497 {
498 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
499 return false;
500 }
501
502 return true;
503}
504
56fe75cb 505CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
989b1de1 506{
56fe75cb 507 CCurrencyValueMap retVal;
508
989b1de1
MT
509 if (numInputs > MAX_EXPORT_INPUTS)
510 {
56fe75cb 511 return retVal;
989b1de1
MT
512 }
513 static const arith_uint256 satoshis(100000000);
514
56fe75cb 515 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1)));
516
517 for (auto &feePair : totalFees.valueMap)
518 {
519 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
520 }
521 return retVal;
522}
523
524CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
525{
526 CCurrencyValueMap retVal;
989b1de1 527
56fe75cb 528 for (auto &feePair : CalculateExportFee().valueMap)
529 {
530 CAmount feeAmount = feePair.second;
531 auto it = totalFees.valueMap.find(feePair.first);
532 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
533 }
534 return retVal;
989b1de1
MT
535}
536
b2a98c42
MT
537bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
538{
2fd1f0fb 539 bool retval = false;
b2a98c42 540 LOCK(cs_mergemining);
23d61f0a 541
3016973b 542 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 543
b2a98c42
MT
544 auto chainIt = mergeMinedChains.find(chainID);
545 if (chainIt != mergeMinedChains.end())
546 {
547 arith_uint256 target;
548 target.SetCompact(chainIt->second.block.nBits);
549 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
550 {
551 // make sure we don't just match by target
56fe75cb 552 if (removeRange.first->second->GetID() == chainID)
b2a98c42
MT
553 {
554 mergeMinedTargets.erase(removeRange.first);
555 break;
556 }
557 }
558 mergeMinedChains.erase(chainID);
2fd1f0fb 559 dirty = retval = true;
b2a98c42
MT
560
561 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 562 //if (!mergeMinedChains.size())
563 //{
564 // sem_submitthread.post();
565 //}
b2a98c42 566 }
2fd1f0fb 567 return retval;
b2a98c42
MT
568}
569
570// remove merge mined chains added and not updated since a specific time
9ec65f90 571void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
b2a98c42
MT
572{
573 vector<uint160> toRemove;
574
575 LOCK(cs_mergemining);
576 for (auto blkData : mergeMinedChains)
577 {
578 if (blkData.second.block.nTime < pruneBefore)
579 {
580 toRemove.push_back(blkData.first);
581 }
582 }
583
584 for (auto id : toRemove)
585 {
3016973b 586 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
587 RemoveMergedBlock(id);
588 }
589}
590
591// adds or updates merge mined blocks
592// returns false if failed to add
593bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
594{
b2a98c42
MT
595 // determine if we should replace one or add to the merge mine vector
596 {
597 LOCK(cs_mergemining);
598
2fd1f0fb 599 arith_uint256 target;
56fe75cb 600 uint160 cID = blkData.GetID();
b2a98c42
MT
601 auto it = mergeMinedChains.find(cID);
602 if (it != mergeMinedChains.end())
603 {
1fa4454d 604 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 605 }
1fa4454d 606 target.SetCompact(blkData.block.nBits);
23d61f0a 607
3016973b 608 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 609
1fa4454d 610 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 611 dirty = true;
b2a98c42 612 }
bce52b27 613 return true;
b2a98c42
MT
614}
615
616bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
617{
618 {
619 LOCK(cs_mergemining);
620 auto chainIt = mergeMinedChains.find(chainID);
621 if (chainIt != mergeMinedChains.end())
622 {
623 rpcChainData = (CRPCChainData)chainIt->second;
624 return true;
625 }
626 return false;
627 }
628}
629
630// this returns a pointer to the data without copy and assumes the lock is held
631CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
632{
633 {
634 auto chainIt = mergeMinedChains.find(chainID);
635 if (chainIt != mergeMinedChains.end())
636 {
637 return &chainIt->second;
638 }
639 return NULL;
640 }
641}
642
9ec65f90 643void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
e771a884 644{
3016973b 645 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 646 {
647 LOCK(cs_mergemining);
cff3c5ad 648
2830db29 649 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
650 }
e771a884 651 sem_submitthread.post();
652}
653
f8f61a6d 654// get the latest block header and submit one block at a time, returning after there are no more
655// matching blocks to be found
656vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
657{
658 std::set<uint160> inHeader;
f8f61a6d 659 bool submissionFound;
660 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
661 vector<pair<string, UniValue>> results;
662
f8f61a6d 663 CBlockHeader bh;
664 arith_uint256 lastHash;
b2a98c42
MT
665 CPBaaSBlockHeader pbh;
666
02560af7 667 do
b2a98c42 668 {
02560af7 669 submissionFound = false;
b2a98c42 670 {
02560af7 671 LOCK(cs_mergemining);
672 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
673 // common, merge mined headers for notarization, drop out on any submission
674 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 675 {
02560af7 676 // add the PBaaS chain ids from this header to a set for search
677 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 678 {
02560af7 679 inHeader.insert(pbh.chainID);
680 }
b2a98c42 681
33d6f38a 682 uint160 chainID;
02560af7 683 // now look through all targets that are equal to or above the hash of this header
684 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
685 {
56fe75cb 686 chainID = chainIt->second->GetID();
02560af7 687 if (inHeader.count(chainID))
2830db29 688 {
02560af7 689 // first, check that the winning header matches the block that is there
690 CPBaaSPreHeader preHeader(chainIt->second->block);
691 preHeader.SetBlockData(headerIt->second);
692
693 // check if the block header matches the block's specific data, only then can we create a submission from this block
694 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 695 {
02560af7 696 // save block as is, remove the block from merged headers, replace header, and submit
697 chainData = *chainIt->second;
698
699 *(CBlockHeader *)&chainData.block = headerIt->second;
700
02560af7 701 submissionFound = true;
f8f61a6d 702 }
3016973b
MT
703 //else // not an error condition. code is here for debugging
704 //{
705 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
706 //}
cff3c5ad 707 }
3016973b
MT
708 //else // not an error condition. code is here for debugging
709 //{
710 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
711 //}
02560af7 712 }
2830db29 713
02560af7 714 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
715 if (submissionFound)
716 {
717 // once it is going to be submitted, remove block from this chain until a new one is added again
718 RemoveMergedBlock(chainID);
719 break;
720 }
721 else
02560af7 722 {
723 qualifiedHeaders.erase(headerIt);
b2a98c42 724 }
f8f61a6d 725 }
02560af7 726 }
727 if (submissionFound)
728 {
729 // submit one block and loop again. this approach allows multiple threads
730 // to collectively empty the submission queue, mitigating the impact of
731 // any one stalled daemon
732 UniValue submitParams(UniValue::VARR);
733 submitParams.push_back(EncodeHexBlk(chainData.block));
734 UniValue result, error;
735 try
f8f61a6d 736 {
02560af7 737 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
738 result = find_value(result, "result");
739 error = find_value(result, "error");
b2a98c42 740 }
02560af7 741 catch (exception e)
742 {
743 result = UniValue(e.what());
744 }
745 results.push_back(make_pair(chainData.chainDefinition.name, result));
746 if (result.isStr() || !error.isNull())
747 {
748 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());
749 }
750 else
751 {
752 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
753 }
754 }
755 } while (submissionFound);
b2a98c42
MT
756 return results;
757}
758
f8f61a6d 759// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
760uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
761{
762 vector<uint160> inHeader;
763 vector<UniValue> toCombine;
764 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 765 arith_uint256 target(0);
b2a98c42
MT
766
767 CPBaaSBlockHeader pbh;
768
b2a98c42 769 {
f8f61a6d 770 LOCK(cs_mergemining);
b2a98c42 771
f8f61a6d 772 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
773
774 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 775 {
f8f61a6d 776 if (bh.GetPBaaSHeader(pbh, i))
777 {
778 inHeader.push_back(pbh.chainID);
779 }
780 }
781
782 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 783 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 784 for (uint32_t i = 0; i < inHeader.size(); i++)
785 {
786 auto it = mergeMinedChains.find(inHeader[i]);
787 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
788 {
789 bh.DeletePBaaSHeader(i);
790 }
b2a98c42 791 }
b2a98c42 792
b2a98c42
MT
793 for (auto chain : mergeMinedChains)
794 {
795 // get the native PBaaS header for each chain and put it into the
796 // header we are given
326f5f88 797 // it must have itself in as a PBaaS header
56fe75cb 798 uint160 cid = chain.second.GetID();
3a2a1777 799 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
800 {
801 if (!bh.AddUpdatePBaaSHeader(pbh))
802 {
803 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 804 break;
805 }
806 else
807 {
808 arith_uint256 t;
809 t.SetCompact(chain.second.block.nBits);
810 if (t > target)
811 {
812 target = t;
813 }
b2a98c42
MT
814 }
815 }
326f5f88
MT
816 else
817 {
818 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
819 }
820
b2a98c42 821 }
2830db29 822 dirty = false;
b2a98c42 823 }
326f5f88 824
f8f61a6d 825 return target.GetCompact();
b2a98c42
MT
826}
827
ccac80a3 828bool CConnectedChains::IsVerusPBaaSAvailable()
829{
56fe75cb 830 return notaryChainVersion >= "0.6.4";
ccac80a3 831}
832
f8f61a6d 833extern string PBAAS_HOST, PBAAS_USERPASS;
834extern int32_t PBAAS_PORT;
6bb9fdbc 835bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 836{
6bb9fdbc 837 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 838 {
6bb9fdbc 839 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 840 if (uniVer.isStr())
841 {
2156d3d0 842 LOCK(cs_mergemining);
f8f61a6d 843 notaryChainVersion = uni_get_str(uniVer);
6bb9fdbc 844 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
56fe75cb 845 CCurrencyDefinition chainDef(chainDefUni);
f8f61a6d 846 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
847 }
9f0c14b2 848 }
ccac80a3 849 return IsVerusPBaaSAvailable();
9f0c14b2
MT
850}
851
56fe75cb 852uint32_t CConnectedChains::NotaryChainHeight()
853{
854 LOCK(cs_mergemining);
855 return notaryChainHeight;
856}
857
2299bd95 858bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2
MT
859{
860 if (IsVerusActive())
861 {
ccac80a3 862 notaryChainVersion = "";
9f0c14b2
MT
863 }
864 else
865 {
866 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 867 // tolerate only 15 second timeout
9a891caf 868 UniValue chainInfo, chainDef;
869 try
870 {
871 UniValue params(UniValue::VARR);
2fd1f0fb 872 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 873 if (!chainInfo.isNull())
874 {
875 params.push_back(VERUS_CHAINNAME);
56fe75cb 876 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
9a891caf 877
878 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
879 {
56fe75cb 880 // if we have not past block 1 yet, store the best known update of our current state
881 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
882 {
883 bool success = false;
884 params.clear();
885 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
886 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
887 if (!chainDef.isNull())
888 {
889 CCurrencyDefinition currencyDef(chainDef);
890 if (currencyDef.IsValid())
891 {
892 thisChain = currencyDef;
893 if (NotaryChainHeight() >= thisChain.startBlock)
894 {
895 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
896 }
897 success = true;
898 }
899 }
900 return success;
901 }
9a891caf 902 return true;
903 }
904 }
905 } catch (exception e)
4fa3b13d 906 {
56fe75cb 907 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
4fa3b13d 908 }
9f0c14b2 909 }
9a891caf 910 notaryChainVersion = "";
4fa3b13d 911 return false;
9f0c14b2
MT
912}
913
56fe75cb 914int CConnectedChains::GetThisChainPort() const
915{
916 int port;
917 string host;
918 for (auto node : defaultPeerNodes)
919 {
920 SplitHostPort(node.networkAddress, port, host);
921 if (port)
922 {
923 return port;
924 }
925 }
926 return 0;
927}
928
41f170fd
MT
929CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
930{
931 CCoinbaseCurrencyState currencyState;
932 CBlock block;
933 LOCK(cs_main);
767f8e36 934 bool isVerusActive = IsVerusActive();
935 if (!isVerusActive &&
f37d48ca 936 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
41f170fd
MT
937 height != 0 &&
938 height <= chainActive.Height() &&
939 chainActive[height] &&
88d014d0 940 ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1b1a31e7 941 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
41f170fd 942 {
1b1a31e7 943 return currencyState;
41f170fd 944 }
7d8fcc31 945 else
946 {
56fe75cb 947 return GetInitialCurrencyState(thisChain);
7d8fcc31 948 }
41f170fd
MT
949}
950
a4f9bc97 951bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
c3250dcd
MT
952{
953 LOCK(cs_mergemining);
b7c685b8 954
a4f9bc97 955 if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
956 {
957 return false;
c3250dcd
MT
958 }
959 latestMiningOutputs = minerOutputs;
960 latestDestination = firstDestinationOut;
bb6c3482 961 return true;
c3250dcd
MT
962}
963
964void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
965{
966 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
967 {
968 if (!nHeight)
969 {
970 return;
971 }
972
56fe75cb 973 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
974 std::multimap<std::pair<uint160, uint160>, std::pair<CInputDescriptor, CReserveTransfer>> sortedTransferOutputs;
975 std::map<uint160, CCurrencyDefinition> currencyDefCache; // keep cache as we look up definitions
c3250dcd 976
9ace9b55 977 LOCK(cs_main);
978
56fe75cb 979 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
980
c3250dcd
MT
981 // get all available transfer outputs to aggregate into export transactions
982 if (GetUnspentChainTransfers(transferOutputs))
983 {
bb6c3482 984 if (!transferOutputs.size())
985 {
986 return;
987 }
56fe75cb 988 for (auto &output : transferOutputs)
989 {
990 CCurrencyDefinition destDef, systemDef;
991 auto it = currencyDefCache.find(output.second.second.currencyID);
992 if ((it != currencyDefCache.end() && !(destDef = it->second).IsValid()) ||
993 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.currencyID, destDef)))
994 {
995 printf("%s: chain definition for export currency ID %s not found\n\n", __func__, output.second.second.currencyID.GetHex().c_str());
996 LogPrintf("%s: chain definition for export currency ID %s not found\n\n", __func__, output.second.second.currencyID.GetHex().c_str());
997 continue;
998 }
999 if (it == currencyDefCache.end())
1000 {
1001 currencyDefCache[output.second.second.currencyID] = destDef;
1002 }
1003
1004 // if this is a currency without its own chain that starts at a height greater than this one,
1005 // skip this tranfer for now. all transfers posted should be able to be imported when the system starts.
a041b57e 1006 if (destDef.systemID == ASSETCHAINS_CHAINID)
56fe75cb 1007 {
a041b57e 1008 if (destDef.startBlock > nHeight)
1009 {
1010 continue;
1011 }
1012
1013 // if the chain has failed to launch, we are done
1014 CCurrencyValueMap minPreMap, preConvertedMap;
1015 if (destDef.minPreconvert.size() &&
1016 (minPreMap = CCurrencyValueMap(destDef.currencies, destDef.minPreconvert)) > preConvertedMap &&
1017 (preConvertedMap = CCurrencyValueMap(destDef.currencies, GetInitialCurrencyState(destDef).reserveIn)) < minPreMap)
1018 {
1019 // this chain should get refunded, nothing more should get aggregated
1020 continue;
1021 }
56fe75cb 1022 }
1023
1024 it = currencyDefCache.find(output.second.second.systemID);
1025 if ((it != currencyDefCache.end() && !(systemDef = it->second).IsValid()) ||
1026 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.systemID, systemDef)))
1027 {
1028 printf("%s: chain definition for export system ID %s not found\n\n", __func__, output.second.second.currencyID.GetHex().c_str());
1029 LogPrintf("%s: chain definition for export system ID %s not found\n\n", __func__, output.second.second.currencyID.GetHex().c_str());
1030 continue;
1031 }
1032 if (it == currencyDefCache.end())
1033 {
1034 currencyDefCache[output.second.second.systemID] = systemDef;
1035 }
1036
1037 // if tokens on this chain, group like currencies, otherwise don't
1038 if (systemDef.GetID() == thisChainID)
1039 {
1040 sortedTransferOutputs.insert({{output.first, output.second.second.currencyID}, output.second});
1041 }
1042 else
1043 {
1044 sortedTransferOutputs.insert({{output.first, uint160()}, output.second});
1045 }
1046 }
1047
c3250dcd
MT
1048 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1049 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1050
56fe75cb 1051 // this pair will sort to the end
1052 std::pair<uint160, uint160> bookEnd({uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff")), uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff"))});
1053 std::pair<uint160, uint160> lastChain = bookEnd;
41ad1f62 1054
78a36746 1055 // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
56fe75cb 1056 sortedTransferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
c3250dcd 1057
56fe75cb 1058 CCurrencyDefinition lastChainDef;
9ace9b55 1059
78a36746 1060 // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1061 // export of that type, or there are MIN_INPUTS or more outputs to aggregate
56fe75cb 1062 for (auto it = sortedTransferOutputs.begin(); it != sortedTransferOutputs.end(); it++)
78a36746 1063 {
1064 // get chain target and see if it is the same
56fe75cb 1065 if (lastChain.first == bookEnd.first || it->first.first == lastChain.first)
c3250dcd 1066 {
78a36746 1067 txInputs.push_back(it->second);
78a36746 1068 }
1069 else
1070 {
1071 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
56fe75cb 1072 // we need an unspent export output to export, or use the last one of it is an export to the same
1073 // system
1074 if (GetUnspentChainExports(lastChain.first, exportOutputs) && exportOutputs.size())
c3250dcd 1075 {
78a36746 1076 auto lastExport = *exportOutputs.begin();
c3250dcd 1077
78a36746 1078 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
c3250dcd
MT
1079 {
1080 boost::optional<CTransaction> oneExport;
1081
1082 // make one or more transactions that spends the last export and all possible cross chain transfers
1083 while (txInputs.size())
1084 {
1085 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1086
56fe75cb 1087 // loop through and separate out individual currencies on this chain into their own exports
1088 int numInputs = 0;
1089 int inputsLeft = txInputs.size();
c3250dcd 1090
56fe75cb 1091 if (it->first.second.IsNull())
1092 {
1093 numInputs = txInputs.size();
1094 }
1095 else
1096 {
1097 for (int i = 0; i < txInputs.size(); i++)
1098 {
1099 if (txInputs[i].second.currencyID == it->first.second)
1100 {
1101 numInputs++;
1102 }
1103 else
1104 {
1105 break;
1106 }
1107 }
c3250dcd 1108
56fe75cb 1109 }
1110 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1111 {
1112 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1113 }
1114 inputsLeft = txInputs.size() - numInputs;
c3250dcd
MT
1115 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1116 {
1117 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1118 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1119 assert(numInputs > 0);
1120 }
1121
1122 // each time through, we make one export transaction with the remainder or a subset of the
1123 // reserve transfer inputs. inputs can be:
1124 // 1. transfers of reserve for fractional reserve chains
1125 // 2. pre-conversions for pre-launch participation in the premine
1126 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1127 //
1128 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1129 // or reserve token inputs.
1130 //
1131 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1132 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1133 // as part of the import process
1134 //
1135 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1136 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
5cd8b21a 1137 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1138 // Verus chain.
c3250dcd 1139 //
56fe75cb 1140 CCurrencyValueMap totalTxFees;
1141 CCurrencyValueMap totalAmounts;
fe9545ee 1142 CAmount exportOutVal = 0;
c3250dcd
MT
1143 std::vector<CBaseChainObject *> chainObjects;
1144
1145 // first, we must add the export output from the current export thread to this chain
56fe75cb 1146 if (oneExport.is_initialized())
c3250dcd
MT
1147 {
1148 // spend the last export transaction output
1149 CTransaction &tx = oneExport.get();
1150 COptCCParams p;
1151 int j;
1152 for (j = 0; j < tx.vout.size(); j++)
1153 {
1154 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1155 {
1156 break;
1157 }
1158 }
1159
1160 // had to be found and valid if we made the tx
1161 assert(j < tx.vout.size() && p.IsValid());
1162
1163 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
fe9545ee 1164 exportOutVal = tx.vout[j].nValue;
c3250dcd
MT
1165 }
1166 else
1167 {
1168 // spend the recentExportIt output
78a36746 1169 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
fe9545ee 1170 exportOutVal = lastExport.second.second.nValue;
c3250dcd
MT
1171 }
1172
715182a4 1173 COptCCParams p;
56fe75cb 1174 std::vector<int> toRemove;
c3250dcd
MT
1175 for (int j = 0; j < numInputs; j++)
1176 {
1177 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
56fe75cb 1178 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1179 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1180
1181 // if the source currency is a reserve or fractional currecy of the destination native currency,
1182 // use the source currency for the fee, otherwise, the fee must be paid in destination, native
1183 // currency
1184 //
1185 // if the native currency of this chain is a reserve of the destination currency
1186 // use the source currency as, if native or non-reserve, it will require native
1187 uint160 feeCurrencyID = txInputs[j].second.systemID;
1188 std::map<uint160, int32_t> currencyMap = ConnectedChains.ThisChain().GetCurrenciesMap();
1189 if ((ConnectedChains.ThisChain().IsReserve() &&
1190 currencyMap.count(txInputs[j].second.systemID)) ||
1191 (currencyDefCache[txInputs[j].second.systemID].IsReserve() &&
1192 currencyDefCache[txInputs[j].second.systemID].GetCurrenciesMap().count(txInputs[j].second.currencyID)))
1193 {
1194 // transfer fee paid with source currency if can be converted to destination currency
1195 feeCurrencyID = txInputs[j].second.currencyID;
1196 }
1197
1198 CCurrencyValueMap newTransferOutput;
1199 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue;
1200 newTransferOutput.valueMap[feeCurrencyID] += txInputs[j].second.nFees;
1201
1202 if ((newTransferInput - newTransferOutput).HasNegative())
81fc0174 1203 {
dd1b8052 1204 // 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 1205 // 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
1206 // we should formalize this into a chain contribution or amount adjustment.
56fe75cb 1207 printf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1208 LogPrintf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1209 toRemove.push_back(j);
81fc0174 1210 }
1211 else
1212 {
56fe75cb 1213 totalAmounts += newTransferOutput;
1214 totalTxFees.valueMap[feeCurrencyID] += txInputs[j].second.nFees;
81fc0174 1215 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1216 }
1217 }
56fe75cb 1218 // remove in reverse order so one removal does not affect the position of the next
1219 for (int j = toRemove.size() - 1; j >= 0; j--)
1220 {
1221 txInputs.erase(txInputs.begin() + toRemove[j]);
1222 numInputs--;
1223 }
1224 if (!numInputs)
81fc0174 1225 {
81fc0174 1226 continue;
c3250dcd
MT
1227 }
1228
56fe75cb 1229 CCrossChainExport ccx(lastChain.first, numInputs, totalAmounts, totalTxFees);
1230
1231 // make extra outputs for fees in each currency
1232 for (auto &outPair : ccx.CalculateExportFee().valueMap)
1233 {
1234 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
1235 outPair.first, outPair.second, 0, lastChain.first, DestinationToTransferDestination(feeOutput));
1236 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1237 }
c3250dcd 1238
410af2c6 1239 // do a preliminary check
1240 CReserveTransactionDescriptor rtxd;
56fe75cb 1241 std::vector<CTxOut> vOutputs;
9ace9b55 1242
56fe75cb 1243 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, chainObjects, vOutputs))
410af2c6 1244 {
8bb25a86 1245 DeleteOpRetObjects(chainObjects);
1246
410af2c6 1247 printf("%s: failed to create valid exports\n", __func__);
1248 LogPrintf("%s: failed to create valid exports\n", __func__);
9ace9b55 1249
1250 // debugging output
8bb25a86 1251 printf("%s: failed to export outputs:\n", __func__);
56fe75cb 1252 for (auto oneout : vOutputs)
9ace9b55 1253 {
9ace9b55 1254 UniValue uniOut;
1255 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1256 printf("%s\n", uniOut.write(true, 2).c_str());
1257 }
410af2c6 1258 }
8bb25a86 1259 else
410af2c6 1260 {
56fe75cb 1261 CCcontract_info CC;
1262 CCcontract_info *cp;
1263
1264 // debugging out
8bb25a86 1265 printf("%s: exported outputs:\n", __func__);
56fe75cb 1266 for (auto &oneout : chainObjects)
8bb25a86 1267 {
56fe75cb 1268 if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1269 {
1270 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1271 printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1272 }
8bb25a86 1273 }
c3250dcd 1274
8bb25a86 1275 CScript opRet = StoreOpRetArray(chainObjects);
1276 DeleteOpRetObjects(chainObjects);
c3250dcd 1277
56fe75cb 1278 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1279 for (auto &oneCurrencyOut : ccx.totalAmounts.valueMap)
1280 {
1281 CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1282 // if we control this currency, it must go to reserve deposit of the system it is
1283 // being exported to
1284 if (oneDef.systemID == ASSETCHAINS_CHAINID)
1285 {
1286 CAmount nativeOut = oneDef.GetID() == ASSETCHAINS_CHAINID ? oneCurrencyOut.second : 0;
c3250dcd 1287
56fe75cb 1288 // send the entire amount to a reserve transfer output of the specific chain
1289 // we receive our fee on the other chain or when it comes back
1290 std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(oneDef.GetID(), EVAL_RESERVE_DEPOSIT))});
1291 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
c3250dcd 1292
56fe75cb 1293 CTokenOutput ro(oneCurrencyOut.first, oneCurrencyOut.second);
c3250dcd 1294
56fe75cb 1295 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_DEPOSIT, dests, 1, &ro), &indexDests),
1296 nativeOut);
1297 }
1298 }
c3250dcd 1299
56fe75cb 1300 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
c3250dcd 1301
56fe75cb 1302 // send native amount of zero to a cross chain export output of the specific chain
1303 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain.first, EVAL_CROSSCHAIN_EXPORT))});
1304 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
c3250dcd 1305
56fe75cb 1306 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests),
1307 exportOutVal);
c3250dcd 1308
8bb25a86 1309 tb.AddOpRet(opRet);
1310 tb.SetFee(0);
c3250dcd 1311
88d014d0 1312 TransactionBuilderResult buildResult(tb.Build());
c3250dcd 1313
88d014d0 1314 if (!buildResult.IsError() && buildResult.IsTx())
c3250dcd 1315 {
8bb25a86 1316 // replace the last one only if we have a valid new one
88d014d0 1317 CTransaction tx = buildResult.GetTxOrThrow();
8bb25a86 1318
1319 LOCK2(cs_main, mempool.cs);
1320 static int lastHeight = 0;
1321 // remove conflicts, so that we get in
1322 std::list<CTransaction> removed;
1323 mempool.removeConflicts(tx, removed);
1324
1325 // add to mem pool, prioritize according to the fee we will get, and relay
1326 printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1327 LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1328 if (myAddtomempool(tx))
1329 {
1330 uint256 hash = tx.GetHash();
56fe75cb 1331 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1332 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
8bb25a86 1333 RelayTransaction(tx);
1334 }
1335 }
1336 else
1337 {
1338 // we can't do any more useful work for this chain if we failed here
88d014d0 1339 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1340 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
8bb25a86 1341 break;
c3250dcd
MT
1342 }
1343 }
8bb25a86 1344
c3250dcd
MT
1345 // erase the inputs we've attempted to spend
1346 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1347 }
1348 }
1349 }
1350 }
9ace9b55 1351 lastChain = it->first;
c3250dcd
MT
1352 }
1353 }
1354 }
1355}
1356
1357// send new imports from this chain to the specified chain, which generally will be the notary chain
1358void CConnectedChains::SendNewImports(const uint160 &chainID,
1359 const CPBaaSNotarization &notarization,
1360 const uint256 &lastExportTx,
1361 const CTransaction &lastCrossImport,
1362 const CTransaction &lastExport)
1363{
1364 // currently only support sending imports to
1365}
1366
b2a98c42
MT
1367void CConnectedChains::SubmissionThread()
1368{
1369 try
1370 {
1371 arith_uint256 lastHash;
687e93d5
MT
1372 int64_t lastImportTime = 0;
1373 uint32_t lastHeight = 0;
b2a98c42 1374
a82942e4 1375 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
1376 while (true)
1377 {
c3250dcd
MT
1378 boost::this_thread::interruption_point();
1379
9f0c14b2 1380 if (IsVerusActive())
b2a98c42 1381 {
23d61f0a 1382 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 1383 //printf("SubmissionThread: pruning\n");
c3250dcd 1384 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 1385 bool submit = false;
b2a98c42 1386 {
2fd1f0fb 1387 LOCK(cs_mergemining);
bea3e6a2
MT
1388 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1389 {
326f5f88 1390 qualifiedHeaders.clear();
bea3e6a2 1391 }
2fd1f0fb 1392 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 1393
9014248c 1394 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 1395 }
1396 if (submit)
1397 {
9014248c 1398 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 1399 SubmitQualifiedBlocks();
b2a98c42 1400 }
9f0c14b2
MT
1401 else
1402 {
9014248c 1403 //printf("SubmissionThread: waiting on sem\n");
2fd1f0fb 1404 sem_submitthread.wait();
9f0c14b2 1405 }
b2a98c42
MT
1406 }
1407 else
1408 {
9f0c14b2 1409 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
5a72525a 1410 if (CheckVerusPBaaSAvailable())
2830db29 1411 {
5a72525a 1412 // check to see if we have recently earned a block with an earned notarization that qualifies for
1413 // submitting an accepted notarization
1414 if (earnedNotarizationHeight)
572c763f 1415 {
5a72525a 1416 CBlock blk;
1417 int32_t txIndex = -1, height;
572c763f 1418 {
5a72525a 1419 LOCK(cs_mergemining);
1420 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1421 {
1422 blk = earnedNotarizationBlock;
1423 earnedNotarizationBlock = CBlock();
1424 txIndex = earnedNotarizationIndex;
1425 height = earnedNotarizationHeight;
1426 earnedNotarizationHeight = 0;
1427 }
572c763f 1428 }
572c763f 1429
5a72525a 1430 if (txIndex != -1)
d6bc5de8 1431 {
5a72525a 1432 //printf("SubmissionThread: testing notarization\n");
1433 CTransaction lastConfirmed;
1434 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1435
1436 if (!txId.IsNull())
1437 {
1438 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1439 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1440 }
d6bc5de8 1441 }
572c763f 1442 }
687e93d5 1443
5a72525a 1444 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1445 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1446 {
1447 lastImportTime = GetAdjustedTime();
1448 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
687e93d5 1449
5a72525a 1450 // see if our notary has a confirmed notarization for us
1451 UniValue params(UniValue::VARR);
1452 UniValue result;
687e93d5 1453
5a72525a 1454 params.push_back(thisChain.name);
687e93d5 1455
5a72525a 1456 try
1457 {
1458 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1459 } catch (exception e)
1460 {
1461 result = NullUniValue;
1462 }
687e93d5 1463
5a72525a 1464 if (!result.isNull())
1465 {
1466 auto txUniStr = find_value(result, "lastimporttransaction");
1467 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1468 auto txTemplateStr = find_value(result, "importtxtemplate");
56fe75cb 1469 CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
1470 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
687e93d5 1471
5a72525a 1472 CTransaction lastImportTx, lastConfirmedTx, templateTx;
687e93d5 1473
5a72525a 1474 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1475 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1476 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1477 DecodeHexTx(templateTx, txTemplateStr.get_str()))
687e93d5 1478 {
5a72525a 1479 std::vector<CTransaction> importTxes;
56fe75cb 1480 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
687e93d5 1481 {
5a72525a 1482 for (auto importTx : importTxes)
687e93d5 1483 {
5a72525a 1484 UniValue txResult;
70506d95 1485 params.setArray();
5a72525a 1486 params.push_back(EncodeHexTx(importTx));
1487
1488 try
687e93d5 1489 {
5a72525a 1490 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
fa6b4d41 1491 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
5a72525a 1492 {
fa6b4d41 1493 params.setArray();
5a72525a 1494 params.push_back(txResult);
1495 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1496 }
1497 else
1498 {
1499 txResult = NullUniValue;
1500 }
1501
1502 } catch (exception e)
687e93d5
MT
1503 {
1504 txResult = NullUniValue;
1505 }
5a72525a 1506 uint256 testId;
1507 if (txResult.isStr())
1508 {
1509 testId.SetHex(txResult.get_str());
1510 }
1511 if (testId.IsNull())
1512 {
1513 break;
1514 }
687e93d5
MT
1515 }
1516 }
1517 }
1518 }
1519 }
1520 }
1521 sleep(3);
b2a98c42 1522 }
b2a98c42
MT
1523 boost::this_thread::interruption_point();
1524 }
1525 }
1526 catch (const boost::thread_interrupted&)
1527 {
1528 LogPrintf("Verus merge mining thread terminated\n");
1529 }
1530}
9f0c14b2 1531
b2a98c42
MT
1532void CConnectedChains::SubmissionThreadStub()
1533{
1534 ConnectedChains.SubmissionThread();
1535}
1536
572c763f
MT
1537void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1538{
1539 // called after winning a block that contains an earned notarization
1540 // the earned notarization and its height are queued for processing by the submission thread
1541 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1542 // kept
1543 LOCK(cs_mergemining);
1544
94a210aa 1545 // we only care about the last
572c763f 1546 earnedNotarizationHeight = height;
94a210aa
MT
1547 earnedNotarizationBlock = blk;
1548 earnedNotarizationIndex = txIndex;
572c763f
MT
1549}
1550
68b309c0
MT
1551bool IsChainDefinitionInput(const CScript &scriptSig)
1552{
1553 uint32_t ecode;
56fe75cb 1554 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
68b309c0 1555}
13ed2980 1556
This page took 0.376785 seconds and 4 git commands to generate.