1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin Core developers
3 // Copyright (c) 2019 Michael Toutonghi
4 // Distributed under the MIT software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include "chainparams.h"
9 #include "consensus/consensus.h"
10 #include "consensus/validation.h"
13 #include "crypto/equihash.h"
21 #include "rpc/server.h"
22 #include "txmempool.h"
24 #include "validationinterface.h"
26 #include "wallet/wallet.h"
32 #include <boost/assign/list_of.hpp>
36 #include "rpc/pbaasrpc.h"
37 #include "transaction_builder.h"
41 extern uint32_t ASSETCHAINS_ALGO;
42 extern int32_t ASSETCHAINS_EQUIHASH, ASSETCHAINS_LWMAPOS;
43 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
44 extern uint64_t ASSETCHAINS_STAKED;
45 extern int32_t KOMODO_MININGTHREADS;
46 extern bool VERUS_MINTBLOCKS;
47 extern uint8_t NOTARY_PUBKEY33[33];
48 extern uint160 ASSETCHAINS_CHAINID;
49 extern uint160 VERUS_CHAINID;
50 extern std::string VERUS_CHAINNAME;
51 extern int32_t USE_EXTERNAL_PUBKEY;
52 extern std::string NOTARY_PUBKEY;
54 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
55 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
56 extern int64_t ASSETCHAINS_SUPPLY;
57 extern uint64_t ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
58 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
59 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
60 extern std::string VERUS_CHAINNAME;
63 arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
65 std::set<uint160> ClosedPBaaSChains({});
67 // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
68 static UniValue BIP22ValidationResult(const CValidationState& state)
73 std::string strRejectReason = state.GetRejectReason();
75 throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
76 if (state.IsInvalid())
78 if (strRejectReason.empty())
80 return strRejectReason;
82 // Should be impossible
86 class submitblock_StateCatcher : public CValidationInterface
91 CValidationState state;
93 submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {};
96 virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
97 if (block.GetHash() != hash)
104 bool GetCurrencyDefinition(uint160 chainID, CCurrencyDefinition &chainDef, int32_t *pDefHeight)
108 if (chainID == ConnectedChains.ThisChain().GetID())
110 chainDef = ConnectedChains.ThisChain();
117 else if (!IsVerusActive())
119 if (ConnectedChains.NotaryChain().IsValid() && (chainID == ConnectedChains.NotaryChain().chainDefinition.GetID()))
121 chainDef = ConnectedChains.NotaryChain().chainDefinition;
130 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
131 CCurrencyDefinition foundDef;
133 if (!ClosedPBaaSChains.count(chainID) &&
134 GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_CURRENCY_DEFINITION)), 1, unspentOutputs))
136 for (auto ¤cyDefOut : unspentOutputs)
138 if ((foundDef = CCurrencyDefinition(currencyDefOut.second.script)).IsValid())
143 *pDefHeight = currencyDefOut.second.blockHeight;
149 return foundDef.IsValid();
152 bool GetCurrencyDefinition(string &name, CCurrencyDefinition &chainDef)
154 return GetCurrencyDefinition(CCrossChainRPCData::GetID(name), chainDef);
157 CTxDestination ValidateDestination(const std::string &destStr)
159 CTxDestination destination = DecodeDestination(destStr);
160 CTransferDestination transferDestination;
161 if (destination.which() == COptCCParams::ADDRTYPE_ID)
163 AssertLockHeld(cs_main);
164 if (!CIdentity::LookupIdentity(GetDestinationID(destination)).IsValid())
166 return CTxDestination();
172 // set default peer nodes in the current connected chains
173 bool SetPeerNodes(const UniValue &nodes)
175 if (!nodes.isArray() || nodes.size() == 0)
180 LOCK(ConnectedChains.cs_mergemining);
181 ConnectedChains.defaultPeerNodes.clear();
183 for (int i = 0; i < nodes.size(); i++)
185 CNodeData oneNode(nodes[i]);
186 if (oneNode.networkAddress != "")
188 ConnectedChains.defaultPeerNodes.push_back(oneNode);
191 // set all command line parameters into mapArgs from chain definition
192 vector<string> nodeStrs;
193 for (auto node : ConnectedChains.defaultPeerNodes)
195 nodeStrs.push_back(node.networkAddress);
199 mapMultiArgs["-seednode"] = nodeStrs;
201 if (int port = ConnectedChains.GetThisChainPort())
203 mapArgs["-port"] = to_string(port);
208 // adds the chain definition for this chain and nodes as well
209 // this also sets up the notarization chain, if there is one
210 uint256 CurrencyDefHash()
212 return ::GetHash(ConnectedChains.ThisChain());
215 // adds the chain definition for this chain and nodes as well
216 // this also sets up the notarization chain, if there is one
217 bool SetThisChain(const UniValue &chainDefinition)
219 ConnectedChains.ThisChain() = CCurrencyDefinition(chainDefinition);
220 if (!ConnectedChains.ThisChain().IsValid())
224 SetPeerNodes(find_value(chainDefinition, "nodes"));
226 if (!IsVerusActive())
228 CCurrencyDefinition ¬aryChainDef = ConnectedChains.notaryChain.chainDefinition;
229 // we set the notary chain to either Verus or VerusTest
230 notaryChainDef.nVersion = PBAAS_VERSION;
233 // setup Verus test parameters
234 notaryChainDef.name = "VRSCTEST";
235 notaryChainDef.preAllocation = {std::make_pair(uint160(), 5000000000000000)};
236 notaryChainDef.rewards = std::vector<int64_t>({2400000000});
237 notaryChainDef.rewardsDecay = std::vector<int64_t>({0});
238 Split(GetArg("-ac_halving",""), ASSETCHAINS_HALVING, 0);
239 notaryChainDef.halving = std::vector<int32_t>({(int32_t)(ASSETCHAINS_HALVING[0])});
240 notaryChainDef.eraEnd = std::vector<int32_t>({0});
244 // first setup Verus parameters
245 notaryChainDef.name = "VRSC";
246 notaryChainDef.rewards = std::vector<int64_t>({0,38400000000,2400000000});
247 notaryChainDef.rewardsDecay = std::vector<int64_t>({100000000,0,0});
248 notaryChainDef.halving = std::vector<int32_t>({1,43200,1051920});
249 notaryChainDef.eraEnd = std::vector<int32_t>({10080,226080,0});
251 notaryChainDef.options = (notaryChainDef.OPTION_CANBERESERVE | notaryChainDef.OPTION_ID_REFERRALS);
252 notaryChainDef.idRegistrationAmount = CCurrencyDefinition::DEFAULT_ID_REGISTRATION_AMOUNT;
253 notaryChainDef.idReferralLevels = CCurrencyDefinition::DEFAULT_ID_REFERRAL_LEVELS;
254 notaryChainDef.systemID = notaryChainDef.GetID();
256 ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
257 ASSETCHAINS_TIMEUNLOCKFROM = 0;
258 ASSETCHAINS_TIMEUNLOCKTO = 0;
262 ConnectedChains.ThisChain().options = (CCurrencyDefinition::OPTION_CANBERESERVE | CCurrencyDefinition::OPTION_ID_REFERRALS);
263 ConnectedChains.ThisChain().idRegistrationAmount = CCurrencyDefinition::DEFAULT_ID_REGISTRATION_AMOUNT;
264 ConnectedChains.ThisChain().idReferralLevels = CCurrencyDefinition::DEFAULT_ID_REFERRAL_LEVELS;
265 ConnectedChains.ThisChain().systemID = ConnectedChains.ThisChain().GetID();
268 memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
269 assert(ConnectedChains.ThisChain().name.size() < sizeof(ASSETCHAINS_SYMBOL));
270 strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
272 auto numEras = ConnectedChains.ThisChain().rewards.size();
273 ASSETCHAINS_LASTERA = numEras - 1;
274 mapArgs["-ac_eras"] = to_string(numEras);
276 mapArgs["-ac_end"] = "";
277 mapArgs["-ac_reward"] = "";
278 mapArgs["-ac_halving"] = "";
279 mapArgs["-ac_decay"] = "";
280 mapArgs["-ac_options"] = "";
282 for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
284 if (j > ASSETCHAINS_LASTERA)
286 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
287 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
288 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
289 ASSETCHAINS_ENDSUBSIDY[j] = 0;
290 ASSETCHAINS_ERAOPTIONS[j] = 0;
294 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
295 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
296 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
297 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
298 ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().options;
301 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
302 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
303 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
304 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
305 mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
309 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
310 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
311 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
312 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
313 mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
318 PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
319 mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
320 PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
321 mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
324 CCurrencyState currencyState = ConnectedChains.GetCurrencyState(0);
326 ASSETCHAINS_SUPPLY = currencyState.supply;
327 mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
331 void GetCurrencyDefinitions(vector<CCurrencyDefinition> &chains, bool includeExpired)
336 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
338 if (GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetID(), EVAL_CURRENCY_DEFINITION)), 1, addressIndex))
340 for (auto txidx : addressIndex)
344 if (GetTransaction(txidx.first.txhash, tx, blkHash))
346 std::vector<CCurrencyDefinition> newChains = CCurrencyDefinition::GetCurrencyDefinitions(tx);
347 chains.insert(chains.begin(), newChains.begin(), newChains.end());
349 int downTo = chains.size() - newChains.size();
350 for (int i = chains.size() - 1; i >= downTo; i--)
352 UniValue valStr(UniValue::VSTR);
354 // TODO: remove/comment this if statement, as it is redundant with the one below
355 if (!valStr.read(chains[i].ToUniValue().write()))
357 printf("Invalid characters in blockchain definition: %s\n", chains[i].ToUniValue().write().c_str());
360 // remove after to use less storage
361 if (!valStr.read(chains[i].ToUniValue().write()) || ClosedPBaaSChains.count(chains[i].GetID()) || (!includeExpired && chains[i].endBlock != 0 && chains[i].endBlock < chainActive.Height()))
363 chains.erase(chains.begin() + i);
371 bool CConnectedChains::LoadReserveCurrencies()
373 // we need to get the currency definition for all convertible currencies in addition to Verus
374 // and store them in ConnectedChains. then, add currency definition outputs for each
375 // currency to the coinbase
376 for (auto &curID : thisChain.currencies)
378 // get the total amount pre-converted
379 CCurrencyDefinition oneDef;
380 UniValue params(UniValue::VARR);
381 params.push_back(EncodeDestination(CIdentityID(curID)));
386 result = find_value(RPCCallRoot("getcurrency", params), "result");
387 } catch (exception e)
389 result = NullUniValue;
392 if (!result.isNull())
394 oneDef = CCurrencyDefinition(result);
397 if (result.isNull() || !oneDef.IsValid())
399 // no matter what happens, we should be able to get a valid currency state of some sort, if not, fail
400 LogPrintf("Unable to get currency definition for %s\n", EncodeDestination(CIdentityID(curID)).c_str());
401 printf("Unable to get currency definition for %s\n", EncodeDestination(CIdentityID(curID)).c_str());
406 LOCK(cs_mergemining);
407 reserveCurrencies[curID] = oneDef;
413 bool CConnectedChains::GetLastImport(const uint160 &systemID,
414 CTransaction &lastImport,
415 CPartialTransactionProof &crossChainExport,
416 CCrossChainImport &ccImport,
417 CCrossChainExport &ccCrossExport)
419 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
421 LOCK2(cs_main, mempool.cs);
423 // get last import from the specified chain
424 if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(systemID, EVAL_CROSSCHAIN_IMPORT), 1, unspentOutputs))
429 // make sure it isn't just a burned transaction to that address, drop out on first match
430 const std::pair<CAddressUnspentKey, CAddressUnspentValue> *pOutput = NULL;
432 for (const auto &output : unspentOutputs)
434 if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() &&
435 p.evalCode == EVAL_CROSSCHAIN_IMPORT &&
447 CCurrencyDefinition newCur;
449 if (!myGetTransaction(pOutput->first.txhash, lastImport, hashBlk) ||
450 !(lastImport.vout.size() &&
451 (lastImport.vout.back().scriptPubKey.IsOpReturn() || CCurrencyDefinition::GetCurrencyDefinitions(lastImport).size())))
455 else if (!lastImport.vout.back().scriptPubKey.IsOpReturn())
457 ccCrossExport = CCrossChainExport();
458 ccImport = CCrossChainImport(p.vData[0]);
459 crossChainExport = CPartialTransactionProof();
463 ccImport = CCrossChainImport(p.vData[0]);
464 CPartialTransactionProof *pTxProof;
465 auto opRetArr = RetrieveOpRetArray(lastImport.vout.back().scriptPubKey);
466 if (!opRetArr.size() ||
467 opRetArr[0]->objectType != CHAINOBJ_TRANSACTION_PROOF ||
468 (pTxProof = &(((CChainObject<CPartialTransactionProof> *)(opRetArr[0]))->object))->txProof.proofSequence.size() < 3)
470 DeleteOpRetObjects(opRetArr);
475 crossChainExport = ((CChainObject<CPartialTransactionProof> *)opRetArr[0])->object;
476 DeleteOpRetObjects(opRetArr);
478 if (crossChainExport.GetPartialTransaction(tx).IsNull())
484 ccCrossExport = CCrossChainExport(tx);
491 // returns newly created import transactions to the specified chain/system from exports on this chain specified chain
492 // nHeight is the height for which we have an MMR that the chainID chain considers confirmed for this chain. it will
493 // accept proofs of any transactions with that MMR and height.
494 // Parameters should be validated before this call.
495 bool CConnectedChains::CreateLatestImports(const CCurrencyDefinition ¤cyDef,
496 const CTransaction &lastCrossChainImport,
497 const CTransaction &importTxTemplate,
498 const CTransaction &lastConfirmedNotarization,
499 const CCurrencyValueMap &AvailableTokenInput,
500 CAmount TotalNativeInput,
501 std::vector<CTransaction> &newImports)
503 uint160 systemID = currencyDef.systemID;
504 uint160 thisChainID = thisChain.GetID();
505 uint160 currencyID = currencyDef.GetID();
506 bool isTokenImport = currencyDef.IsToken() && systemID == thisChainID;
507 bool isDefinition = false;
509 CCurrencyValueMap availableTokenInput(AvailableTokenInput);
510 CAmount totalNativeInput(TotalNativeInput);
512 // printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
514 CPBaaSNotarization lastConfirmed(lastConfirmedNotarization);
515 if ((isTokenImport && chainActive.LastTip() == NULL) ||
516 (!isTokenImport && (!lastConfirmed.IsValid() || (chainActive.LastTip() == NULL) || lastConfirmed.notarizationHeight > chainActive.LastTip()->GetHeight())))
518 LogPrintf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
519 printf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
523 CCrossChainImport lastCCI(lastCrossChainImport);
524 if (!lastCCI.IsValid())
526 LogPrintf("%s: Invalid lastCrossChainImport transaction\n", __func__);
527 printf("%s: Invalid lastCrossChainImport transaction\n", __func__);
531 std::vector<CBaseChainObject *> chainObjs;
533 // either a fully valid import with an export or the first import either in block 1 or the chain definition
534 // on the Verus chain
535 std::vector<CCurrencyDefinition> chainDefs;
536 if (!(lastCrossChainImport.vout.back().scriptPubKey.IsOpReturn() &&
537 (chainObjs = RetrieveOpRetArray(lastCrossChainImport.vout.back().scriptPubKey)).size() >= 1 &&
538 chainObjs[0]->objectType == CHAINOBJ_TRANSACTION_PROOF) &&
539 !(chainObjs.size() == 0 &&
540 (chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(lastCrossChainImport)).size()))
542 DeleteOpRetObjects(chainObjs);
543 LogPrintf("%s: Invalid last import tx\n", __func__);
544 printf("%s: Invalid last import tx\n", __func__);
548 bool isVerusActive = IsVerusActive();
550 LOCK2(cs_main, mempool.cs);
552 // confirmation of tokens are simultaneous to blocks
555 lastConfirmed.notarizationHeight = chainActive.Height();
558 uint256 lastExportHash;
559 CTransaction lastExportTx;
561 uint32_t blkHeight = 0;
562 uint32_t nHeight = chainActive.Height() + 1;
565 if (chainObjs.size())
567 CPartialTransactionProof oneExportProof(((CChainObject<CPartialTransactionProof> *)chainObjs[0])->object);
568 if (oneExportProof.txProof.proofSequence.size() == 3)
570 lastExportHash = oneExportProof.TransactionHash();
573 BlockMap::iterator blkMapIt;
574 DeleteOpRetObjects(chainObjs);
575 if (!lastExportHash.IsNull() &&
576 myGetTransaction(lastExportHash, lastExportTx, blkHash) &&
577 (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() &&
579 chainActive.Contains(blkMapIt->second))
582 blkHeight = blkMapIt->second->GetHeight();
587 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
589 // we cannot get export to a chain that has shut down
590 // if the chain definition is spent, a chain is inactive
591 // TODO:PBAAS this check needs to be lifted one level out to any loop that processes multiple
592 // import/exports at a time to prevent complexity
593 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_CURRENCY_DEFINITION)), 1, unspentOutputs) && unspentOutputs.size())
595 CCurrencyDefinition localChainDef;
598 for (auto &txidx : unspentOutputs)
601 uint160 localChainID;
602 if (txidx.second.script.IsPayToCryptoCondition(p) &&
604 p.evalCode == EVAL_CURRENCY_DEFINITION &&
606 (localChainDef = CCurrencyDefinition(p.vData[0])).IsValid() &&
607 (localChainDef.GetID()) == currencyID)
609 if (myGetTransaction(txidx.first.txhash, tx, blkHash))
611 CCrossChainExport ccx(tx);
612 if (ccx.IsValid() && (isVerusActive || tx.IsCoinBase()))
616 lastExportHash = txidx.first.txhash;
617 blkHeight = txidx.second.blockHeight;
628 //LogPrintf("%s: No export thread found\n", __func__);
629 //printf("%s: No export thread found\n", __func__);
633 // which transaction are we in this block?
634 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
636 // get all export transactions including and since this one up to the confirmed height
637 if (blkHeight <= lastConfirmed.notarizationHeight &&
638 GetAddressIndex(CCrossChainRPCData::GetConditionID(isTokenImport ? currencyID : systemID, EVAL_CROSSCHAIN_EXPORT), 1, addressIndex, blkHeight, lastConfirmed.notarizationHeight))
640 // find this export, then check the next one that spends it and use it if also valid
642 uint256 lastHash = lastExportHash;
644 // indexed by input hash
645 std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
647 // validate, order, and relate them with their inputs
648 for (auto &utxo : addressIndex)
650 // if current tx spends lastHash, then we have our next valid transaction to create an import with
651 CTransaction tx, inputtx;
652 uint256 blkHash1, blkHash2;
653 BlockMap::iterator blkIt;
654 CCrossChainExport ccx;
656 if (!utxo.first.spending &&
657 (utxo.first.txhash != lastExportHash) &&
658 myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
659 (ccx = CCrossChainExport(tx)).IsValid() &&
663 myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
664 inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
666 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
668 validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
674 printf("!utxo.first.spending: %s\n", (doNext = !utxo.first.spending) ? "true" : "false");
675 printf("!(utxo.first.txhash == lastExportHash): %s\n", (doNext = !(utxo.first.txhash == lastExportHash)) ? "true" : "false");
676 printf("myGetTransaction(utxo.first.txhash, tx, blkHash1): %s\n", (doNext = myGetTransaction(utxo.first.txhash, tx, blkHash1)) ? "true" : "false");
678 printf("(ccx = CCrossChainExport(tx)).IsValid(): %s\n", (doNext = (ccx = CCrossChainExport(tx)).IsValid()) ? "true" : "false");
680 printf("ccx.numInputs: %d\n", (doNext = ccx.numInputs != 0) ? ccx.numInputs : 0);
682 printf("!tx.IsCoinBase(): %s\n", (doNext = !tx.IsCoinBase()) ? "true" : "false");
684 printf("tx.vin.size(): %lu\n", (doNext = tx.vin.size() != 0) ? tx.vin.size() : 0l);
686 printf("myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)): %s\n", (doNext = myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) ? "true" : "false");
688 printf("inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p): %s\n", (doNext = inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p)) ? "true" : "false");
690 printf("p.IsValid(): %s\n", (doNext = p.IsValid()) ? "true" : "false");
692 printf("p.evalCode == EVAL_CROSSCHAIN_EXPORT: %s\n", (doNext = p.evalCode == EVAL_CROSSCHAIN_EXPORT) ? "true" : "false");
697 CTransaction lastImport(lastCrossChainImport);
699 for (auto aixIt = validExports.find(lastExportHash);
700 aixIt != validExports.end();
701 aixIt = validExports.find(lastExportHash))
703 // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
704 // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
705 // 2. Creates reserve outputs for unconverted imports
706 // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
707 // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
708 // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
709 // then signs the transaction considers it the latest import and the new export the latest export and
710 // loops until there are no more confirmed, consecutive, valid export transactions to export
712 // aixIt has an input from the export thread of last transaction, an optional deposit to the reserve, and an opret of all outputs + 1 fee
713 assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
715 CMutableTransaction newImportTx(importTxTemplate);
717 int ccxOutputNum = 0;
718 CCrossChainExport ccx(aixIt->second.second, &ccxOutputNum);
720 int32_t importOutNum = 0;
721 lastCCI = CCrossChainImport(lastImport, &importOutNum);
722 if (!lastCCI.IsValid() || !ccx.IsValid())
724 LogPrintf("%s: POSSIBLE CORRUPTION bad import/export data in transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
725 printf("%s: POSSIBLE CORRUPTION bad import/export data transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
729 // if no prepared input, make one
730 if (!newImportTx.vin.size())
732 //printf("adding input: %s, %d\n", lastImport.GetHash().GetHex().c_str(), importOutNum);
733 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), importOutNum));
736 newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
742 CCurrencyValueMap availableReserveFees = ccx.totalFees;
743 CCurrencyValueMap exportFees = ccx.CalculateExportFee();
744 CCurrencyValueMap importFees = ccx.CalculateImportFee();
747 CCoinbaseCurrencyState _currencyState, currencyState;
750 // spend export finalization if there is one
751 int32_t finalizeOutNum = -1;
753 CTransactionFinalization ccxFinalization = CTransactionFinalization(aixIt->second.second, &eCode, &finalizeOutNum);
754 if (finalizeOutNum != -1)
756 newImportTx.vin.push_back(CTxIn(aixIt->second.second.GetHash(), finalizeOutNum));
759 if (!NewImportNotarization(currencyDef, nHeight, lastImport, aixIt->second.first.blockHeight, aixIt->second.second, newImportTx, _currencyState))
761 LogPrintf("%s: cannot create notarization for transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
762 printf("%s: cannot create notarization for transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
768 // TODO: confirm that for non-tokens, we don't have to calculate initial state
769 _currencyState = lastConfirmed.currencyState;
772 CReserveTransactionDescriptor rtxd;
773 std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
775 if (!_currencyState.IsValid() ||
776 !rtxd.AddReserveTransferImportOutputs(thisChainID, currencyDef, _currencyState, exportOutputs, newImportTx.vout, ¤cyState))
778 LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
779 printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
781 DeleteOpRetObjects(exportOutputs);
786 DeleteOpRetObjects(exportOutputs);
788 // emit a crosschain import output as summary
789 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
790 pk = CPubKey(ParseHex(CC.CChexstr));
792 CCurrencyValueMap leftoverCurrency = ((availableTokenInput - rtxd.ReserveInputMap()) +
793 CCurrencyValueMap(std::vector<uint160>({systemID}), std::vector<CAmount>({totalNativeInput - rtxd.nativeIn})));
796 printf("%s: leftoverCurrency:\n%s\nReserveInputMap():\n%s\nrtxd.nativeIn:\n%s\nconversionfees:\n%s\ntxreservefees:\n%s\ntxnativefees:\n%ld\nccx.totalfees:\n%s\nexportfees:\n%s\nccx.totalFees - exportFees:\n%s\n\n",
798 leftoverCurrency.ToUniValue().write().c_str(),
799 rtxd.ReserveInputMap().ToUniValue().write().c_str(),
800 ValueFromAmount(rtxd.nativeIn).write().c_str(),
801 (rtxd.ReserveConversionFeesMap() + CCurrencyValueMap(std::vector<uint160>({thisChainID}), std::vector<CAmount>({rtxd.nativeConversionFees}))).ToUniValue().write().c_str(),
802 rtxd.ReserveFees().ToUniValue().write().c_str(),
804 ccx.totalFees.ToUniValue().write().c_str(),
805 exportFees.ToUniValue().write().c_str(),
806 (ccx.totalFees - exportFees).ToUniValue().write().c_str());
809 if (leftoverCurrency.HasNegative())
811 LogPrintf("%s: ERROR - fees do not match. leftoverCurrency:\n%s\nReserveInputMap():\n%s\nrtxd.nativeIn:\n%s\nconversionfees:\n%s\ntxreservefees:\n%s\ntxnativefees:\n%ld\nccx.totalfees:\n%s\nexportfees:\n%s\nccx.totalFees - exportFees:\n%s\n\n",
813 leftoverCurrency.ToUniValue().write().c_str(),
814 rtxd.ReserveInputMap().ToUniValue().write().c_str(),
815 ValueFromAmount(rtxd.nativeIn).write().c_str(),
816 (rtxd.ReserveConversionFeesMap() + CCurrencyValueMap(std::vector<uint160>({thisChainID}), std::vector<CAmount>({rtxd.nativeConversionFees}))).ToUniValue().write().c_str(),
817 rtxd.ReserveFees().ToUniValue().write().c_str(),
819 ccx.totalFees.ToUniValue().write().c_str(),
820 exportFees.ToUniValue().write().c_str(),
821 (ccx.totalFees - exportFees).ToUniValue().write().c_str());
825 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(ccx.systemID, EVAL_CROSSCHAIN_IMPORT)))});
826 std::vector<CTxDestination> dests;
828 if (currencyDef.proofProtocol == CCurrencyDefinition::PROOF_PBAASMMR ||
829 currencyDef.proofProtocol == CCurrencyDefinition::PROOF_CHAINID)
831 dests = std::vector<CTxDestination>({pk});
835 LogPrintf("%s: ERROR - invalid proof protocol\n", __func__);
836 printf("%s: ERROR - invalid proof protocol\n", __func__);
840 CAmount nativeOutConverted = 0;
841 for (auto &oneNativeConversion : rtxd.NativeOutConvertedMap().valueMap)
843 nativeOutConverted += oneNativeConversion.second;
847 printf("DEBUGOUT: nativeOutConverted: %ld, rtxd.ReserveOutConvertedMap():%s\n", nativeOutConverted, rtxd.ReserveOutConvertedMap().ToUniValue().write().c_str());
848 printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
849 printf("rtxd.ReserveInputMap(): %s, ccx.totalAmounts: %s\n", rtxd.ReserveInputMap().ToUniValue().write().c_str(), ccx.totalAmounts.ToUniValue().write().c_str());
850 printf("leftoverCurrency: %s\n", leftoverCurrency.ToUniValue().write().c_str());
853 // breakout native from leftover currency
854 totalNativeInput = leftoverCurrency.valueMap[systemID];
855 leftoverCurrency.valueMap.erase(systemID);
858 printf("DEBUGOUT: totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
859 printf("DEBUGOUT: rtxd.nativeIn: %ld, rtxd.ReserveInputMap():%s\n", rtxd.nativeIn, rtxd.ReserveInputMap().ToUniValue().write().c_str());
862 CCrossChainImport cci = CCrossChainImport(ccx.systemID,
863 rtxd.ReserveOutConvertedMap() +
864 CCurrencyValueMap(std::vector<uint160>({systemID}), std::vector<CAmount>({nativeOutConverted})),
865 leftoverCurrency.CanonicalMap());
867 newImportTx.vout[0] = CTxOut(totalNativeInput, MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests));
869 //printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
870 if (totalNativeInput < 0 || (availableTokenInput - rtxd.ReserveInputMap()).HasNegative())
872 LogPrintf("%s: ERROR - importing more currency than available for %s\n", __func__, currencyDef.name.c_str());
873 LogPrintf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
874 printf("%s: ERROR - importing more currency than available for %s\n", __func__, currencyDef.name.c_str());
875 printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
879 availableTokenInput = leftoverCurrency;
881 // add a proof of the export transaction at the notarization height
883 if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
885 LogPrintf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
886 printf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
890 // prove ccx input 0, export output, and opret
891 std::vector<std::pair<int16_t, int16_t>> parts({{CTransactionHeader::TX_HEADER, (int16_t)0},
892 {CTransactionHeader::TX_PREVOUTSEQ, (int16_t)0},
893 {CTransactionHeader::TX_OUTPUT, ccxOutputNum},
894 {CTransactionHeader::TX_OUTPUT, (int16_t)(aixIt->second.second.vout.size() - 1)}});
896 // prove our transaction up to the MMR root of the last transaction
897 CPartialTransactionProof exportProof = block.GetPartialTransactionProof(aixIt->second.second, aixIt->second.first.txindex, parts);
898 if (!exportProof.components.size())
900 LogPrintf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
901 printf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
904 exportProof.txProof << block.MMRProofBridge();
906 // TODO: don't include chain MMR proof for exports from the same chain
907 ChainMerkleMountainView mmv(chainActive.GetMMR(), lastConfirmed.notarizationHeight);
908 mmv.GetProof(exportProof.txProof, aixIt->second.first.blockHeight);
910 CChainObject<CPartialTransactionProof> exportXProof(CHAINOBJ_TRANSACTION_PROOF, exportProof);
912 CTransaction checkTx;
914 uint256 checkTxID = exportProof.CheckPartialTransaction(checkTx);
915 CMMRProof blockProof;
916 chainActive.GetBlockProof(mmv, blockProof, aixIt->second.first.blockHeight);
917 if (checkTxID != mmv.GetRoot())
920 printf("CTransactionHeaderCheck: %s\n\n", exportProof.components[0].CheckProof().GetHex().c_str());
921 printf("CTransactionProof\n%s\n", exportProof.txProof.ToUniValue().write(1,2).c_str());
922 printf("CTransactionProofCheck: %s\n\n", checkTxID.GetHex().c_str());
923 printf("CBlockProof\n%s\n", blockProof.ToUniValue().write(1,2).c_str());
924 printf("CBlockProofCheck: %s\n\n", blockProof.CheckProof(chainActive[aixIt->second.first.blockHeight]->GetBlockHash()).GetHex().c_str());
925 printf("CBlockHash: %s\n\n", block.GetHash().GetHex().c_str());
926 printf("CBlockMMRRoot: %s\n\n", block.GetBlockMMRRoot().GetHex().c_str());
927 printf("CBlockMMViewRoot: %s\n\n", BlockMMView(block.GetBlockMMRTree()).GetRoot().GetHex().c_str());
928 for (auto &oneNode : mmv.GetPeaks())
930 printf("oneMerklePeakNode: %s:%s\n", oneNode.hash.GetHex().c_str(), oneNode.power.GetHex().c_str());
932 printf("MMVRoot: %s\n\n", mmv.GetRoot().GetHex().c_str());
933 for (auto &oneTx : block.vtx)
935 printf("oneTxRoot: %s\n", TransactionMMView(oneTx.GetTransactionMMR()).GetRoot().GetHex().c_str());
939 printf("%s: invalid partial transaction proof, hash is\n%s, should be\n%s\n", __func__, checkTxID.GetHex().c_str(), mmv.GetRoot().GetHex().c_str());
940 LogPrintf("%s: invalid partial transaction proof, hash is\n%s, should be\n%s\n", __func__, checkTxID.GetHex().c_str(), mmv.GetRoot().GetHex().c_str());
943 uint256 lastProof = exportProof.components[0].CheckProof();
944 for (int i = 1; i < exportProof.components.size(); i++)
946 if (lastProof != exportProof.components[i].CheckProof())
948 printf("%s: error in new proof for component %d\n", __func__, i);
952 // add the opret with the transaction and proof
953 newImportTx.vout.push_back(CTxOut(0, StoreOpRetArray(std::vector<CBaseChainObject *>({&exportXProof}))));
955 // we now have an Import transaction for the chainID chain, it is the latest, and the export we used is now the latest as well
956 // work our way forward
957 lastImport = newImportTx;
958 newImports.push_back(lastImport);
959 lastExportHash = aixIt->second.first.txhash;
960 //printf("loop again with lastImport.GetHash(): %s, lastExportHash %s\n", lastImport.GetHash().GetHex().c_str(), lastExportHash.GetHex().c_str());
966 void CheckPBaaSAPIsValid()
968 //printf("Solution version running: %d\n\n", CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()));
969 if (!chainActive.LastTip() ||
970 CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_PBAAS)
972 throw JSONRPCError(RPC_INVALID_REQUEST, "PBaaS not activated on blockchain.");
976 void CheckIdentityAPIsValid()
978 if (!chainActive.LastTip() ||
979 CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
981 throw JSONRPCError(RPC_INVALID_REQUEST, "Identity APIs not activated on blockchain.");
985 uint160 ValidateCurrencyName(std::string currencyStr, CCurrencyDefinition *pCurrencyDef=NULL)
987 std::string extraName;
989 currencyStr = TrimSpaces(currencyStr);
990 if (!currencyStr.size())
994 ParseSubNames(currencyStr, extraName, true);
995 if (currencyStr.back() == '@' || (extraName != "" && boost::to_lower_copy(extraName) != boost::to_lower_copy(VERUS_CHAINNAME)))
999 if (CCurrencyDefinition::GetID(currencyStr) == ConnectedChains.ThisChain().GetID())
1003 *pCurrencyDef = ConnectedChains.ThisChain();
1005 return ConnectedChains.ThisChain().GetID();
1007 CTxDestination currencyDest = DecodeDestination(currencyStr);
1008 if (currencyDest.which() == COptCCParams::ADDRTYPE_INVALID)
1010 currencyDest = DecodeDestination(currencyStr + "@");
1012 if (currencyDest.which() != COptCCParams::ADDRTYPE_INVALID)
1014 // make sure there is such a currency defined on this chain
1015 CCurrencyDefinition currencyDef;
1016 if (GetCurrencyDefinition(GetDestinationID(currencyDest), currencyDef))
1018 retVal = currencyDef.GetID();
1022 *pCurrencyDef = currencyDef;
1028 uint160 GetChainIDFromParam(const UniValue ¶m, CCurrencyDefinition *pCurrencyDef=NULL)
1030 return ValidateCurrencyName(uni_get_str(param), pCurrencyDef);
1033 UniValue getcurrency(const UniValue& params, bool fHelp)
1035 if (fHelp || params.size() != 1)
1037 throw runtime_error(
1038 "getcurrency \"chainname\"\n"
1039 "\nReturns a complete definition for any given chain if it is registered on the blockchain. If the chain requested\n"
1040 "\nis NULL, chain definition of the current chain is returned.\n"
1043 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1047 " \"version\" : n, (int) version of this chain definition\n"
1048 " \"name\" : \"string\", (string) name or symbol of the chain, same as passed\n"
1049 " \"address\" : \"string\", (string) cryptocurrency address to send fee and non-converted premine\n"
1050 " \"currencyid\" : \"i-address\", (string) string that represents the currency ID, same as the ID behind the currency\n"
1051 " \"premine\" : n, (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
1052 " \"convertible\" : \"xxxx\" (bool) if this currency is a fractional reserve currency of Verus\n"
1053 " \"startblock\" : n, (int) block # on this chain, which must be notarized into block one of the chain\n"
1054 " \"endblock\" : n, (int) block # after which, this chain's useful life is considered to be over\n"
1055 " \"eras\" : \"[obj, ...]\", (objarray) different chain phases of rewards and convertibility\n"
1057 " \"reward\" : \"[n, ...]\", (int) reward start for each era in native coin\n"
1058 " \"decay\" : \"[n, ...]\", (int) exponential or linear decay of rewards during each era\n"
1059 " \"halving\" : \"[n, ...]\", (int) blocks between halvings during each era\n"
1060 " \"eraend\" : \"[n, ...]\", (int) block marking the end of each era\n"
1061 " \"eraoptions\" : \"[n, ...]\", (int) options (reserved)\n"
1063 " \"nodes\" : \"[obj, ..]\", (objectarray, optional) up to 8 nodes that can be used to connect to the blockchain"
1065 " \"nodeidentity\" : \"txid\", (string, optional) internet, TOR, or other supported address for node\n"
1066 " \"paymentaddress\" : n, (int, optional) rewards payment address\n"
1068 " \"bestnotarization\" : {\n"
1070 " \"besttxid\" : \"txid\"\n"
1072 " \"confirmednotarization\" : {\n"
1074 " \"confirmedtxid\" : \"txid\"\n"
1078 + HelpExampleCli("getcurrency", "\"chainname\"")
1079 + HelpExampleRpc("getcurrency", "\"chainname\"")
1083 CheckPBaaSAPIsValid();
1084 LOCK2(cs_main, mempool.cs);
1086 UniValue ret(UniValue::VOBJ);
1088 CCurrencyDefinition chainDef;
1089 uint160 chainID = GetChainIDFromParam(params[0], &chainDef);
1091 if (chainID.IsNull())
1093 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1096 if (chainDef.IsValid())
1098 ret = chainDef.ToUniValue();
1100 if (chainDef.IsToken() && chainDef.systemID == ASSETCHAINS_CHAINID)
1102 ret.push_back(Pair("bestcurrencystate", ConnectedChains.GetCurrencyState(chainID, chainActive.Height() + 1).ToUniValue()));
1106 CChainNotarizationData cnd;
1107 GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
1109 std::vector<CNodeData> vNNodes;
1110 int32_t confirmedHeight = -1, bestHeight = -1;
1112 if (cnd.forks.size())
1114 // get all nodes from notarizations of the best chain into a vector
1115 for (auto &oneNot : cnd.forks[cnd.bestChain])
1117 vNNodes.insert(vNNodes.end(), cnd.vtx[oneNot].second.nodes.begin(), cnd.vtx[oneNot].second.nodes.end());
1120 confirmedHeight = cnd.vtx.size() && cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed].second.notarizationHeight : -1;
1121 bestHeight = cnd.vtx.size() && cnd.bestChain != -1 ? cnd.vtx[cnd.forks[cnd.bestChain].back()].second.notarizationHeight : -1;
1123 // shuffle and take 8 nodes,
1124 // up to two of which will be from the last confirmed notarization if present
1125 std::random_shuffle(vNNodes.begin(), vNNodes.end());
1126 int numConfirmedNodes = cnd.lastConfirmed == -1 ? 0 : cnd.vtx[cnd.lastConfirmed].second.nodes.size();
1127 if (vNNodes.size() > (8 - numConfirmedNodes))
1131 if (numConfirmedNodes)
1133 vNNodes.insert(vNNodes.begin(), cnd.vtx[cnd.lastConfirmed].second.nodes.begin(), cnd.vtx[cnd.lastConfirmed].second.nodes.end());
1137 UniValue nodeArr(UniValue::VARR);
1138 for (auto &oneNode : vNNodes)
1140 nodeArr.push_back(oneNode.ToUniValue());
1142 ret.push_back(Pair("nodes", nodeArr));
1146 if (!chainDef.IsToken())
1148 ret.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
1149 if (confirmedHeight != -1)
1151 ret.push_back(Pair("lastconfirmedtxid", cnd.vtx[cnd.lastConfirmed].first.GetHex().c_str()));
1152 ret.push_back(Pair("lastconfirmedcurrencystate", cnd.vtx[cnd.lastConfirmed].second.currencyState.ToUniValue()));
1155 ret.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
1156 if (bestHeight != -1)
1158 ret.push_back(Pair("besttxid", cnd.vtx[cnd.forks[cnd.bestChain].back()].first.GetHex().c_str()));
1159 ret.push_back(Pair("bestcurrencystate", cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.ToUniValue()));
1166 return NullUniValue;
1170 UniValue getpendingtransfers(const UniValue& params, bool fHelp)
1172 if (fHelp || params.size() > 1)
1174 throw runtime_error(
1175 "getpendingtransfers \"chainname\"\n"
1176 "\nReturns all pending transfers for a particular chain that have not yet been aggregated into an export\n"
1179 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1186 + HelpExampleCli("getpendingtransfers", "\"chainname\"")
1187 + HelpExampleRpc("getpendingtransfers", "\"chainname\"")
1191 CheckPBaaSAPIsValid();
1193 uint160 chainID = GetChainIDFromParam(params[0]);
1195 if (chainID.IsNull())
1197 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1200 CCurrencyDefinition chainDef;
1205 if ((IsVerusActive() && GetCurrencyDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetID() == chainID)
1207 // look for new exports
1208 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> inputDescriptors;
1210 if (GetUnspentChainTransfers(inputDescriptors, chainID))
1212 UniValue ret(UniValue::VARR);
1214 for (auto &desc : inputDescriptors)
1216 UniValue oneExport(UniValue::VOBJ);
1218 oneExport.push_back(Pair("currencyid", EncodeDestination(CIdentityID(desc.first))));
1219 oneExport.push_back(Pair("txid", desc.second.first.txIn.prevout.hash.GetHex()));
1220 oneExport.push_back(Pair("n", (int32_t)desc.second.first.txIn.prevout.n));
1221 oneExport.push_back(Pair("valueout", desc.second.first.nValue));
1222 oneExport.push_back(Pair("reservetransfer", desc.second.second.ToUniValue()));
1223 ret.push_back(oneExport);
1233 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
1236 return NullUniValue;
1239 UniValue getexports(const UniValue& params, bool fHelp)
1241 if (fHelp || params.size() != 1)
1243 throw runtime_error(
1244 "getexports \"chainname\"\n"
1245 "\nReturns all pending export transfers that are not yet provable with confirmed notarizations.\n"
1248 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1255 + HelpExampleCli("getexports", "\"chainname\"")
1256 + HelpExampleRpc("getexports", "\"chainname\"")
1260 CheckPBaaSAPIsValid();
1262 LOCK2(cs_main, mempool.cs);
1264 uint160 chainID = GetChainIDFromParam(params[0]);
1266 if (chainID.IsNull())
1268 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1271 CCurrencyDefinition chainDef;
1274 if (GetCurrencyDefinition(chainID, chainDef, &defHeight))
1276 // which transaction are we in this block?
1277 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1279 CChainNotarizationData cnd;
1280 if (GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd))
1282 // get all export transactions including and since this one up to the confirmed height
1283 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT),
1284 1, addressIndex, cnd.IsConfirmed() ? cnd.vtx[cnd.lastConfirmed].second.crossHeight : defHeight))
1286 UniValue ret(UniValue::VARR);
1288 for (auto &idx : addressIndex)
1291 CTransaction exportTx;
1292 if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
1294 std::vector<CBaseChainObject *> opretTransfers;
1295 CCrossChainExport ccx;
1296 if ((ccx = CCrossChainExport(exportTx)).IsValid() && exportTx.vout.back().scriptPubKey.IsOpReturn())
1298 UniValue oneExport(UniValue::VOBJ);
1299 UniValue transferArray(UniValue::VARR);
1300 opretTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
1301 oneExport.push_back(Pair("blockheight", idx.first.blockHeight));
1302 oneExport.push_back(Pair("exportid", idx.first.txhash.GetHex()));
1303 oneExport.push_back(Pair("description", ccx.ToUniValue()));
1304 for (auto oneTransfer : opretTransfers)
1306 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
1308 transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
1311 DeleteOpRetObjects(opretTransfers);
1312 oneExport.push_back(Pair("transfers", transferArray));
1313 ret.push_back(oneExport);
1326 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
1329 return NullUniValue;
1332 UniValue getimports(const UniValue& params, bool fHelp)
1334 if (fHelp || params.size() != 1)
1336 throw runtime_error(
1337 "getimports \"chainname\"\n"
1338 "\nReturns all imports from a specific chain.\n"
1341 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1348 + HelpExampleCli("getimports", "\"chainname\"")
1349 + HelpExampleRpc("getimports", "\"chainname\"")
1353 CheckPBaaSAPIsValid();
1355 uint160 chainID = GetChainIDFromParam(params[0]);
1357 if (chainID.IsNull())
1359 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1362 CCurrencyDefinition chainDef;
1367 if (GetCurrencyDefinition(chainID, chainDef, &defHeight))
1369 // which transaction are we in this block?
1370 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1372 CBlockIndex *pIndex;
1374 CChainNotarizationData cnd;
1375 // get all import transactions including and since this one up to the confirmed height
1376 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT), 1, addressIndex))
1378 UniValue ret(UniValue::VARR);
1380 for (auto &idx : addressIndex)
1383 CTransaction importTx;
1384 if (!idx.first.spending && myGetTransaction(idx.first.txhash, importTx, blkHash))
1386 CCrossChainImport cci;
1387 if ((cci = CCrossChainImport(importTx)).IsValid() && importTx.vout.back().scriptPubKey.IsOpReturn())
1389 std::vector<CBaseChainObject *> opretImports;
1390 CTransaction exportTx;
1391 CCrossChainExport ccx;
1393 opretImports = RetrieveOpRetArray(importTx.vout.back().scriptPubKey);
1395 if (opretImports.size() >= 1 &&
1396 opretImports[0]->objectType == CHAINOBJ_TRANSACTION_PROOF &&
1397 !((CChainObject<CPartialTransactionProof> *)opretImports[0])->object.GetPartialTransaction(exportTx).IsNull() &&
1398 (ccx = CCrossChainExport(exportTx)).IsValid() &&
1400 exportTx.vout.size() &&
1401 exportTx.vout.back().scriptPubKey.IsOpReturn())
1403 std::vector<CBaseChainObject *> opretTransfers;
1405 UniValue oneImport(UniValue::VOBJ);
1406 UniValue transferArray(UniValue::VARR);
1407 opretTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
1408 oneImport.push_back(Pair("blockheight", idx.first.blockHeight));
1409 oneImport.push_back(Pair("importid", idx.first.txhash.GetHex()));
1410 oneImport.push_back(Pair("description", cci.ToUniValue()));
1411 for (auto oneTransfer : opretTransfers)
1413 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
1415 transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
1418 DeleteOpRetObjects(opretTransfers);
1419 oneImport.push_back(Pair("transfers", transferArray));
1420 ret.push_back(oneImport);
1422 DeleteOpRetObjects(opretImports);
1434 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
1436 return NullUniValue;
1439 UniValue listcurrencies(const UniValue& params, bool fHelp)
1441 if (fHelp || params.size() > 1)
1443 throw runtime_error(
1444 "listcurrencies (includeexpired)\n"
1445 "\nReturns a complete definition for any given chain if it is registered on the blockchain. If the chain requested\n"
1446 "\nis NULL, chain definition of the current chain is returned.\n"
1449 "1. \"includeexpired\" (bool, optional) if true, include chains that are no longer active\n"
1454 " \"version\" : n, (int) version of this chain definition\n"
1455 " \"name\" : \"string\", (string) name or symbol of the chain, same as passed\n"
1456 " \"address\" : \"string\", (string) cryptocurrency address to send fee and non-converted premine\n"
1457 " \"currencyid\" : \"hex-string\", (string) i-address that represents the chain ID, same as the ID that launched the chain\n"
1458 " \"premine\" : n, (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
1459 " \"convertible\" : \"xxxx\" (bool) if this currency is a fractional reserve currency of Verus\n"
1460 " \"startblock\" : n, (int) block # on this chain, which must be notarized into block one of the chain\n"
1461 " \"endblock\" : n, (int) block # after which, this chain's useful life is considered to be over\n"
1462 " \"eras\" : \"[obj, ...]\", (objarray) different chain phases of rewards and convertibility\n"
1464 " \"reward\" : \"[n, ...]\", (int) reward start for each era in native coin\n"
1465 " \"decay\" : \"[n, ...]\", (int) exponential or linear decay of rewards during each era\n"
1466 " \"halving\" : \"[n, ...]\", (int) blocks between halvings during each era\n"
1467 " \"eraend\" : \"[n, ...]\", (int) block marking the end of each era\n"
1468 " \"eraoptions\" : \"[n, ...]\", (int) options (reserved)\n"
1470 " \"nodes\" : \"[obj, ..]\", (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
1472 " \"nodeaddress\" : \"txid\", (string, optional) internet, TOR, or other supported address for node\n"
1473 " \"paymentaddress\" : n, (int, optional) rewards payment address\n"
1479 + HelpExampleCli("listcurrencies", "true")
1480 + HelpExampleRpc("listcurrencies", "true")
1484 CheckPBaaSAPIsValid();
1486 UniValue ret(UniValue::VARR);
1488 bool includeExpired = params[0].isBool() ? params[0].get_bool() : false;
1490 vector<CCurrencyDefinition> chains;
1492 LOCK2(cs_main, mempool.cs);
1493 GetCurrencyDefinitions(chains, includeExpired);
1496 for (auto def : chains)
1498 LOCK2(cs_main, mempool.cs);
1500 UniValue oneChain(UniValue::VOBJ);
1501 oneChain.push_back(Pair("currencydefinition", def.ToUniValue()));
1503 CChainNotarizationData cnd;
1504 GetNotarizationData(def.GetID(), IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
1506 int32_t confirmedHeight = -1, bestHeight = -1;
1507 confirmedHeight = cnd.vtx.size() && cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed].second.notarizationHeight : -1;
1508 bestHeight = cnd.vtx.size() && cnd.bestChain != -1 ? cnd.vtx[cnd.forks[cnd.bestChain].back()].second.notarizationHeight : -1;
1512 oneChain.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
1513 if (confirmedHeight != -1)
1515 oneChain.push_back(Pair("lastconfirmedtxid", cnd.vtx[cnd.lastConfirmed].first.GetHex().c_str()));
1516 oneChain.push_back(Pair("lastconfirmedcurrencystate", cnd.vtx[cnd.lastConfirmed].second.currencyState.ToUniValue()));
1519 oneChain.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
1520 if (bestHeight != -1)
1522 oneChain.push_back(Pair("besttxid", cnd.vtx[cnd.forks[cnd.bestChain].back()].first.GetHex().c_str()));
1523 oneChain.push_back(Pair("bestcurrencystate", cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.ToUniValue()));
1525 ret.push_back(oneChain);
1530 // returns all chain transfer outputs, both spent and unspent between a specific start and end block with an optional chainFilter. if the chainFilter is not
1531 // NULL, only transfers to that system are returned
1532 bool GetChainTransfers(multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> &inputDescriptors, uint160 chainFilter, int start, int end, uint32_t flags)
1536 flags = CReserveTransfer::VALID;
1538 bool nofilter = chainFilter.IsNull();
1540 // which transaction are we in this block?
1541 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1542 std::set<uint256> countedTxes; // don't count twice
1544 LOCK2(cs_main, mempool.cs);
1546 if (!GetAddressIndex(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER), 1, addressIndex, start, end))
1552 for (auto it = addressIndex.begin(); it != addressIndex.end(); it++)
1557 // each tx gets counted once
1558 if (countedTxes.count(it->first.txhash))
1562 countedTxes.insert(it->first.txhash);
1564 if (myGetTransaction(it->first.txhash, ntx, blkHash))
1568 UniValue univTx(UniValue::VOBJ);
1569 TxToUniv(ntx, hashBlk, univTx);
1570 printf("tx: %s\n", univTx.write(1,2).c_str());
1572 for (int i = 0; i < ntx.vout.size(); i++)
1574 // if this is a transfer output, optionally to this chain, add it to the input vector
1576 std::vector<CTxDestination> dests;
1577 int numRequired = 0;
1579 if (ExtractDestinations(ntx.vout[i].scriptPubKey, typeRet, dests, numRequired))
1583 printf("filter: %s\n", EncodeDestination(CKeyID(chainFilter)).c_str());
1585 for (auto &oneDest : dests)
1587 printf("%s\n", EncodeDestination(oneDest).c_str());
1593 CReserveTransfer rt;
1594 if (ntx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
1595 p.evalCode == EVAL_RESERVE_TRANSFER &&
1596 p.vData.size() > 1 && (rt = CReserveTransfer(p.vData[0])).IsValid() &&
1597 (m = COptCCParams(p.vData[1])).IsValid() &&
1598 (nofilter || (m.vKeys.size() > 1 && GetDestinationID(m.vKeys[1]) == chainFilter)) &&
1599 (rt.flags & flags) == flags)
1601 inputDescriptors.insert(make_pair(GetDestinationID(m.vKeys[1]),
1602 make_pair(CInputDescriptor(ntx.vout[i].scriptPubKey, ntx.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))), rt)));
1604 else if (p.IsValid() &&
1605 p.evalCode == EVAL_RESERVE_TRANSFER &&
1606 p.version != p.VERSION_V3)
1608 // if we change the version, stop here in case it wasn't caught
1615 LogPrintf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1616 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1624 // returns all unspent chain transfer outputs with an optional chainFilter. if the chainFilter is not
1625 // NULL, only transfers to that chain are returned
1626 bool GetUnspentChainTransfers(multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> &inputDescriptors, uint160 chainFilter)
1628 bool nofilter = chainFilter.IsNull();
1630 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1632 LOCK2(cs_main, mempool.cs);
1634 if (!GetAddressUnspent(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER), 1, unspentOutputs))
1640 CCoinsViewCache view(pcoinsTip);
1642 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1646 if (view.GetCoins(it->first.txhash, coins))
1648 if (coins.IsAvailable(it->first.index))
1650 // if this is a transfer output, optionally to this chain, add it to the input vector
1651 // chain filter was applied in index search
1654 CReserveTransfer rt;
1655 if (::IsPayToCryptoCondition(coins.vout[it->first.index].scriptPubKey, p) && p.evalCode == EVAL_RESERVE_TRANSFER &&
1656 p.vData.size() > 1 &&
1657 p.version >= p.VERSION_V3 &&
1658 (m = COptCCParams(p.vData.back())).IsValid() &&
1659 (rt = CReserveTransfer(p.vData[0])).IsValid() &&
1660 m.vKeys.size() > 1 &&
1661 (nofilter || GetDestinationID(m.vKeys[1]) == chainFilter))
1663 inputDescriptors.insert(make_pair(GetDestinationID(m.vKeys[1]),
1664 make_pair(CInputDescriptor(coins.vout[it->first.index].scriptPubKey,
1665 coins.vout[it->first.index].nValue,
1666 CTxIn(COutPoint(it->first.txhash, it->first.index))), rt)));
1672 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1679 // returns all unspent chain transfer outputs with an optional chainFilter. if the chainFilter is not
1680 // NULL, only transfers to that chain are returned
1681 bool GetUnspentChainExports(uint160 chainID, multimap<uint160, pair<int, CInputDescriptor>> &exportOutputs)
1683 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1685 LOCK2(cs_main, mempool.cs);
1687 if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT), 1, unspentOutputs))
1693 CCoinsViewCache view(pcoinsTip);
1695 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1699 if (view.GetCoins(it->first.txhash, coins))
1701 for (int i = 0; i < coins.vout.size(); i++)
1703 if (coins.IsAvailable(i))
1705 // if this is an export output, optionally to this chain, add it to the input vector
1707 CCrossChainExport cx;
1708 if (coins.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
1709 p.vData.size() && (cx = CCrossChainExport(p.vData[0])).IsValid())
1711 exportOutputs.insert(make_pair(cx.systemID,
1712 make_pair(coins.nHeight, CInputDescriptor(coins.vout[i].scriptPubKey, coins.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))))));
1719 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1727 bool GetNotarizationData(uint160 chainID, uint32_t ecode, CChainNotarizationData ¬arizationData, vector<pair<CTransaction, uint256>> *optionalTxOut)
1729 notarizationData.version = PBAAS_VERSION;
1731 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1732 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentFinalizations;
1733 CCurrencyDefinition chainDef;
1735 bool isEarned = ecode == EVAL_EARNEDNOTARIZATION;
1736 bool allFinalized = false;
1738 // get the last unspent notarization for this currency
1739 if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZE_NOTARIZATION), 1, unspentFinalizations))
1745 multimap<int32_t, pair<uint256, CPBaaSNotarization>> sorted;
1746 multimap<int32_t, pair<CTransaction, uint256>> sortedTxs;
1748 notarizationData.lastConfirmed = 0;
1750 // filter out all transactions that do not spend from the notarization thread, or originate as the
1752 for (auto it = unspentFinalizations.begin(); it != unspentFinalizations.end(); it++)
1754 // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1758 if (myGetTransaction(it->first.txhash, ntx, blkHash))
1760 if (!chainDef.IsValid())
1762 // try to make a chain definition out of each transaction, and keep the first one that is valid
1763 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(ntx);
1764 if (chainDefs.size())
1766 chainDef = chainDefs[0];
1769 CPBaaSNotarization notarization = CPBaaSNotarization(ntx);
1770 if (notarization.IsValid())
1772 auto blkit = mapBlockIndex.find(blkHash);
1773 if (blkit != mapBlockIndex.end())
1775 // sort by block height, index by transaction id
1776 sorted.insert(make_pair(blkit->second->GetHeight(), make_pair(it->first.txhash, notarization)));
1779 sortedTxs.insert(make_pair(blkit->second->GetHeight(), make_pair(ntx, blkHash)));
1782 // if we are not on block 1 of a PBaaS chain and we are a first notarization that is not confirmed, none can be confirmed yet
1783 if (notarization.prevHeight == 0 && !isEarned)
1785 notarizationData.lastConfirmed = -1;
1791 printf("cannot retrieve transaction %s, may need to reindex\n", it->first.txhash.GetHex().c_str());
1798 allFinalized = true;
1800 if (GetAddressUnspent(CCrossChainRPCData::GetConditionID(chainID, ecode), 1, unspentOutputs))
1802 // filter out all transactions that do not spend from the notarization thread, or originate as the
1804 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1806 // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1810 if (myGetTransaction(it->first.txhash, ntx, blkHash))
1812 if (!chainDef.IsValid())
1814 // try to make a chain definition out of each transaction, and keep the first one that is valid
1815 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(ntx);
1816 if (chainDefs.size())
1818 chainDef = chainDefs[0];
1821 CPBaaSNotarization notarization = CPBaaSNotarization(ntx);
1822 if (notarization.IsValid())
1824 auto blkit = mapBlockIndex.find(blkHash);
1825 if (blkit != mapBlockIndex.end())
1827 // sort by block height, index by transaction id
1828 sorted.insert(make_pair(blkit->second->GetHeight(), make_pair(it->first.txhash, notarization)));
1831 sortedTxs.insert(make_pair(blkit->second->GetHeight(), make_pair(ntx, blkHash)));
1834 // if we are not on block 1 of a PBaaS chain and we are a first notarization that is not confirmed, none can be confirmed yet
1835 if (notarization.prevHeight == 0 && !isEarned)
1837 notarizationData.lastConfirmed = -1;
1843 printf("cannot retrieve transaction %s, may need to reindex 2\n", it->first.txhash.GetHex().c_str());
1851 //printf("no notarizations found\n");
1858 notarizationData.vtx.push_back(sorted.begin()->second);
1859 if (optionalTxOut && sortedTxs.size())
1861 optionalTxOut->push_back(sortedTxs.begin()->second);
1863 notarizationData.forks.push_back(vector<int32_t>(0));
1864 notarizationData.forks.back().push_back(0);
1865 notarizationData.bestChain = 0;
1869 if (!chainDef.IsValid())
1871 // the first entry of all forks must reference a confirmed transaction if there is one
1872 CTransaction rootTx;
1874 auto prevHash = sorted.begin()->second.second.prevNotarization;
1875 if (!prevHash.IsNull())
1877 if (!myGetTransaction(prevHash, rootTx, blkHash))
1882 // ensure that we have a finalization output
1884 CPBaaSNotarization notarization;
1885 CTransactionFinalization finalization;
1886 uint32_t notarizeIdx, finalizeIdx;
1888 if (GetNotarizationAndFinalization(ecode, CMutableTransaction(rootTx), notarization, ¬arizeIdx, &finalizeIdx))
1890 notarizationData.vtx.insert(notarizationData.vtx.begin(), make_pair(prevHash, notarization));
1891 notarizationData.lastConfirmed = 0;
1894 optionalTxOut->insert(optionalTxOut->begin(), make_pair(rootTx, blkHash));
1897 // debugging, this else is not needed
1900 printf("previous transaction does not have both notarization and finalizaton outputs\n");
1907 notarizationData.lastConfirmed = -1;
1911 else if (!chainDef.IsToken())
1915 // we still have the chain definition in our forks, so no notarization has been confirmed yet
1916 notarizationData.lastConfirmed = -1;
1920 multimap<uint256, pair<int32_t, int32_t>> references; // associates the txid, the fork index, and the index in the fork
1922 for (auto p : sorted)
1924 notarizationData.vtx.push_back(make_pair(p.second.first, p.second.second));
1929 for (auto p : sortedTxs)
1931 optionalTxOut->push_back(p.second);
1935 // we now have all unspent notarizations sorted by block height, and the last confirmed notarization as first, if there
1936 // is one. if there is a confirmed notarization, all forks should refer to it, or they are invalid and should be spent.
1938 // find roots and create a chain from each
1939 for (int32_t i = 0; i < notarizationData.vtx.size(); i++)
1941 auto &nzp = notarizationData.vtx[i];
1942 auto it = nzp.second.prevNotarization.IsNull() ? references.end() : references.find(nzp.second.prevNotarization);
1944 int32_t chainIdx = 0;
1947 // do we refer to a notarization that is already in a fork?
1948 if (it != references.end())
1950 std::vector<int32_t> &fork = notarizationData.forks[it->second.first];
1952 // if it is the end of the fork, put this entry there, if not the end, copy up to it and start another fork
1953 if (it->second.second == (fork.size() - 1))
1956 chainIdx = it->second.first;
1957 posIdx = fork.size() - 1;
1961 notarizationData.forks.push_back(vector<int32_t>(&fork[0], &fork[it->second.second] + 1));
1962 notarizationData.forks.back().push_back(i);
1963 chainIdx = notarizationData.forks.size() - 1;
1964 posIdx = notarizationData.forks.back().size() - 1;
1969 // start a new fork that references no one else
1970 notarizationData.forks.push_back(vector<int32_t>(0));
1971 notarizationData.forks.back().push_back(i);
1972 chainIdx = notarizationData.forks.size() - 1;
1973 posIdx = notarizationData.forks.back().size() - 1;
1975 references.insert(make_pair(nzp.first, make_pair(chainIdx, posIdx)));
1980 // now, we should have all forks in vectors
1981 // they should all have roots that point to the same confirmed or initial notarization, which should be enforced by chain rules
1982 // the best chain should simply be the tip with most power
1983 for (int i = 0; i < notarizationData.forks.size(); i++)
1985 CChainPower curPower = ExpandCompactPower(notarizationData.vtx[notarizationData.forks[i].back()].second.compactPower, i);
1986 if (curPower > best)
1991 notarizationData.bestChain = best.nHeight;
1996 UniValue getnotarizationdata(const UniValue& params, bool fHelp)
1998 if (fHelp || params.size() < 1 || params.size() > 2)
2000 throw runtime_error(
2001 "getnotarizationdata \"currencyid\" accepted\n"
2002 "\nReturns the latest PBaaS notarization data for the specifed currencyid.\n"
2005 "1. \"currencyid\" (string, required) the hex-encoded ID or string name search for notarizations on\n"
2006 "2. \"accepted\" (bool, optional) accepted, not earned notarizations, default: true if on\n"
2007 " VRSC or VRSCTEST, false otherwise\n"
2011 " \"version\" : n, (numeric) The notarization protocol version\n"
2015 + HelpExampleCli("getnotarizationdata", "\"currencyid\" true")
2016 + HelpExampleRpc("getnotarizationdata", "\"currencyid\"")
2020 CheckPBaaSAPIsValid();
2023 CChainNotarizationData nData;
2026 if (IsVerusActive())
2028 ecode = EVAL_ACCEPTEDNOTARIZATION;
2032 ecode = EVAL_EARNEDNOTARIZATION;
2035 LOCK2(cs_main, mempool.cs);
2037 chainID = GetChainIDFromParam(params[0]);
2039 if (chainID.IsNull())
2041 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid currencyid");
2044 if (params.size() > 1)
2046 if (!params[1].get_bool())
2048 ecode = EVAL_EARNEDNOTARIZATION;
2052 if (GetNotarizationData(chainID, ecode, nData))
2054 return nData.ToUniValue();
2058 return NullUniValue;
2062 // get inputs for all of the unspent reward outputs sent to a specific reward type for the range specified
2063 // returns the total input amount
2064 CAmount GetUnspentRewardInputs(const CCurrencyDefinition &chainDef, vector<CInputDescriptor> &inputs, int32_t serviceCode, int32_t height)
2067 uint160 chainID = chainDef.GetID();
2069 // look for unspent outputs that match the addressout hashed with service code
2070 CKeyID keyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, serviceCode), EVAL_SERVICEREWARD));
2072 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
2074 if (GetAddressUnspent(keyID, 1, unspentOutputs))
2076 // spend all billing periods prior or equal to this one
2077 int billingPeriod = height / chainDef.billingPeriod;
2079 // we need to look through the inputs to ensure that they are
2080 // actual service reward outputs in the correct billing periof, since we don't currently prevent other types of transaction outputs from being
2081 // sent to the same address, though doing so would burn its value anyhow
2084 for (auto output : unspentOutputs)
2086 // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
2089 if (pcoinsTip->GetCoins(output.first.txhash, coins))
2091 for (auto txout : coins.vout)
2094 if (!txout.IsNull() && IsPayToCryptoCondition(txout.scriptPubKey, p) && p.evalCode == EVAL_SERVICEREWARD)
2096 FromVector(p.vData[0], sr);
2099 inputs.push_back(CInputDescriptor(txout.scriptPubKey, txout.nValue, CTxIn(output.first.txhash, output.first.index)));
2100 retval += txout.nValue;
2105 LogPrintf("GetUnspentRewardInputs: cannot retrieve transaction %s\n", output.first.txhash.GetHex().c_str());
2106 printf("GetUnspentRewardInputs: cannot retrieve transaction %s\n", output.first.txhash.GetHex().c_str());
2115 // this adds any new notarization rewards that have been sent to the notarization reward pool for this
2116 // billing period since last accepted notarization, up to a maximum number of inputs
2117 CAmount AddNewNotarizationRewards(CCurrencyDefinition &chainDef, vector<CInputDescriptor> &inputs, CMutableTransaction mnewTx, int32_t height)
2119 // get current chain info
2121 newIn = GetUnspentRewardInputs(chainDef, inputs, SERVICE_NOTARIZATION, height);
2122 for (auto input : inputs)
2124 mnewTx.vin.push_back(input.txIn);
2129 UniValue submitacceptednotarization(const UniValue& params, bool fHelp)
2131 if (fHelp || params.size() != 1)
2133 throw runtime_error(
2134 "submitacceptednotarization \"hextx\"\n"
2135 "\nFinishes an almost complete notarization transaction based on the notary chain and the current wallet or pubkey.\n"
2136 "\nIf successful in submitting the transaction based on all rules, a transaction ID is returned, otherwise, NULL.\n"
2139 "1. \"hextx\" (hexstring, required) partial hex-encoded notarization transaction to submit\n"
2140 " transaction should have only one notarization and one opret output\n"
2143 "txid (hexstring) transaction ID of submitted transaction\n"
2146 + HelpExampleCli("submitacceptednotarization", "\"hextx\"")
2147 + HelpExampleRpc("submitacceptednotarization", "\"hextx\"")
2151 CheckPBaaSAPIsValid();
2153 // decode the transaction and ensure that it is formatted as expected
2154 CTransaction notarization;
2155 CPBaaSNotarization pbn;
2157 if (!DecodeHexTx(notarization, params[0].get_str()) ||
2158 notarization.vin.size() ||
2159 notarization.vout.size() != 2 ||
2160 !(pbn = CPBaaSNotarization(notarization)).IsValid() ||
2161 !notarization.vout.back().scriptPubKey.IsOpReturn())
2163 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid notarization transaction");
2166 LOCK2(cs_main, mempool.cs);
2168 CCurrencyDefinition chainDef;
2169 int32_t chainDefHeight;
2170 if (!GetCurrencyDefinition(pbn.currencyID, chainDef, &chainDefHeight))
2172 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain notarization");
2175 // ensure we are still eligible to submit
2176 // finalize all transactions we can and send the notarization reward, plus all orphaned finalization outputs
2177 // to the confirmed recipient
2179 CChainNotarizationData nData;
2180 vector<pair<CTransaction, uint256>> txesBlkHashes;
2182 bool success = GetNotarizationData(pbn.currencyID, EVAL_ACCEPTEDNOTARIZATION, nData, &txesBlkHashes);
2184 // get notarization data and check all transactions
2188 //LogPrintf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
2189 //printf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
2191 // if any notarization exists that is accepted and more recent than the last one, but one we still agree with,
2192 // we cannot submit, aside from that, we will prepare and submit
2193 set<uint256> priorBlocks;
2194 map<uint256, CPBaaSNotarization *> notarizationData;
2196 // printf("opRet: %s\n", notarization.vout[notarization.vout.size() - 1].scriptPubKey.ToString().c_str());
2198 auto chainObjects = RetrieveOpRetArray(notarization.vout[notarization.vout.size() - 1].scriptPubKey);
2200 bool stillValid = false;
2201 if (chainObjects.size() && chainObjects.back()->objectType == CHAINOBJ_PRIORBLOCKS)
2203 // once here, default to true
2206 CPriorBlocksCommitment &pbc = ((CChainObject<CPriorBlocksCommitment> *)chainObjects.back())->object;
2207 for (auto prior : pbc.priorBlocks)
2209 priorBlocks.insert(prior);
2212 for (auto it = nData.vtx.rbegin(); it != nData.vtx.rend(); it++)
2214 // if any existing, accepted notarization is a subset of us, don't post, we will be rejected anyhow
2215 if (priorBlocks.count(it->second.notarizationPreHash))
2222 notarizationData.insert(make_pair(it->first, &it->second));
2227 DeleteOpRetObjects(chainObjects);
2229 auto lastIt = notarizationData.find(pbn.prevNotarization);
2231 if (!stillValid || (lastIt == notarizationData.end()))
2233 //printf("Notarization not matched or invalidated by prior notarization\n");
2234 throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization not matched or invalidated by prior notarization");
2237 if (pbn.prevHeight != lastIt->second->notarizationHeight)
2239 printf("Notarization heights not matched with previous notarization\n");
2240 throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization heights not matched with previous notarization");
2243 if (pbn.prevHeight != 0 && (pbn.prevHeight + CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED > pbn.notarizationHeight))
2245 //printf("Less than minimum number of blocks between notarizations\n");
2246 throw JSONRPCError(RPC_VERIFY_REJECTED, "Less than minimum number of blocks between notarizations");
2249 // valid, spend notarization outputs, all needed finalizations, add any applicable reward output for
2250 // confirmation to confirmed notary and miner of block,
2251 // add our output address and submit
2252 CMutableTransaction mnewTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
2253 vector<CInputDescriptor> notarizationInputs;
2254 for (auto input : notarization.vin)
2256 mnewTx.vin.push_back(input);
2258 for (auto output : notarization.vout)
2260 mnewTx.vout.push_back(output);
2263 CTransaction lastTx = txesBlkHashes.back().first;
2265 int32_t confirmedInput = -1;
2266 int32_t confirmedIndex;
2267 CTxDestination payee;
2269 uint32_t notarizationIdx = -1, finalizationIdx = -1;
2270 CPBaaSNotarization dummy;
2272 notarizationInputs = AddSpendsAndFinalizations(nData, pbn.prevNotarization, mnewTx, &confirmedInput, &confirmedIndex, &payee);
2274 // if we got our inputs, add finalization
2275 if (notarizationInputs.size())
2278 CCcontract_info *cp;
2279 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2281 // use public key of cc
2282 CPubKey pk(ParseHex(CC.CChexstr));
2283 std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(pbn.currencyID, EVAL_FINALIZE_NOTARIZATION))});
2285 std::vector<CTxDestination> dests;
2286 if (chainDef.notarizationProtocol == chainDef.NOTARIZATION_NOTARY_CHAINID)
2288 dests = std::vector<CTxDestination>({CIdentityID(chainDef.GetID())});
2292 dests = std::vector<CTxDestination>({pk});
2295 CTransactionFinalization nf(confirmedInput);
2297 mnewTx.vout.insert(mnewTx.vout.begin() + (mnewTx.vout.size() - 1),
2298 CTxOut(CCurrencyDefinition::DEFAULT_OUTPUT_VALUE,
2299 MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf), &indexDests)));
2302 if (notarizationInputs.size() && GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, mnewTx, dummy, ¬arizationIdx, &finalizationIdx))
2304 // we need to add outputs to pay the reward to the confirmed notary and block miner/staker of that notarization
2305 // the rest goes back into the notarization thread
2306 // first input should be the notarization thread
2307 CTransaction newTx(mnewTx);
2308 CTransaction confirmedTx;
2309 CPBaaSNotarization confirmedPBN;
2310 CBlockIndex *pindex = NULL;
2312 CBlock confirmedBlock;
2316 BlockMap::iterator it;
2318 LOCK2(cs_main, mempool.cs);
2320 CCoinsViewCache view(pcoinsTip);
2321 int64_t dummyInterest;
2322 valueIn = view.GetValueIn(chainActive.LastTip()->GetHeight(), &dummyInterest, newTx, chainActive.LastTip()->nTime);
2326 throw JSONRPCError(RPC_TRANSACTION_REJECTED, "unable to spend necessary transaction outputs");
2329 if (confirmedInput != -1)
2331 // get data from confirmed tx and block that contains confirmed tx
2332 confirmedTx = txesBlkHashes[confirmedIndex].first;
2333 hashBlock = txesBlkHashes[confirmedIndex].second;
2334 if ((it = mapBlockIndex.find(hashBlock)) != mapBlockIndex.end())
2336 pindex = mapBlockIndex.find(hashBlock)->second;
2339 // add all inputs that might provide notary reward and calculate notary reward based on that plus current
2340 // notarization input value divided by number of blocks left in billing period, times blocks per notarization
2343 valueIn += AddNewNotarizationRewards(chainDef, notarizationInputs, mnewTx, pindex->GetHeight());
2347 LogPrintf("submitacceptednotarization: cannot find chain %s, possible corrupted database\n", chainDef.name.c_str());
2348 printf("submitacceptednotarization: cannot find chain %s, possible corrupted database\n", chainDef.name.c_str());
2353 // recipient of notary rewards and miner to share it with
2354 // notary recipient is the one from the confirmed notarization
2355 // and miner recipient is from the block it was mined into
2356 CTxDestination notaryRecipient, minerRecipient;
2358 // get recipients of any reward output
2359 if (confirmedInput != -1)
2362 if (pindex && ReadBlockFromDisk(confirmedBlock, pindex, Params().GetConsensus()) &&
2363 (confirmedPBN = CPBaaSNotarization(confirmedTx)).IsValid() &&
2364 ExtractDestination(confirmedBlock.vtx[0].vout[0].scriptPubKey, minerRecipient, false))
2366 notaryRecipient = confirmedPBN.notaryDest;
2370 throw JSONRPCError(RPC_DATABASE_ERROR, "unable to retrieve confirmed notarization data");
2374 // minimum amount must go to main thread and finalization, then divide what is left among blocks in the billing period
2375 uint64_t blocksLeft = chainDef.billingPeriod - ((confirmedPBN.notarizationHeight - chainDef.startBlock) % chainDef.billingPeriod);
2376 CAmount valueOut = 0;
2377 CAmount notaryValueOut;
2379 if (valueIn > (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1))
2381 if (confirmedInput != -1)
2383 if (valueIn < (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1)))
2385 valueOut = valueIn - (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1);
2389 valueOut = (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (valueIn - (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1))) / blocksLeft;
2392 notaryValueOut = valueIn - ((CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1) + valueOut);
2399 if (notaryValueOut >= (PBAAS_MINNOTARIZATIONOUTPUT << 1))
2401 // pay the confirmed notary with
2402 // notarization reward for this billing period / remaining blocks in the billing period * min blocks in notarization
2403 // the finalization out has minimum, the notarization out has all the remainder
2404 // outputs we should have here:
2405 // 1) notarization out
2406 // 2) finalization out
2410 // 66% of output to notary address
2411 // 33% of output to primary address of block reward
2412 auto insertIt = mnewTx.vout.begin() + (finalizationIdx + 1);
2415 CAmount minerOutput = valueOut / 3;
2416 CAmount notaryOutput = valueOut / 3 * 2;
2417 mnewTx.vout.insert(insertIt, CTxOut(minerOutput, GetScriptForDestination(minerRecipient)));
2418 mnewTx.vout.insert(insertIt, CTxOut(notaryOutput, GetScriptForDestination(notaryRecipient)));
2423 notaryValueOut += valueOut;
2427 if ((notaryValueOut + valueOut + CCurrencyDefinition::DEFAULT_OUTPUT_VALUE) > valueIn)
2429 notaryValueOut = valueIn;
2430 printf("Not enough funds to notarize %s\n", chainDef.name.c_str());
2431 LogPrintf("Not enough funds to notarize %s\n", chainDef.name.c_str());
2435 CCcontract_info *cp;
2437 // make the output for the other chain's notarization
2438 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
2439 CPubKey pk(ParseHex(CC.CChexstr));
2440 std::vector<CTxDestination> dests;
2442 std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(pbn.currencyID, EVAL_ACCEPTEDNOTARIZATION))});
2443 if (chainDef.notarizationProtocol == chainDef.NOTARIZATION_NOTARY_CHAINID)
2445 dests = std::vector<CTxDestination>({CIdentityID(chainDef.GetID())});
2449 dests = std::vector<CTxDestination>({pk});
2452 mnewTx.vout[notarizationIdx] = CTxOut(notaryValueOut, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn), &indexDests));
2454 CTransaction ntx(mnewTx);
2456 uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
2458 // sign the transaction and submit
2459 for (int i = 0; i < ntx.vin.size(); i++)
2462 SignatureData sigdata;
2464 const CScript *pScriptPubKey;
2466 if (i < notarizationInputs.size())
2468 pScriptPubKey = ¬arizationInputs[i].scriptPubKey;
2469 value = notarizationInputs[i].nValue;
2471 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, value, SIGHASH_ALL), *pScriptPubKey, sigdata, consensusBranchId);
2475 fprintf(stderr,"submitacceptednotarization: failure to sign accepted notarization\n");
2476 throw JSONRPCError(RPC_VERIFY_ERROR, "Failed to sign notarizaton for " + chainDef.name);
2478 UpdateTransaction(mnewTx, i, sigdata);
2483 // add to mempool and submit transaction
2484 CTransaction tx(mnewTx);
2486 CValidationState state;
2487 bool fMissingInputs;
2490 LOCK2(cs_main, mempool.cs);
2491 accepted = AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs);
2494 if (state.GetRejectReason() != "")
2496 printf("Cannot enter notarization into mempool for chain %s, %s\n", chainDef.name.c_str(), state.GetRejectReason().c_str());
2498 if (state.IsInvalid()) {
2499 throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
2501 if (fMissingInputs) {
2502 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
2504 throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
2509 RelayTransaction(tx);
2512 return newTx.GetHash().GetHex();
2515 throw JSONRPCError(RPC_VERIFY_REJECTED, "Failed to get notarizaton data for chainID: " + pbn.currencyID.GetHex());
2518 UniValue getcrossnotarization(const UniValue& params, bool fHelp)
2520 if (fHelp || params.size() < 2 || params.size() > 3)
2522 throw runtime_error(
2523 "getcrossnotarization \"systemid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'\n"
2524 "\nCreates and returns a cross notarization of this chain back to the system id caller,\n"
2525 "assuming that a prior notarization for that system is found.\n"
2528 "1. \"systemid\" (string, required) the currency system id to search for notarizations on\n"
2529 "2. \"txidlist\" (stringarray, optional) list of transaction ids to check in preferred order, first found is returned\n"
2530 "3. \"accepted\" (bool, optional) accepted, not earned notarizations, default: true if on\n"
2531 " VRSC or VRSCTEST, false otherwise\n"
2535 " \"crosstxid\" : \"xxxx\", (hexstring) cross-transaction id of the notarization that matches, which is one in the arguments\n"
2536 " \"txid\" : \"xxxx\", (hexstring) transaction id of the notarization that was found\n"
2537 " \"rawtx\" : \"hexdata\", (hexstring) entire matching transaction data, serialized\n"
2538 " \"newtx\" : \"hexdata\" (hexstring) the proposed notarization transaction with an opret and opretproof\n"
2542 + HelpExampleCli("getcrossnotarization", "\"systemid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
2543 + HelpExampleRpc("getcrossnotarization", "\"systemid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
2549 UniValue ret(UniValue::VOBJ);
2551 if (IsVerusActive())
2553 ecode = EVAL_ACCEPTEDNOTARIZATION;
2557 ecode = EVAL_EARNEDNOTARIZATION;
2560 if (params[0].type() == UniValue::VSTR)
2564 chainID.SetHex(params[0].get_str());
2566 catch(const std::exception& e)
2571 if (chainID.IsNull())
2573 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid currencyid");
2576 if (params.size() > 2)
2578 if (!params[2].get_bool())
2580 ecode = EVAL_EARNEDNOTARIZATION;
2585 if (ecode == EVAL_ACCEPTEDNOTARIZATION)
2587 crosscode = EVAL_EARNEDNOTARIZATION;
2591 crosscode = EVAL_ACCEPTEDNOTARIZATION;
2594 if (params[1].type() != UniValue::VARR)
2596 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid second parameter object type: " + itostr(params[1].type()));
2599 vector<UniValue> values = params[1].getValues();
2601 for (int32_t i = 0; i < values.size(); i++)
2603 auto txid = uint256S(values[i].get_str());
2606 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter for notarization ID: " + values[i].get_str());
2611 CChainNotarizationData nData;
2613 LOCK2(cs_main, mempool.cs);
2615 CCurrencyDefinition chainDef;
2616 if (!GetCurrencyDefinition(chainID, chainDef))
2618 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain ID: " + EncodeDestination(CIdentityID(chainID)));
2621 vector<pair<CTransaction, uint256>> nTxes;
2623 // get notarization data and check all transactions
2624 if (GetNotarizationData(chainID, ecode, nData, &nTxes))
2627 CPBaaSNotarization ourLast;
2631 // loop in reverse through list, as most recent is at end
2632 for (int32_t i = nData.vtx.size() - 1; i >= 0; i--)
2634 const pair<uint256, CPBaaSNotarization> &nzp = nData.vtx[i];
2635 tx = nTxes[i].first;
2636 blkHash = nTxes[i].second;
2637 auto nit = txids.find(nzp.second.crossNotarization);
2638 if (i == 0 || !(nit == txids.end()))
2641 // we have the first matching transaction, return it
2642 ret.push_back(Pair("crosstxid", nzp.second.crossNotarization.GetHex()));
2643 ret.push_back(Pair("txid", nzp.first.GetHex()));
2644 ret.push_back(Pair("rawtx", EncodeHexTx(tx)));
2649 // now make the basic notarization for this chain that the other chain daemon can complete
2650 // after it is returned
2653 // make sure our MMR matches our tip height, etc.
2656 CPBaaSNotarization prevNotarization(tx);
2658 if(!prevNotarization.IsValid())
2660 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Invalid prior notarization");
2663 int32_t proofheight = chainActive.Height();
2664 ChainMerkleMountainView mmv(chainActive.GetMMR(), proofheight);
2665 uint256 mmrRoot = mmv.GetRoot();
2666 uint256 preHash = mmv.mmr.GetNode(proofheight).hash;
2668 // prove the last notarization txid with new MMR, which also provides its blockhash and power as part of proof
2670 CBlockIndex *pnindex = mapBlockIndex.find(blkHash)->second;
2672 if(!pnindex || !ReadBlockFromDisk(block, pnindex, Params().GetConsensus(), 0))
2674 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
2677 int32_t prevHeight = pnindex->GetHeight();
2679 // which transaction are we in this block?
2680 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2682 if (!GetAddressIndex(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZE_NOTARIZATION), 1, addressIndex, prevHeight, prevHeight))
2684 throw JSONRPCError(RPC_INTERNAL_ERROR, "Address index read error - possible corruption in address index");
2687 uint256 txHash = tx.GetHash();
2688 unsigned int txIndex;
2689 for (txIndex = 0; txIndex < addressIndex.size(); txIndex++)
2691 if (addressIndex[txIndex].first.txhash == txHash)
2697 if (txIndex == addressIndex.size())
2699 throw JSONRPCError(RPC_INTERNAL_ERROR, "Notarization not found in address index - possible corruption");
2703 // get index in the block as our transaction index for proofs
2704 txIndex = addressIndex[txIndex].first.index;
2707 CMMRProof blockProof;
2708 chainActive.GetBlockProof(mmv, blockProof, proofheight);
2710 // create and store the notarization proof of chain
2711 vector<CBaseChainObject *> chainObjects;
2714 // if bock headers are merge mined, keep header refs, not headers
2715 CBlockHeaderAndProof bhp(blockProof, chainActive[proofheight]->GetBlockHeader());
2716 CChainObject<CBlockHeaderAndProof> latestHeaderObj(CHAINOBJ_HEADER, bhp);
2718 chainObjects.push_back(&latestHeaderObj);
2719 orp.AddObject(CHAINOBJ_HEADER, bhp.BlockHash());
2721 // prove the important last notarization components in the tx.
2722 // we care about the following components:
2723 // input 0 - can be used to determine if the tx is a coinbase and is the spending thread for notarization
2724 // notarization output - the notarization state being attested to
2725 // currency definition output matching notarization output - signifies first notarization
2726 std::vector<std::pair<int16_t, int16_t>> txComponents({{(int16_t)CTransactionHeader::TX_PREVOUTSEQ, (int16_t)0}});
2727 for (int i = 0; i < tx.vout.size(); i++)
2730 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
2732 if (p.evalCode == ecode)
2734 txComponents.push_back({CTransactionHeader::TX_OUTPUT, i});
2736 else if (p.evalCode == EVAL_CURRENCY_DEFINITION &&
2739 CCurrencyDefinition definedCurrency(p.vData[0]);
2740 if (definedCurrency.IsValid() &&
2741 definedCurrency.GetID() == chainID)
2743 txComponents.push_back({CTransactionHeader::TX_OUTPUT, i});
2748 CPartialTransactionProof txProof = block.GetPartialTransactionProof(tx, txIndex, txComponents);
2750 // add the cross transaction from this chain to return
2751 CChainObject<CPartialTransactionProof> strippedTxObj(CHAINOBJ_TRANSACTION_PROOF, txProof);
2753 chainObjects.push_back(&strippedTxObj);
2754 orp.AddObject(CHAINOBJ_TRANSACTION_PROOF, tx.GetHash());
2756 // add the MMR block nodes between the last notarization and this one, containing root that combines merkle, block, and compact power hashes
2757 CPriorBlocksCommitment priorBlocks;
2758 int numPriorBlocks = proofheight - ourLast.crossHeight;
2760 if (numPriorBlocks > PBAAS_MAXPRIORBLOCKS || numPriorBlocks > (proofheight - 1))
2761 numPriorBlocks = PBAAS_MAXPRIORBLOCKS > (proofheight - 1) ? ((proofheight - 1) < 1 ? 0 : (proofheight - 1)) : PBAAS_MAXPRIORBLOCKS;
2763 // push back the merkle, block hash, and block power commitments for prior blocks to ensure no
2764 // unintended notary overlap
2765 for (int i = numPriorBlocks; i >= 0; i--)
2767 priorBlocks.priorBlocks.push_back(mmv.mmr.GetNode(proofheight - i).hash);
2770 CChainObject<CPriorBlocksCommitment> priorBlocksObj(CHAINOBJ_PRIORBLOCKS, priorBlocks);
2771 chainObjects.push_back(&priorBlocksObj);
2772 orp.AddObject(CHAINOBJ_PRIORBLOCKS, ::GetHash(priorBlocks));
2774 // get node keys and addresses
2775 vector<CNodeData> nodes;
2776 const static int MAX_NODES = 2;
2780 if (!vNodes.empty())
2782 for (int i = 0; i < vNodes.size(); i++)
2785 vNodes[i]->copyStats(stats);
2786 if (vNodes[i]->fSuccessfullyConnected && !vNodes[i]->fInbound)
2788 CIdentityID idID(vNodes[i]->hashPaymentAddress);
2789 nodes.push_back(CNodeData(vNodes[i]->addr.ToString(), EncodeDestination(CTxDestination(idID))));
2795 // reduce number to max by removing randomly
2796 while (nodes.size() > MAX_NODES)
2798 int toErase = GetRandInt(nodes.size() - 1);
2799 nodes.erase(nodes.begin() + toErase);
2802 CTxDestination notaryDest;
2803 if (!VERUS_DEFAULTID.IsNull())
2805 notaryDest = VERUS_DEFAULTID;
2807 else if (USE_EXTERNAL_PUBKEY)
2809 CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
2810 if (pubKey.IsFullyValid())
2812 notaryDest = pubKey;
2817 static bool printMsg = true;
2820 printf("No notary public key recipient has been set, so this node cannot receive rewards for notarization\n");
2821 LogPrintf("No notary public key recipient has been set, so this node cannot receive rewards for notarization\n");
2826 CBlockIndex *nzIndex = chainActive[proofheight];
2827 CCurrencyState currencyState = ConnectedChains.GetCurrencyState(proofheight);
2829 // get the current block's MMR root and proof height
2830 CPBaaSNotarization notarization = CPBaaSNotarization(CCurrencyDefinition::NOTARIZATION_AUTO,
2831 ASSETCHAINS_CHAINID,
2836 ArithToUint256(GetCompactPower(nzIndex->nNonce, nzIndex->nBits, nzIndex->nVersion)),
2839 tx.GetHash(), prevNotarization.notarizationHeight,
2843 // we now have the chain objects, all associated proofs, and notarization data, make an appropriate transaction template with opret
2844 // and return it. notarization will need to be completed, so the only thing we really need to construct on this chain is the opret
2845 CMutableTransaction newNotarization = CreateNewContextualCMutableTransaction(Params().GetConsensus(), proofheight);
2848 CCcontract_info *cp;
2850 // make the output for the other chain's notarization
2851 cp = CCinit(&CC, crosscode);
2853 std::vector<CTxDestination> dests;
2854 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, crosscode))});
2855 if (chainDef.notarizationProtocol == chainDef.NOTARIZATION_NOTARY_CHAINID)
2857 dests = std::vector<CTxDestination>({CIdentityID(chainDef.GetID())});
2861 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2863 newNotarization.vout.push_back(CTxOut(CCurrencyDefinition::DEFAULT_OUTPUT_VALUE,
2864 MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(crosscode, dests, 1, ¬arization), &indexDests)));
2866 // make the unspent finalization output
2867 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2868 if (chainDef.notarizationProtocol != chainDef.NOTARIZATION_NOTARY_CHAINID)
2870 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2872 indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZE_NOTARIZATION))});
2874 CTransactionFinalization nf;
2875 newNotarization.vout.push_back(CTxOut(DEFAULT_TRANSACTION_FEE,
2876 MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf), &indexDests)));
2878 newNotarization.vout.push_back(CTxOut(0, StoreOpRetArray(chainObjects)));
2880 CTransaction newTx(newNotarization);
2881 ret.push_back(Pair("newtx", EncodeHexTx(newTx)));
2887 UniValue paynotarizationrewards(const UniValue& params, bool fHelp)
2889 if (fHelp || (params.size() != 2 && params.size() != 3))
2891 throw runtime_error(
2892 "paynotarizationrewards \"currencyid\" \"amount\" \"billingperiod\"\n"
2893 "\nAdds some amount of funds to a specific billing period of a PBaaS chain, which will be released\n"
2894 "\nin the form of payments to notaries whose notarizations are confirmed.\n"
2901 + HelpExampleCli("paynotarizationrewards", "\"hextx\"")
2902 + HelpExampleRpc("paynotarizationrewards", "\"hextx\"")
2905 CheckPBaaSAPIsValid();
2911 throw JSONRPCError(RPC_INVALID_PARAMETER, "Paying notarization rewards requires an active wallet");
2914 chainID = GetChainIDFromParam(params[0]);
2915 if (chainID.IsNull())
2917 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or currencyid");
2920 CAmount amount = AmountFromValue(params[1]);
2921 uint32_t billingPeriod = 0;
2923 if (params.size() == 3)
2925 uint32_t billingPeriod = uni_get_int(params[2]);
2928 CServiceReward sr = CServiceReward(SERVICE_NOTARIZATION, billingPeriod);
2931 CCcontract_info *cp;
2932 cp = CCinit(&CC, EVAL_SERVICEREWARD);
2934 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2936 std::vector<CTxDestination> dests({CKeyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, SERVICE_NOTARIZATION), EVAL_SERVICEREWARD))});
2938 std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeCC1of1Vout(EVAL_SERVICEREWARD, amount, pk, dests, sr).scriptPubKey, amount}});
2941 // create the transaction with native coin as input
2942 LOCK2(cs_main, pwalletMain->cs_wallet);
2944 CReserveKey reserveKey(pwalletMain);
2949 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2951 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainID.GetHex() + ": " + failReason);
2953 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2955 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2957 return UniValue(wtx.GetHash().GetHex());
2960 UniValue listreservetransactions(const UniValue& params, bool fHelp)
2962 if (fHelp || params.size() != 1)
2964 throw runtime_error(
2965 "listreservetransactions (maxnumber) (minconfirmations)\n"
2966 "\nLists all reserve coin transactions sent to/from the current wallet.\n"
2973 + HelpExampleCli("listreservetransactions", "100 0")
2974 + HelpExampleRpc("listreservetransactions", "100 0")
2977 // lists all transactions in a wallet that are
2978 return NullUniValue;
2981 // this must be called after all initial contributions are updated in the currency definition.
2982 CCoinbaseCurrencyState GetInitialCurrencyState(const CCurrencyDefinition &chainDef)
2984 bool isFractional = chainDef.IsFractional();
2985 CCurrencyState cState;
2987 // calculate contributions and conversions
2988 const std::vector<CAmount> reserveFees(chainDef.currencies.size());
2989 std::vector<int64_t> conversions = chainDef.conversions;
2991 CAmount nativeFees = 0;
2994 cState = CCurrencyState(chainDef.currencies,
2996 chainDef.contributions.size() ? chainDef.contributions : std::vector<int64_t>(chainDef.currencies.size(), 0),
2997 chainDef.initialFractionalSupply,
2999 chainDef.initialFractionalSupply,
3000 CCurrencyState::FLAG_VALID + CCurrencyState::FLAG_FRACTIONAL);
3001 cState.UpdateWithEmission(chainDef.GetTotalPreallocation());
3002 conversions = cState.PricesInReserve();
3006 cState.currencies = chainDef.currencies;
3007 cState.reserves = conversions;
3008 CAmount PreconvertedNative = cState.ReserveToNative(CCurrencyValueMap(chainDef.currencies, chainDef.preconverted));
3009 cState = CCurrencyState(chainDef.currencies,
3010 std::vector<int32_t>(0),
3015 CCurrencyState::FLAG_VALID);
3016 cState.UpdateWithEmission(chainDef.GetTotalPreallocation());
3019 CCoinbaseCurrencyState retVal(cState,
3022 chainDef.preconverted,
3023 std::vector<int64_t>(chainDef.currencies.size()),
3024 std::vector<int64_t>(chainDef.currencies.size()),
3032 UniValue reserveexchange(const UniValue& params, bool fHelp)
3034 if (fHelp || params.size() != 1)
3036 throw runtime_error(
3037 "reserveexchange '[{\"toreserve\": 1, \"recipient\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'\n"
3038 "\nThis sends a Verus output as a JSON object or lists of Verus outputs as a list of objects to an address on the same or another chain.\n"
3039 "\nFunds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
3043 " \"toreserve\" : \"bool\", (bool, optional) if present, conversion is to the underlying reserve (Verus), if false, from Verus\n"
3044 " \"recipient\" : \"Rxxx\", (string, required) recipient of converted funds or funds that failed to convert\n"
3045 " \"amount\" : n, (int64, required) amount of source coins that will be converted, depending on the toreserve flag, the rest is change\n"
3046 " \"limit\" : n, (int64, optional) price in reserve limit, below which for buys and above which for sells, execution will occur\n"
3047 " \"validbefore\" : n, (int, optional) block before which this can execute as a conversion, otherwise, it executes as a send with normal network fee\n"
3048 " \"subtractfee\" : \"bool\", (bool, optional) if true, reduce amount to destination by the fee amount, otherwise, add from inputs to cover fee"
3052 " \"txid\" : \"transactionid\" (string) The transaction id.\n"
3055 + HelpExampleCli("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
3056 + HelpExampleRpc("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
3060 CheckPBaaSAPIsValid();
3061 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Not yet implemented. Use sendcurrency in this release.");
3064 UniValue sendcurrency(const UniValue& params, bool fHelp)
3066 if (fHelp || params.size() < 2 || params.size() > 3)
3068 throw runtime_error(
3069 "sendcurrency \"fromaddress\" '[{\"address\":... ,\"amount\":...},...]' (returntx)\n"
3070 "\nThis sends one or many Verus outputs to one or many addresses on the same or another chain.\n"
3071 "Funds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
3072 "If \"fromaddress\" is specified, all funds will be taken from that address, otherwise funds may come\n"
3073 "from any source set of UTXOs controlled by the wallet.\n"
3076 "1. \"fromaddress\" (string, required) The VerusID or address to send the funds from. \"*\" means all addresses\n"
3077 "2. \"outputs\" (array, required) An array of json objects representing currencies, amounts, and destinations to send.\n"
3079 " \"currency\": \"name\" (string, required) Name of the source currency to send in this output, defaults to native of chain\n"
3080 " \"amount\":amount (numeric, required) The numeric amount of currency, denominated in source currency\n"
3081 " \"convertto\":\"name\", (string, optional) Valid currency to convert to, either a reserve of a native source, or fractional of reserve\n"
3082 " \"address\":\"dest\" (string, required) The address and optionally chain/system after the \"@\" as a system specific destination\n"
3083 " \"refundto\":\"dest\" (string, optional) For pre-conversions, this is where refunds will go, defaults to fromaddress\n"
3084 " \"memo\":memo (string, optional) If destination is a zaddr (not supported on testnet), a string message (not hexadecimal) to include.\n"
3085 " \"preconvert\":\"false\", (bool, optional) auto-convert to PBaaS currency at market price, this only works if the order is mined before block start of the chain\n"
3086 " \"subtractfee\":\"bool\", (bool, optional) if true, output must be of native or convertible reserve currency, and output will be reduced by share of fee\n"
3088 "3. \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed and returned\n"
3091 " \"txid\" : \"transactionid\" (string) The transaction id if (returntx) is false\n"
3092 " \"hextx\" : \"hex\" (string) The hexadecimal, serialized transaction if (returntx) is true\n"
3095 + HelpExampleCli("sendcurrency", "\"*\" '[{\"currency\":\"btc\",\"address\":\"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\" ,\"amount\":500.0},...]'")
3096 + HelpExampleRpc("sendcurrency", "\"bob@\" '[{\"currency\":\"btc\", \"address\":\"alice@quad\", \"amount\":500.0},...]'")
3100 std::string sourceAddress = uni_get_str(params[0]);
3101 CTxDestination sourceDest;
3103 if (!(sourceAddress == "*" || (sourceDest = DecodeDestination(sourceAddress)).which() != COptCCParams::ADDRTYPE_INVALID))
3105 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. First parameter must be source address/identity or \"*\". See help.");
3108 if (!params[1].isArray() || !params[1].size())
3110 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. Second parameter must be array of outputs. See help.");
3113 bool returnTx = false;
3114 if (params.size() > 2)
3116 returnTx = uni_get_bool(params[2]);
3119 bool isVerusActive = IsVerusActive();
3120 CCurrencyDefinition &thisChain = ConnectedChains.ThisChain();
3121 uint160 thisChainID = thisChain.GetID();
3122 bool toFractional = false;
3124 std::vector<CRecipient> outputs;
3126 const UniValue &uniOutputs = params[1];
3128 uint32_t height = chainActive.Height();
3132 for (int i = 0; i < uniOutputs.size(); i++)
3134 auto currencyStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "currency")));
3135 CAmount sourceAmount = AmountFromValue(find_value(uniOutputs[i], "amount"));
3136 auto convertToStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "convertto")));
3137 auto destStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "address")));
3138 auto refundToStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "refundto")));
3139 auto memoStr = uni_get_str(find_value(uniOutputs[i], "memo"));
3140 bool preConvert = uni_get_bool(find_value(uniOutputs[i], "preconvert"));
3141 bool subtractFee = uni_get_bool(find_value(uniOutputs[i], "subtractfee"));
3142 bool mintNew = uni_get_bool(find_value(uniOutputs[i], "mintnew"));
3144 if (currencyStr.size() ||
3145 convertToStr.size() ||
3146 refundToStr.size() ||
3151 CheckPBaaSAPIsValid();
3154 LOCK2(cs_main, mempool.cs);
3156 CCurrencyDefinition sourceCurrencyDef;
3157 uint160 sourceCurrencyID;
3158 if (currencyStr != "")
3160 sourceCurrencyID = ValidateCurrencyName(currencyStr, &sourceCurrencyDef);
3161 if (sourceCurrencyID.IsNull())
3163 throw JSONRPCError(RPC_INVALID_PARAMETER, "If source currency is specified, it must be valid.");
3168 sourceCurrencyDef = thisChain;
3169 sourceCurrencyID = sourceCurrencyDef.GetID();
3170 currencyStr = thisChain.name;
3173 CCurrencyDefinition convertToCurrencyDef;
3174 uint160 convertToCurrencyID;
3175 if (convertToStr != "")
3177 convertToCurrencyID = ValidateCurrencyName(convertToStr, &convertToCurrencyDef);
3178 if (convertToCurrencyID.IsNull())
3180 throw JSONRPCError(RPC_INVALID_PARAMETER, "If currency conversion is requested, destination currency must be valid.");
3184 std::string systemDestStr;
3185 uint160 destSystemID = thisChainID;
3186 CCurrencyDefinition destSystemDef = thisChain;
3187 std::vector<std::string> subNames = ParseSubNames(destStr, systemDestStr, true);
3188 if (systemDestStr != "")
3190 destSystemID = ValidateCurrencyName(systemDestStr, &destSystemDef);
3191 if (destSystemID.IsNull() || destSystemDef.IsToken() || destSystemDef.systemID != destSystemDef.GetID())
3193 throw JSONRPCError(RPC_INVALID_PARAMETER, "If destination system is specified, destination system or chain must be valid.");
3198 (!(sourceCurrencyDef.IsToken() &&
3199 GetDestinationID(sourceDest) == sourceCurrencyID &&
3200 sourceCurrencyDef.proofProtocol == sourceCurrencyDef.PROOF_CHAINID &&
3201 destSystemID == thisChainID &&
3203 convertToCurrencyID.IsNull())))
3205 // attempt to mint currency that isn't under the source ID's control
3206 throw JSONRPCError(RPC_INVALID_PARAMETER, "Only the ID of a mintable currency can mint such a currency. Minting cannot be combined with conversion.");
3209 CTxDestination destination = ValidateDestination(destStr);
3210 CTransferDestination transferDestination;
3211 if (destination.which() == COptCCParams::ADDRTYPE_INVALID)
3213 if (destSystemDef.options & destSystemDef.OPTION_GATEWAY)
3215 std::vector<unsigned char> rawDestBytes;
3216 for (int i = 0; i < subNames.size(); i++)
3220 rawDestBytes.push_back('.');
3222 rawDestBytes.insert(rawDestBytes.end(), subNames[i].begin(), subNames[i].end());
3224 if (!rawDestBytes.size())
3226 throw JSONRPCError(RPC_INVALID_PARAMETER, "Specified destination must be valid.");
3228 transferDestination = CTransferDestination(CTransferDestination::DEST_RAW, rawDestBytes);
3232 throw JSONRPCError(RPC_INVALID_PARAMETER, "Specified destination must be valid.");
3236 CTxDestination refundDestination = DecodeDestination(refundToStr);
3237 if (refundDestination.which() == COptCCParams::ADDRTYPE_ID &&
3238 GetDestinationID(refundDestination) != GetDestinationID(destination))
3240 if (!CIdentity::LookupIdentity(GetDestinationID(refundDestination)).IsValid())
3242 throw JSONRPCError(RPC_INVALID_PARAMETER, "When refunding to an ID, the ID must be valid.");
3245 else if (refundDestination.which() == COptCCParams::ADDRTYPE_INVALID)
3247 refundDestination = destination;
3250 if (memoStr.size() > 512)
3252 throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo, if specified, must be under 512 characters.");
3256 CRecipient oneOutput;
3258 // send a reserve transfer preconvert
3259 uint32_t flags = CReserveTransfer::VALID;
3262 flags |= CReserveTransfer::PRECONVERT;
3264 if (!convertToCurrencyID.IsNull())
3266 flags |= CReserveTransfer::CONVERT;
3270 flags |= CReserveTransfer::MINT_CURRENCY;
3271 convertToCurrencyID = sourceCurrencyID;
3272 convertToCurrencyDef = sourceCurrencyDef;
3275 // are we a system/chain transfer with or without conversion?
3276 if (destSystemID != thisChainID)
3279 CCcontract_info *cp;
3280 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
3281 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3285 if (convertToCurrencyID.IsNull())
3287 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot preconvert to unspecified or invalid currency.");
3289 CCurrencyValueMap validConversionCurrencies = CCurrencyValueMap(convertToCurrencyDef.currencies,
3290 std::vector<CAmount>(convertToCurrencyDef.currencies.size()));
3291 if (!validConversionCurrencies.valueMap.count(sourceCurrencyID))
3293 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ".");
3295 if (convertToCurrencyDef.startBlock <= (height + 1))
3297 throw JSONRPCError(RPC_INVALID_PARAMETER, "Too late to convert " + sourceCurrencyDef.name + " to " + convertToStr + ", as pre-launch is over.");
3300 CReserveTransfer rt = CReserveTransfer(flags,
3304 convertToCurrencyID,
3305 DestinationToTransferDestination(destination));
3306 rt.nFees = rt.CalculateTransferFee();
3308 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3309 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(destSystemID)});
3311 oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + rt.CalculateTransferFee() : 0;
3312 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3314 // only valid conversion for a cross-chain send is to the native currency of the chain
3315 else if (convertToCurrencyID == destSystemID)
3317 flags |= CReserveTransfer::CONVERT;
3318 // native currency must be the currency we are converting to, and the source must be a reserve
3319 auto reserveMap = convertToCurrencyDef.GetCurrenciesMap();
3320 if (!reserveMap.count(sourceCurrencyID))
3322 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 3");
3324 // converting from reserve to a fractional of that reserve
3325 auto dest = DestinationToTransferDestination(destination);
3326 auto fees = CReserveTransfer::CalculateTransferFee(dest);
3327 CReserveTransfer rt = CReserveTransfer(flags,
3331 convertToCurrencyID,
3334 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3335 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(destSystemID)});
3337 oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + fees : fees;
3338 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3340 else if (convertToCurrencyID.IsNull())
3342 auto dest = DestinationToTransferDestination(destination);
3343 auto fees = CReserveTransfer::CalculateTransferFee(dest);
3344 CReserveTransfer rt = CReserveTransfer(flags,
3351 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3352 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(destSystemID)});
3354 oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + fees : fees;
3355 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3358 // a currency conversion without transfer?
3359 else if (!convertToCurrencyID.IsNull())
3361 // if pre-converting to a token, it must be a non-reserve preconvert.
3362 // if we pre-convert for a new chain launch, which is required for a reserve, it will not be as a token
3363 if (convertToCurrencyDef.IsToken() && preConvert)
3365 if (convertToCurrencyDef.startBlock <= (height + 1))
3367 throw JSONRPCError(RPC_INVALID_PARAMETER, "Too late to convert " + sourceCurrencyDef.name + " to " + convertToStr + ", as pre-launch is over.");
3370 CCurrencyValueMap validConversionCurrencies = CCurrencyValueMap(convertToCurrencyDef.currencies,
3371 std::vector<CAmount>(convertToCurrencyDef.currencies.size()));
3372 if (!validConversionCurrencies.valueMap.count(sourceCurrencyID))
3374 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 1");
3378 CCcontract_info *cp;
3379 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
3380 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3382 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3383 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)),
3384 CKeyID(convertToCurrencyID)});
3386 CReserveTransfer rt = CReserveTransfer(flags,
3390 convertToCurrencyID,
3391 DestinationToTransferDestination(destination));
3392 rt.nFees = rt.CalculateTransferFee();
3393 oneOutput.nAmount = (sourceCurrencyID == thisChainID) ? sourceAmount + rt.nFees : 0;
3394 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3396 else if (!preConvert &&
3398 (toFractional = convertToCurrencyDef.IsFractional() && convertToCurrencyDef.GetCurrenciesMap().count(sourceCurrencyID)) ||
3399 (sourceCurrencyDef.IsFractional() && sourceCurrencyDef.GetCurrenciesMap().count(convertToCurrencyID))))
3401 // the following cases end up here:
3402 // 1. we are minting currency
3403 // 2. we are converting from a fractional currency to its reserve or back
3406 CCcontract_info *cp;
3407 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
3408 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3411 // we onnly allow minting of tokens right now
3412 // TODO: support centralized minting of native AND fractional currency
3413 // minting of fractional currency should emit coins without changing price by
3414 // adjusting reserve ratio
3415 if (!convertToCurrencyDef.IsToken() || convertToCurrencyDef.IsFractional())
3417 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot mint native or fractional currency " + convertToCurrencyDef.name);
3419 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
3420 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)),
3421 CKeyID(convertToCurrencyID)});
3423 CReserveTransfer rt = CReserveTransfer(flags,
3427 convertToCurrencyID,
3428 DestinationToTransferDestination(destination));
3429 rt.nFees = rt.CalculateTransferFee();
3430 oneOutput.nAmount = rt.CalculateTransferFee();
3431 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3436 flags |= CReserveTransfer::CONVERT;
3438 // determine the currency that is the fractional currency, whether that is the source
3440 CCurrencyDefinition *pFractionalCurrency = &sourceCurrencyDef;
3442 // determine the reserve currency of the destination that we are relevant to,
3443 // again, whether source or destination
3444 CCurrencyDefinition *pReserveCurrency = &convertToCurrencyDef;
3446 // is our destination currency, the conversion destination?
3449 pReserveCurrency = pFractionalCurrency;
3450 pFractionalCurrency = &convertToCurrencyDef;
3453 if (pFractionalCurrency->startBlock > height)
3455 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + " except through preconvert before the startblock has passed.");
3458 auto reserveMap = pFractionalCurrency->GetCurrenciesMap();
3459 auto reserveIndexIt = reserveMap.find(pReserveCurrency->GetID());
3460 if (reserveIndexIt == reserveMap.end())
3462 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". Must have reserve<->fractional relationship.");
3464 int reserveIndex = reserveIndexIt->second;
3466 // converting from reserve to a fractional of that reserve
3467 auto dest = DestinationToTransferDestination(destination);
3468 auto fees = CReserveTransfer::CalculateTransferFee(dest);
3470 // since this is conversion to a reserve token, which must be fungible with Verus, we will take the fee from
3471 // source currency, in an amount we calculate to be equivalent to the expected fee amount in Verus + some buffer,
3472 // based on the current price, even if that price must be first converted from reserve to token value, then
3474 CChainNotarizationData cnd;
3475 if (!GetNotarizationData(pFractionalCurrency->GetID(), EVAL_ACCEPTEDNOTARIZATION, cnd))
3477 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to get reserve currency data for " + pFractionalCurrency->name + ".");
3480 CReserveTransfer rt = CReserveTransfer(flags,
3484 convertToCurrencyID,
3487 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3488 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(pFractionalCurrency->GetID())});
3490 oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + fees : 0;
3491 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3494 // TODO: must be integrated into the functions above
3495 if (sourceCurrencyDef.IsFractional())
3497 // native currency must be the currency we are converting to, and the source must be a fractional with that as a reserve
3498 auto reserveMap = sourceCurrencyDef.GetCurrenciesMap();
3499 if (!reserveMap.count(convertToCurrencyID))
3501 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 3");
3503 // converting from reserve to a fractional of that reserve
3505 CReserveExchange re(flags, convertToCurrencyID, sourceAmount);
3506 std::vector<CTxDestination> dests = std::vector<CTxDestination>({destination});
3507 oneOutput.nAmount = 0;
3508 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveExchange>(EVAL_RESERVE_EXCHANGE, dests, 1, &re));
3516 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 4");
3519 // or a normal native or reserve output?
3522 if (sourceCurrencyID == thisChainID)
3524 oneOutput.nAmount = sourceAmount;
3525 oneOutput.scriptPubKey = GetScriptForDestination(destination);
3529 oneOutput.nAmount = 0;
3531 std::vector<CTxDestination> dests = std::vector<CTxDestination>({destination});
3532 CTokenOutput to(sourceCurrencyID, sourceAmount);
3534 oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &to));
3537 if (!oneOutput.scriptPubKey.size())
3539 throw JSONRPCError(RPC_INVALID_PARAMETER, "Failure to make currency output");
3541 oneOutput.fSubtractFeeFromAmount = subtractFee;
3542 outputs.push_back(oneOutput);
3545 catch(const std::exception& e)
3547 std::cerr << e.what() << '\n';
3548 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters.");
3551 // now figure out if we are pulling from a single address or all addresses
3552 // make, sign, and send or return the transaction
3553 CTxDestination *pSourceDest = (sourceDest.which() == COptCCParams::ADDRTYPE_INVALID) ? nullptr : &sourceDest;
3556 CReserveKey reserveKey(pwalletMain);
3558 int nChangePosRet, nChangeOutputs;
3559 std::string failReason;
3564 pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, nFeesRet, nChangePosRet, nChangeOutputs, failReason, NULL, pSourceDest, true))
3567 // now, we either return or commit the transaction
3570 // keep the change key
3571 reserveKey.KeepKey();
3572 ret = UniValue(EncodeHexTx(wtx));
3574 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
3576 UniValue jsonTx(UniValue::VOBJ);
3577 TxToUniv(wtx, uint256(), jsonTx);
3578 LogPrintf("%s\n", jsonTx.write(1,2).c_str());
3579 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit reserve transaction " + wtx.GetHash().GetHex());
3583 ret = UniValue(wtx.GetHash().GetHex());
3588 throw JSONRPCError(errorRet, failReason);
3593 bool GetLastImportIn(uint160 chainID, CTransaction &lastImportTx)
3595 // look for unspent chain transfer outputs for all chains
3596 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
3598 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
3602 if (!GetAddressUnspent(keyID, 1, unspentOutputs))
3607 for (auto output : unspentOutputs)
3609 // find the first one that is either in block 1, part of a chain definition transaction, or spends a prior
3610 // import output in input 0, which cannot be done unless the output is rooted in a valid import thread. anyone can try to send
3611 // coins to this address without being part of the chain, but they will be wasted and unspendable.
3613 if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
3615 // we actually don't care what is in the transaction here, only that it is a valid cross chain import tx
3616 // that is either the first in a chain or spends a valid cross chain import output
3617 CCrossChainImport cci(p.vData[0]);
3618 CTransaction lastTx;
3620 if (cci.IsValid() && myGetTransaction(output.first.txhash, lastTx, blkHash))
3622 if (output.second.blockHeight == 1 && lastTx.IsCoinBase())
3624 lastImportTx = lastTx;
3629 if (IsVerusActive())
3631 // if this is the Verus chain, then we need to either be part of a chain definition transaction
3632 // or spend a valid import output
3633 std::vector<CCurrencyDefinition> definedChains = CCurrencyDefinition::GetCurrencyDefinitions(lastTx);
3634 if (definedChains.size() == 1 && definedChains[0].IsValid())
3636 lastImportTx = lastTx;
3639 // if we get here, we must spend a valid import output
3640 CTransaction inputTx;
3641 uint256 inputBlkHash;
3642 if (lastTx.vin.size() && myGetTransaction(lastTx.vin[0].prevout.hash, inputTx, inputBlkHash) && CCrossChainImport(lastTx).IsValid())
3644 lastImportTx = lastTx;
3655 UniValue getlastimportin(const UniValue& params, bool fHelp)
3657 if (fHelp || params.size() != 1)
3659 throw runtime_error(
3660 "getlastimportin \"fromname\"\n"
3661 "\nThis returns the last import transaction from the chain specified and a blank transaction template to use when making new\n"
3662 "\nimport transactions. Since the typical use for this call is to make new import transactions from the other chain that will be then\n"
3663 "\nbroadcast to this chain, we include the template by default.\n"
3666 " \"fromname\" (string, required) name of the chain to get the last import transaction in from\n"
3670 " \"lastimporttransaction\": \"hex\" Hex encoded serialized import transaction\n"
3671 " \"lastconfirmednotarization\" : \"hex\" Hex encoded last confirmed notarization transaction\n"
3672 " \"importtxtemplate\": \"hex\" Hex encoded import template for new import transactions\n"
3673 " \"nativeimportavailable\": \"amount\" Total amount of native import currency available to import as native\n"
3674 " \"tokenimportavailable\": \"array\" ([{\"currencyid\":amount},..], required) tokens available to import, if controlled by this chain\n"
3678 + HelpExampleCli("getlastimportin", "jsondefinition")
3679 + HelpExampleRpc("getlastimportin", "jsondefinition")
3682 // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3683 // import transactions using the importtxtemplate as a template
3684 CheckPBaaSAPIsValid();
3686 uint160 chainID = GetChainIDFromParam(params[0]);
3688 if (chainID.IsNull())
3690 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3693 CTransaction lastImportTx, lastConfirmedTx;
3695 CChainNotarizationData cnd;
3696 std::vector<std::pair<CTransaction, uint256>> txesOut;
3699 LOCK2(cs_main, mempool.cs);
3701 if (!GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd, &txesOut))
3703 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No chain notarizations for " + uni_get_str(params[0]) + " found");
3707 UniValue ret(UniValue::VNULL);
3709 if (cnd.IsConfirmed())
3711 lastConfirmedTx = txesOut[cnd.lastConfirmed].first;
3712 if (GetLastImportIn(chainID, lastImportTx))
3714 ret = UniValue(UniValue::VOBJ);
3715 CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
3716 // find the import output
3719 for (importIdx = 0; importIdx < lastImportTx.vout.size(); importIdx++)
3721 if (lastImportTx.vout[importIdx].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
3726 if (importIdx >= lastImportTx.vout.size())
3728 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid lastImport transaction");
3731 txTemplate.vin.push_back(CTxIn(lastImportTx.GetHash(), importIdx, CScript()));
3733 // if Verus is active, our template import will gather all RESERVE_DEPOSITS that it can to spend into the
3735 if (IsVerusActive())
3737 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> reserveDeposits;
3739 LOCK2(cs_main, mempool.cs);
3741 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_DEPOSIT)), 1, reserveDeposits))
3743 for (auto deposit : reserveDeposits)
3746 if (deposit.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_RESERVE_DEPOSIT)
3748 txTemplate.vin.push_back(CTxIn(deposit.first.txhash, deposit.first.index, CScript()));
3754 CCoinsViewCache view(pcoinsTip);
3755 int64_t dummyInterest;
3756 CAmount totalImportAvailable = view.GetValueIn(cnd.vtx[cnd.lastConfirmed].second.notarizationHeight, &dummyInterest, txTemplate);
3757 CCurrencyValueMap reserveImportAvailable = view.GetReserveValueIn(cnd.vtx[cnd.lastConfirmed].second.notarizationHeight, txTemplate);
3759 ret.push_back(Pair("lastimporttransaction", EncodeHexTx(lastImportTx)));
3760 ret.push_back(Pair("lastconfirmednotarization", EncodeHexTx(lastConfirmedTx)));
3761 ret.push_back(Pair("importtxtemplate", EncodeHexTx(txTemplate)));
3762 ret.push_back(Pair("nativeimportavailable", totalImportAvailable));
3763 ret.push_back(Pair("tokenimportavailable", reserveImportAvailable.ToUniValue()));
3769 UniValue getlatestimportsout(const UniValue& params, bool fHelp)
3771 if (fHelp || params.size() != 1)
3773 throw runtime_error(
3774 "getlatestimportsout \"name\" \"lastimporttransaction\" \"importtxtemplate\"\n"
3775 "\nThis creates and returns all new imports for the specified chain that are exported from this blockchain.\n"
3779 " \"name\" : \"string\" (string, required) name of the chain to get the export transactions for\n"
3780 " \"lastimporttransaction\" : \"hex\" (hex, required) the last import transaction to follow, return list starts with import that spends this\n"
3781 " \"lastconfirmednotarization\" : \"hex\" (hex, required) the last confirmed notarization transaction\n"
3782 " \"importtxtemplate\" : \"hex\" (hex, required) template transaction to use, so we create a transaction compatible with the other chain\n"
3783 " \"nativeimportavailable\": \"amount\" (number, required) Total amount of native import currency available to import as native\n"
3784 " \"tokenimportavailable\": \"array\" ([{\"currencyid\":amount},..], required) tokens available to import, if controlled by this chain\n"
3788 "UniValue array of hex transactions"
3791 + HelpExampleCli("getlatestimportsout", "jsonargs")
3792 + HelpExampleRpc("getlatestimportsout", "jsonargs")
3796 UniValue ret(UniValue::VARR);
3798 // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3799 // import transactions using the importtxtemplate as a template
3800 CheckPBaaSAPIsValid();
3802 bool isVerusActive = IsVerusActive();
3804 uint160 chainID = GetChainIDFromParam(find_value(params[0], "name"));
3806 if (chainID.IsNull())
3811 // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3812 // import transactions using the importtxtemplate as a template
3813 CCurrencyDefinition chainDef;
3815 if (!GetCurrencyDefinition(chainID, chainDef))
3820 std::string lastImportHex = uni_get_str(find_value(params[0], "lastimporttx"));
3821 CTransaction lastImportTx;
3823 std::string templateTxHex = uni_get_str(find_value(params[0], "importtxtemplate"));
3824 CTransaction templateTx;
3826 std::string lastConfirmedTxHex = uni_get_str(find_value(params[0], "lastconfirmednotarization"));
3827 CTransaction lastConfirmedNotarization;
3829 CAmount nativeImportAvailable = uni_get_int64(find_value(params[0], "nativeimportavailable"));
3830 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
3832 std::vector<CBaseChainObject *> chainObjs;
3833 std::vector<CCurrencyDefinition> curDefs = CCurrencyDefinition::GetCurrencyDefinitions(lastImportTx);
3834 if (!(DecodeHexTx(lastImportTx, lastImportHex) &&
3835 DecodeHexTx(templateTx, templateTxHex) &&
3836 DecodeHexTx(lastConfirmedNotarization, lastConfirmedTxHex) &&
3837 CCrossChainImport(lastImportTx).IsValid() &&
3838 (curDefs.size() && curDefs[0].IsValid() ||
3839 (lastImportTx.vout.back().scriptPubKey.IsOpReturn() &&
3840 (chainObjs = RetrieveOpRetArray(lastImportTx.vout.back().scriptPubKey)).size() == 1 &&
3841 chainObjs[0]->objectType == CHAINOBJ_CROSSCHAINPROOF))))
3843 DeleteOpRetObjects(chainObjs);
3847 //UniValue txUniv(UniValue::VOBJ);
3848 //TxToUniv(lastImportTx, uint256(), txUniv);
3849 //printf("lastImportTx%s\n", txUniv.write(1,2).c_str());
3851 DeleteOpRetObjects(chainObjs);
3853 std::vector<CTransaction> newImports;
3854 if (!ConnectedChains.CreateLatestImports(chainDef, lastImportTx, templateTx, lastConfirmedNotarization, tokenImportAvailable, nativeImportAvailable, newImports))
3859 for (auto import : newImports)
3861 ret.push_back(EncodeHexTx(import));
3866 // refunds a failed launch if it is eligible and not already refunded. if it fails to refund, it returns false
3867 // and a string with the reason for failure
3868 bool RefundFailedLaunch(uint160 currencyID, CTransaction &lastImportTx, std::vector<CTransaction> &newRefunds, std::string &errorReason)
3870 int32_t defHeight = 0;
3871 CCurrencyDefinition chainDef;
3872 CTransaction chainDefTx;
3873 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
3875 LOCK2(cs_main, mempool.cs);
3877 if (!GetCurrencyDefinition(currencyID, chainDef, &defHeight) || currencyID == thisChainID)
3879 errorReason = "currency-not-found-or-ineligible";
3883 if (!GetLastImportIn(chainDef.IsToken() ? chainDef.GetID() : chainDef.systemID, lastImportTx))
3885 errorReason = "no-import-thread-found";
3889 std::vector<CBaseChainObject *> chainObjs;
3890 CPartialTransactionProof lastExportTxProof;
3892 // either a fully valid import with an export or the first import either in block 1 or chain definition
3893 // on the Verus chain
3894 std::vector<CCurrencyDefinition> curDefs = CCurrencyDefinition::GetCurrencyDefinitions(lastImportTx);
3895 if (!(lastImportTx.vout.back().scriptPubKey.IsOpReturn() &&
3896 (chainObjs = RetrieveOpRetArray(lastImportTx.vout.back().scriptPubKey)).size() >= 1 &&
3897 chainObjs[0]->objectType == CHAINOBJ_TRANSACTION_PROOF &&
3898 !(lastExportTxProof = ((CChainObject<CPartialTransactionProof> *)(chainObjs[0]))->object).txProof.proofSequence.size() < 3) &&
3899 !(chainObjs.size() == 0 && curDefs.size() && curDefs[0].IsValid()))
3901 DeleteOpRetObjects(chainObjs);
3902 errorReason = "invalid-import-thread-found";
3906 DeleteOpRetObjects(chainObjs);
3908 bool isVerusActive = IsVerusActive();
3910 uint256 lastExportHash;
3911 CTransaction lastExportTx;
3913 uint32_t blkHeight = defHeight;
3916 if (lastExportTxProof.txProof.proofSequence.size())
3918 lastExportHash = lastExportTxProof.TransactionHash();
3921 BlockMap::iterator blkMapIt;
3922 if (myGetTransaction(lastExportHash, tx, blkHash) &&
3923 (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() &&
3927 blkHeight = blkMapIt->second->GetHeight();
3932 // if we haven't processed any imports/refunds yet, setup to payout from the first export and continue
3933 CCrossChainExport ccx(lastImportTx);
3937 lastExportTx = lastImportTx;
3938 lastExportHash = lastImportTx.GetHash();
3944 LogPrintf("%s: No export thread found\n", __func__);
3945 //printf("%s: No export thread found\n", __func__);
3949 // we need to get the last import transaction and handle refunds like exports without conversion or
3950 // launch fees, so we can make incremental progress if necessary and know when there are none left
3951 int32_t nHeight = chainActive.Height();
3953 // make sure the chain is qualified for a refund
3954 CCurrencyValueMap minPreMap, preConvertedMap;
3955 CCoinbaseCurrencyState currencyState = ConnectedChains.GetCurrencyState(chainDef.GetID(), chainDef.startBlock - 1);
3956 if (!(chainDef.minPreconvert.size() &&
3957 (minPreMap = CCurrencyValueMap(chainDef.currencies, chainDef.minPreconvert)) > preConvertedMap &&
3958 chainDef.startBlock < nHeight &&
3959 (preConvertedMap = CCurrencyValueMap(chainDef.currencies, currencyState.reserveIn)) < minPreMap))
3961 errorReason = "chain-ineligible";
3966 // convert exports & conversions intended for the failed currency into imports back to this chain that spend from the import thread
3967 // all reservetransfers are refunded and charged 1/2 of an export fee
3968 uint160 chainID = chainDef.systemID;
3970 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
3972 // get all exports for the chain
3973 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
3975 CBlockIndex *pIndex;
3977 // get all export transactions that were posted to the chain, since none can be sent
3978 // TODO:PBAAS - all sends to a failed chain after start block should fail. this may not want to
3979 // refund all exports, in case we decide to reuse chain names
3980 if (GetAddressIndex(keyID, 1, addressIndex, defHeight, nHeight))
3982 // get all exports from first to last, and make sure they spend from the export thread consecutively
3984 uint256 lastHash = lastExportHash;
3986 // indexed by input hash
3987 std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
3989 // validate, order, and relate them with their inputs
3990 for (auto utxo : addressIndex)
3992 // if current tx spends lastHash, then we have our next valid transaction to create an import with
3993 CTransaction tx, inputtx;
3994 uint256 blkHash1, blkHash2;
3995 BlockMap::iterator blkIt;
3996 CCrossChainExport ccx;
3998 if (!utxo.first.spending &&
3999 !(utxo.first.txhash == lastExportHash) &&
4000 myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
4001 (ccx = CCrossChainExport(tx)).IsValid() &&
4003 (!tx.IsCoinBase() &&
4005 myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
4006 inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
4008 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
4010 validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
4014 CTransaction lastImport(lastImportTx);
4015 CCrossChainImport lastCCI;
4017 for (auto aixIt = validExports.find(lastExportHash);
4018 aixIt != validExports.end();
4019 aixIt = validExports.find(lastExportHash))
4021 // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
4022 // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
4023 // 2. Creates reserve outputs for unconverted imports
4024 // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
4025 // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
4026 // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
4027 // then signs the transaction considers it the latest import and the new export the latest export and
4028 // loops until there are no more confirmed, consecutive, valid export transactions to export
4030 // aixIt has an input from the export thread of last transaction, an optional deposit to the reserve, and an opret of all outputs + 1 fee
4031 assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
4033 int32_t lastImportPrevoutN = 0, lastExportPrevoutN = 0;
4034 lastCCI = CCrossChainImport();
4037 for (int i = 0; i < lastImport.vout.size(); i++)
4039 if (lastImport.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size() && (lastCCI = CCrossChainImport(p.vData[0])).IsValid())
4041 lastImportPrevoutN = i;
4046 int32_t ccxOutputNum = 0;
4047 CCrossChainExport ccx(aixIt->second.second, &ccxOutputNum);
4048 if (!lastCCI.IsValid() || !ccx.IsValid())
4050 LogPrintf("%s: POSSIBLE CORRUPTION bad import/export data in transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
4051 printf("%s: POSSIBLE CORRUPTION bad import/export data transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
4055 CMutableTransaction newImportTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
4056 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), lastImportPrevoutN));
4058 // if no prepared input, make one
4059 newImportTx.vout.clear();
4060 newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
4062 // we need to recalculate fees, as there will be no conversions
4064 CReserveTransactionDescriptor rtxd;
4066 std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
4068 // loop through and recalculate every transfer to charge a normal transfer fee and be a normal output
4069 // recalculate the fee output amount to be based on the new export fee calculation after canceling conversions
4070 ccx.totalFees = CCurrencyValueMap();
4072 CCurrencyValueMap feeMap;
4073 bool inFeeOutputs = false;
4075 for (auto &objPtr : exportOutputs)
4077 if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
4079 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
4081 // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
4082 if (rt.destination.type == CTransferDestination::DEST_FULLID)
4084 CIdentity(rt.destination.destination);
4085 rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
4088 // turn it into a normal transfer, which will create an unconverted output
4089 rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
4091 if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
4093 rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
4096 rt.destCurrencyID = rt.currencyID;
4098 // once we get to fees, we should have processed all non-fee outputs and be able to accurately calculate fees
4099 if (inFeeOutputs || rt.flags & CReserveTransfer::FEE_OUTPUT)
4101 // will be recalculated
4104 inFeeOutputs = true;
4105 ccx.totalFees = feeMap;
4106 //printf("total fees:\n%s\n", feeMap.ToUniValue().write(1,2).c_str());
4109 rt.nValue = ccx.CalculateExportFee().valueMap[rt.currencyID];
4110 //printf("one fee out:\n%s\n", rt.ToUniValue().write(1,2).c_str());
4114 feeMap.valueMap[rt.currencyID] += rt.nFees;
4119 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(),
4125 LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
4126 printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
4128 DeleteOpRetObjects(exportOutputs);
4133 DeleteOpRetObjects(exportOutputs);
4135 // emit a crosschain import output as summary
4137 CCcontract_info *cpcp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
4138 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
4140 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(ccx.systemID, EVAL_CROSSCHAIN_IMPORT)))});
4141 std::vector<CTxDestination> dests;
4143 if (ConnectedChains.ThisChain().proofProtocol == CCurrencyDefinition::PROOF_PBAASMMR ||
4144 ConnectedChains.ThisChain().proofProtocol == CCurrencyDefinition::PROOF_CHAINID)
4146 dests = std::vector<CTxDestination>({pk});
4150 LogPrintf("%s: ERROR - invalid proof protocol\n", __func__);
4151 printf("%s: ERROR - invalid proof protocol\n", __func__);
4155 CCrossChainImport cci = CCrossChainImport(ccx.systemID, CCurrencyValueMap(), CCurrencyValueMap());
4157 newImportTx.vout[0] = CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests));
4159 // now add unspent reserve deposit that came from this export as input to return it back to senders, less transfer fees
4160 // based on consensus rules, this should provide the necessary funds by definition
4161 uint256 depositTxId = aixIt->second.second.GetHash();
4162 for (int i = 0; i < aixIt->second.second.vout.size(); i++)
4165 if (aixIt->second.second.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_RESERVE_DEPOSIT)
4167 newImportTx.vin.push_back(CTxIn(depositTxId, i, CScript()));
4171 // add a proof of the export transaction at the notarization height
4173 if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
4175 LogPrintf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4176 printf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4180 // prove ccx input 0, export output, and opret
4181 std::vector<std::pair<int16_t, int16_t>> parts({{CTransactionHeader::TX_HEADER, (int16_t)0},
4182 {CTransactionHeader::TX_PREVOUTSEQ, (int16_t)0},
4183 {CTransactionHeader::TX_OUTPUT, ccxOutputNum},
4184 {CTransactionHeader::TX_OUTPUT, (int16_t)(aixIt->second.second.vout.size() - 1)}});
4186 // prove our transaction up to the MMR root of the last transaction
4187 CPartialTransactionProof exportProof = block.GetPartialTransactionProof(aixIt->second.second, aixIt->second.first.txindex, parts);
4188 if (!exportProof.components.size())
4190 LogPrintf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4191 printf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4194 exportProof.txProof << block.MMRProofBridge();
4196 // TODO: don't include chain MMR proof for exports from the same chain
4197 ChainMerkleMountainView(chainActive.GetMMR(), nHeight).GetProof(exportProof.txProof, aixIt->second.first.blockHeight);
4199 CChainObject<CPartialTransactionProof> exportXProof(CHAINOBJ_TRANSACTION_PROOF, exportProof);
4201 // add the opret with the transaction and proof
4202 newImportTx.vout.push_back(CTxOut(0, StoreOpRetArray(std::vector<CBaseChainObject *>({&exportXProof}))));
4204 // we now have an Import transaction for the chainID chain, it is the latest, and the export we used is now the latest as well
4205 // work our way forward
4206 newRefunds.push_back(newImportTx);
4207 lastImport = newImportTx;
4208 lastExportHash = aixIt->second.first.txhash;
4215 UniValue refundfailedlaunch(const UniValue& params, bool fHelp)
4217 if (fHelp || params.size() != 1)
4219 throw runtime_error(
4220 "refundfailedlaunch \"currencyid\"\n"
4221 "\nRefunds any funds sent to the chain if they are eligible for refund.\n"
4222 "This attempts to refund all transactions for all contributors.\n"
4225 "\"currencyid\" (iaddress or full chain name, required) the chain to refund contributions to\n"
4230 + HelpExampleCli("refundfailedlaunch", "\"currencyid\"")
4231 + HelpExampleRpc("refundfailedlaunch", "\"currencyid\"")
4234 CheckPBaaSAPIsValid();
4238 chainID = GetChainIDFromParam(params[0]);
4239 if (chainID.IsNull())
4241 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or currencyid");
4244 if (chainID == ConnectedChains.ThisChain().GetID() || chainID == ConnectedChains.NotaryChain().chainDefinition.GetID())
4246 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot refund the specified chain");
4249 CTransaction lastImportTx;
4250 std::vector<CTransaction> refundTxes;
4251 std::string failReason;
4253 if (!RefundFailedLaunch(chainID, lastImportTx, refundTxes, failReason))
4255 throw JSONRPCError(RPC_INVALID_REQUEST, failReason);
4258 uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
4260 UniValue ret(UniValue::VARR);
4262 CCoinsViewCache view(pcoinsTip);
4264 // sign and commit the transactions
4265 for (auto tx : refundTxes)
4267 LOCK2(cs_main, mempool.cs);
4269 CMutableTransaction newTx(tx);
4271 // sign the transaction and submit
4273 for (int i = 0; i < tx.vin.size(); i++)
4275 SignatureData sigdata;
4277 CScript outputScript;
4279 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
4281 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
4282 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
4286 CCoinsViewCache view(pcoinsTip);
4288 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
4290 fprintf(stderr,"refundfailedlaunch: cannot get input coins from tx: %s, output: %d\n", tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
4291 LogPrintf("refundfailedlaunch: cannot get input coins from tx: %s, output: %d\n", tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
4294 value = coins.vout[tx.vin[i].prevout.n].nValue;
4295 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
4298 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
4302 fprintf(stderr,"refundfailedlaunch: failure to sign refund transaction\n");
4303 LogPrintf("refundfailedlaunch: failure to sign refund transaction\n");
4306 UpdateTransaction(newTx, i, sigdata);
4312 // push to local node and sync with wallets
4313 CValidationState state;
4314 bool fMissingInputs;
4315 CTransaction signedTx(newTx);
4316 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
4317 if (state.IsInvalid()) {
4318 fprintf(stderr,"refundfailedlaunch: rejected by memory pool for %s\n", state.GetRejectReason().c_str());
4319 LogPrintf("refundfailedlaunch: rejected by memory pool for %s\n", state.GetRejectReason().c_str());
4321 if (fMissingInputs) {
4322 fprintf(stderr,"refundfailedlaunch: missing inputs\n");
4323 LogPrintf("refundfailedlaunch: missing inputs\n");
4327 fprintf(stderr,"refundfailedlaunch: rejected by memory pool for\n");
4328 LogPrintf("refundfailedlaunch: rejected by memory pool for\n");
4335 RelayTransaction(signedTx);
4336 ret.push_back(signedTx.GetHash().GetHex());
4343 UniValue getinitialcurrencystate(const UniValue& params, bool fHelp)
4345 if (fHelp || params.size() != 1)
4347 throw runtime_error(
4348 "getinitialcurrencystate \"name\"\n"
4349 "\nReturns the total amount of preconversions that have been confirmed on the blockchain for the specified PBaaS chain.\n"
4350 "This should be used to get information about chains that are not this chain, but are being launched by it.\n"
4353 " \"name\" (string, required) name or chain ID of the chain to get the export transactions for\n"
4359 " \"initialratio\" : n,\n"
4360 " \"initialsupply\" : n,\n"
4361 " \"emitted\" : n,\n"
4362 " \"supply\" : n,\n"
4363 " \"reserve\" : n,\n"
4364 " \"currentratio\" : n,\n"
4369 + HelpExampleCli("getinitialcurrencystate", "name")
4370 + HelpExampleRpc("getinitialcurrencystate", "name")
4373 CheckPBaaSAPIsValid();
4375 uint160 chainID = GetChainIDFromParam(params[0]);
4377 if (chainID.IsNull())
4379 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
4384 CCurrencyDefinition chainDef;
4385 int32_t definitionHeight;
4386 if (!GetCurrencyDefinition(chainID, chainDef, &definitionHeight))
4388 throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain " + params[0].get_str() + " not found");
4390 return ConnectedChains.GetCurrencyState(chainDef.GetID(), chainDef.startBlock - 1).ToUniValue();
4393 UniValue getcurrencystate(const UniValue& params, bool fHelp)
4395 if (fHelp || params.size() > 1)
4397 throw runtime_error(
4398 "getcurrencystate \"n\"\n"
4399 "\nReturns the total amount of preconversions that have been confirmed on the blockchain for the specified chain.\n"
4402 " \"n\" or \"m,n\" or \"m,n,o\" (int or string, optional) height or inclusive range with optional step at which to get the currency state\n"
4403 " If not specified, the latest currency state and height is returned\n"
4409 " \"blocktime\": n,\n"
4410 " \"currencystate\": {\n"
4412 " \"initialratio\" : n,\n"
4413 " \"initialsupply\" : n,\n"
4414 " \"emitted\" : n,\n"
4415 " \"supply\" : n,\n"
4416 " \"reserve\" : n,\n"
4417 " \"currentratio\" : n,\n"
4423 + HelpExampleCli("getcurrencystate", "name")
4424 + HelpExampleRpc("getcurrencystate", "name")
4427 CheckPBaaSAPIsValid();
4430 uint64_t startEnd[3] = {0};
4432 lStart = startEnd[1] = startEnd[0] = chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 1;
4434 if (params.size() == 1)
4436 if (uni_get_int(params[0], -1) == -1 && params[0].isStr())
4438 Split(params[0].get_str(), startEnd, startEnd[0], 3);
4442 if (startEnd[0] > startEnd[1])
4444 startEnd[0] = startEnd[1];
4447 if (startEnd[1] > lStart)
4449 startEnd[1] = lStart;
4452 if (startEnd[1] < startEnd[0])
4454 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range for currency state");
4457 if (startEnd[2] == 0)
4462 if (startEnd[2] > INT_MAX)
4464 startEnd[2] = INT_MAX;
4467 uint32_t start = startEnd[0], end = startEnd[1], step = startEnd[2];
4469 UniValue ret(UniValue::VARR);
4471 for (int i = start; i <= end; i += step)
4474 CCoinbaseCurrencyState currencyState = ConnectedChains.GetCurrencyState(i);
4475 UniValue entry(UniValue::VOBJ);
4476 entry.push_back(Pair("height", i));
4477 entry.push_back(Pair("blocktime", (uint64_t)chainActive.LastTip()->nTime));
4479 entry.push_back(Pair("currencystate", currencyState.ToUniValue()));
4480 ret.push_back(entry);
4485 UniValue getsaplingtree(const UniValue& params, bool fHelp)
4487 if (fHelp || params.size() > 1)
4489 throw runtime_error(
4490 "getsaplingtree \"n\"\n"
4491 "\nReturns the entries for a light wallet Sapling tree state.\n"
4494 " \"n\" or \"m,n\" or \"m,n,o\" (int or string, optional) height or inclusive range with optional step at which to get the Sapling tree state\n"
4495 " If not specified, the latest currency state and height is returned\n"
4499 " \"network\": \"VRSC\",\n"
4501 " \"hash\": \"hex\"\n"
4503 " \"tree\": \"hex\"\n"
4508 + HelpExampleCli("getsaplingtree", "name")
4509 + HelpExampleRpc("getsaplingtree", "name")
4514 uint64_t startEnd[3] = {0};
4516 lStart = startEnd[1] = startEnd[0] = chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 1;
4518 if (params.size() == 1)
4520 if (uni_get_int(params[0], -1) == -1 && params[0].isStr())
4522 Split(params[0].get_str(), startEnd, startEnd[0], 3);
4526 if (startEnd[0] > startEnd[1])
4528 startEnd[0] = startEnd[1];
4531 if (startEnd[1] > lStart)
4533 startEnd[1] = lStart;
4536 if (startEnd[1] < startEnd[0])
4538 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range for currency state");
4541 if (startEnd[2] == 0)
4546 if (startEnd[2] > INT_MAX)
4548 startEnd[2] = INT_MAX;
4551 uint32_t start = startEnd[0], end = startEnd[1], step = startEnd[2];
4553 UniValue ret(UniValue::VARR);
4556 CCoinsViewCache view(pcoinsTip);
4557 SaplingMerkleTree tree;
4559 for (int i = start; i <= end; i += step)
4561 CBlockIndex &blkIndex = *(chainActive[i]);
4562 if (view.GetSaplingAnchorAt(blkIndex.hashFinalSaplingRoot, tree))
4564 UniValue entry(UniValue::VOBJ);
4565 entry.push_back(Pair("network", ConnectedChains.ThisChain().name));
4566 entry.push_back(Pair("height", blkIndex.GetHeight()));
4567 entry.push_back(Pair("hash", blkIndex.GetBlockHash().GetHex()));
4568 entry.push_back(Pair("time", (uint64_t)chainActive.LastTip()->nTime));
4569 std::vector<unsigned char> treeBytes = ::AsVector(tree);
4570 entry.push_back(Pair("tree", HexBytes(treeBytes.data(), treeBytes.size())));
4571 ret.push_back(entry);
4577 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
4579 UniValue definecurrency(const UniValue& params, bool fHelp)
4581 if (fHelp || params.size() != 1)
4583 throw runtime_error(
4584 "definecurrency '{\"name\": \"BAAS\", ..., \"nodes\":[{\"networkaddress\":\"identity\"},..]}'\n"
4585 "\nThis defines a blockchain currency, either as an independent blockchain, or as a token on this blockchain. It also spends\n"
4586 "the identity after which this currency is named and sets a bit indicating that it has a currently active blockchain in its name.\n"
4587 "\nTo create a currency of any kind, the identity it is named after must be minted on the blockchain on which the currency is created.\n"
4588 "Once a currency is activated for an identity name, the same symbol may not be reused for another currency or blockchain, even\n"
4589 "if the identity is transferred, revoked or recovered, unless there is an endblock specified and the currency or blockchain has\n"
4590 "deactivated as of that end block.\n"
4591 "\nAll funds to start the currency and for initial conversion amounts must be available to spend from the identity with the same\n"
4592 "name and ID as the currency being defined.\n"
4595 " \"options\" : n, (int, optional) bits:\n"
4596 " 1 = FRACTIONAL, 2 = IDRESTRICTED, 4 = IDSTAKING, 8 = IDREFERRALS\n"
4597 " 0x10 = IDREFERRALSREQUIRED, 0x20 = TOKEN, 0x40 = CANBERESERVE\n"
4598 " \"name\" : \"xxxx\", (string, required) name of existing identity with no active or pending blockchain\n"
4599 " \"idregistrationprice\" : \"xx.xx\", (value, required) price of an identity in native currency\n"
4600 " \"idreferrallevels\" : n, (int, required) how many levels ID referrals go back in reward\n"
4602 " \"notaries\" : \"[identity,..]\", (list, optional) list of identities that are assigned as chain notaries\n"
4603 " \"minnotariesconfirm\" : n, (int, optional) unique notary signatures required to confirm an auto-notarization\n"
4604 " \"notarizationreward\" : \"xx.xx\", (value, required) default VRSC notarization reward total for first billing period\n"
4605 " \"billingperiod\" : n, (int, optional) number of blocks in each billing period\n"
4606 " \"proofprotocol\" : n, (int, optional) if 2, currency can be minted by whoever controls the ID\n"
4608 " \"startblock\" : n, (int, optional) VRSC block must be notarized into block 1 of PBaaS chain, default curheight + 100\n"
4609 " \"endblock\" : n, (int, optional) chain is considered inactive after this block height, and a new one may be started\n"
4611 " \"currencies\" : \"[\"VRSC\",..]\", (list, optional) reserve currencies backing this chain in equal amounts\n"
4612 " \"conversions\" : \"[\"xx.xx\",..]\", (list, optional) if present, must be same size as currencies. pre-launch conversion ratio overrides\n"
4613 " \"minpreconversion\" : \"[\"xx.xx\",..]\", (list, optional) must be same size as currencies. minimum in each currency to launch\n"
4614 " \"maxpreconversion\" : \"[\"xx.xx\",..]\", (list, optional) maximum in each currency allowed\n"
4616 " \"initialcontributions\" : \"[\"xx.xx\",..]\", (list, optional) initial contribution in each currency\n"
4617 " \"prelaunchdiscount\" : \"xx.xx\" (value, optional) for fractional reserve currencies less than 100%, discount on final price at launch"
4618 " \"initialsupply\" : \"xx.xx\" (value, required for fractional) supply after conversion of contributions, before preallocation\n"
4619 " \"prelaunchcarveouts\" : \"[{\"identity\":xx.xx}..]\", (list, optional) identities and % of pre-converted amounts from each reserve currency\n"
4620 " \"preallocations\" : \"[{\"identity\":xx.xx}..]\", (list, optional) list of identities and amounts from pre-allocation\n"
4622 " \"eras\" : \"objarray\", (array, optional) data specific to each era, maximum 3\n"
4624 " \"reward\" : n, (int64, optional) native initial block rewards in each period\n"
4625 " \"decay\" : n, (int64, optional) reward decay for each era\n"
4626 " \"halving\" : n, (int, optional) halving period for each era\n"
4627 " \"eraend\" : n, (int, optional) ending block of each era\n"
4629 " \"nodes\" : \"[obj, ..]\", (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
4631 " \"networkaddress\" : \"txid\", (string, optional) internet, TOR, or other supported address for node\n"
4632 " \"nodeidentity\" : \"name@\", (string, optional) rewards payment and identity\n"
4638 " \"txid\" : \"transactionid\", (string) The transaction id\n"
4639 " \"tx\" : \"json\", (json) The transaction decoded as a transaction\n"
4640 " \"hex\" : \"data\" (string) Raw data for signed transaction\n"
4644 + HelpExampleCli("definecurrency", "jsondefinition")
4645 + HelpExampleRpc("definecurrency", "jsondefinition")
4649 CheckPBaaSAPIsValid();
4650 bool isVerusActive = IsVerusActive();
4652 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
4654 if (!params[0].isObject())
4656 throw JSONRPCError(RPC_INVALID_PARAMETER, "JSON object required. see help.");
4661 throw JSONRPCError(RPC_WALLET_ERROR, "must have active wallet to define PBaaS chain");
4664 UniValue valStr(UniValue::VSTR);
4665 if (!valStr.read(params[0].write()))
4667 throw JSONRPCError(RPC_INVALID_PARAMS, "Invalid characters in blockchain definition");
4670 CCurrencyDefinition newChain(params[0]);
4672 if (!newChain.IsValid())
4674 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid currency definition. see help.");
4677 LOCK2(cs_main, pwalletMain->cs_wallet);
4679 CCurrencyDefinition checkDef;
4680 if (GetCurrencyDefinition(newChain.name, checkDef))
4682 throw JSONRPCError(RPC_INVALID_PARAMETER, newChain.name + " chain already defined. see help.");
4685 // a new blockchain can only be defined with the current chain as parent
4686 if (newChain.parent.IsNull())
4688 throw JSONRPCError(RPC_INVALID_PARAMETER, params[0].write() + " invalid chain name.");
4691 if (newChain.parent != thisChainID)
4693 // parent chain must be current chain
4694 throw JSONRPCError(RPC_INVALID_PARAMETER, "attempting to define a chain relative to a parent that is not the current chain.");
4697 for (auto &oneID : newChain.preAllocation)
4699 if (!CIdentity::LookupIdentity(CIdentityID(oneID.first)).IsValid())
4701 throw JSONRPCError(RPC_INVALID_PARAMETER, "attempting to pre-allocate currency to a non-existent ID.");
4705 uint160 newChainID = newChain.GetID();
4707 std::vector<CNodeData> vNodes;
4708 UniValue nodeArr = find_value(params[0], "nodes");
4709 if (nodeArr.isArray() && nodeArr.size())
4711 for (int i = 0; i < nodeArr.size(); i++)
4713 CNodeData nd(nodeArr[i]);
4716 vNodes.push_back(nd);
4717 if (vNodes.size() == 2)
4725 // a new currency definition must spend an ID that currently has no active currency, which sets a semaphore that "blocks"
4726 // that ID from having more than one at once. Before submitting the transaction, it must be properly signed by the primary authority.
4727 // This also has the effect of piggybacking on the ID protocol's deconfliction between mined blocks to avoid name conflicts,
4728 // as the ID can only have its bit set or unset by one transaction at any time and only as part of a transaction that changes the
4729 // the state of a potentially active currency.
4731 if (newChain.IsToken())
4733 if (newChain.rewards.size())
4735 throw JSONRPCError(RPC_INVALID_PARAMETER, "currency cannot be both a token and also specify a mining and staking rewards schedule.");
4737 // if this is a token definition, the systemID is this chain
4738 newChain.systemID = ConnectedChains.ThisChain().GetID();
4742 // it is a PBaaS chain, and it is its own system, responsible for its own communication and currency control
4743 newChain.systemID = newChainID;
4746 CIdentity launchIdentity;
4747 uint32_t height = chainActive.Height();
4748 uint32_t idHeight = 0;
4750 bool canSign = false, canSpend = false;
4752 std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
4753 if (pwalletMain->GetIdentity(newChainID, keyAndIdentity))
4755 canSign = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SIGN;
4756 canSpend = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SPEND;
4757 launchIdentity = static_cast<CIdentity>(keyAndIdentity.second);
4760 if (!(launchIdentity = CIdentity::LookupIdentity(newChainID, 0, &idHeight, &idTxIn)).IsValidUnrevoked() || launchIdentity.HasActiveCurrency())
4762 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID " + newChain.name + " not found, is revoked, or already has an active currency defined");
4765 if (launchIdentity.parent != thisChainID)
4767 throw JSONRPCError(RPC_INVALID_PARAMETER, "Currency can only be defined using an ID issued by " + VERUS_CHAINNAME);
4770 // refunding a currency after its launch is aborted, or shutting it down after the endblock has passed must be completed
4771 // to fully decommission a blockchain and clear the active blockchain bit from an ID
4773 //if (!newChain.startBlock || newChain.startBlock < (chainActive.Height() + PBAAS_MINSTARTBLOCKDELTA))
4775 // newChain.startBlock = chainActive.Height() + (PBAAS_MINSTARTBLOCKDELTA + 5); // give a little time to send the tx
4777 if (!newChain.startBlock || newChain.startBlock < (chainActive.Height() + 10))
4779 newChain.startBlock = chainActive.Height() + 15; // give a little time to send the tx
4782 if (newChain.endBlock && newChain.endBlock < (newChain.startBlock + CCurrencyDefinition::MIN_CURRENCY_LIFE))
4784 throw JSONRPCError(RPC_INVALID_PARAMS, "If endblock (" + to_string(newChain.endBlock) +
4785 ") is specified, it must be at least " + to_string(CCurrencyDefinition::MIN_CURRENCY_LIFE) +
4786 " blocks after startblock (" + to_string(newChain.startBlock) + ")\n");
4789 if (!newChain.IsToken())
4791 throw JSONRPCError(RPC_INVALID_PARAMS, "Current testnet does not support launch of independent PBaaS blockchains. Please use token currencies for now.\n");
4793 if (newChain.billingPeriod < CCurrencyDefinition::MIN_BILLING_PERIOD || (newChain.notarizationReward / newChain.billingPeriod) < CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION)
4795 throw JSONRPCError(RPC_INVALID_PARAMS, "Billing period of at least " +
4796 to_string(CCurrencyDefinition::MIN_BILLING_PERIOD) +
4797 " blocks and per-block notary rewards of >= " + to_string(CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION) +
4798 " are required to define an active currency\n");
4801 // TODO: check to see if rewards obviously lead to an unstable currency
4802 //for (int i = 0; i < newChain.rewards.size(); i++)
4806 // if we have no emission parameters, this is not a PBaaS blockchain, it is a controlled or bridged token.
4807 // controlled tokens can be centrally or algorithmically controlled.
4808 if (newChain.rewards.empty())
4810 throw JSONRPCError(RPC_INVALID_PARAMS, "A currency must either be based on a token protocol or must specify blockchain rewards, even if 0\n");
4814 if (newChain.currencies.empty() && newChain.IsFractional())
4816 throw JSONRPCError(RPC_INVALID_PARAMS, "Fractional reserve currencies must specify at least one reserve currency\n");
4819 // if this is a fractional reserve currency, ensure that all reserves are currently active
4820 // with at least as long of a life as this currency and that at least one of the currencies
4821 // is VRSC or VRSCTEST.
4822 std::vector<CCurrencyDefinition> reserveCurrencies;
4823 bool hasCoreReserve = false;
4824 if (newChain.IsFractional())
4826 if (newChain.contributions.size() != newChain.currencies.size())
4828 throw JSONRPCError(RPC_INVALID_PARAMETER, "All reserves must have non-zero initial contributions for each reserve for fractional currency " + newChain.name);
4830 for (int i = 0; i < newChain.contributions.size(); i++)
4832 if (newChain.contributions[i] <= 0)
4834 throw JSONRPCError(RPC_INVALID_PARAMETER, "All reserves must have non-zero initial contributions " + EncodeDestination(CIdentityID(newChain.currencies[i])));
4837 newChain.preconverted = newChain.contributions;
4838 for (auto ¤cy : newChain.currencies)
4840 reserveCurrencies.emplace_back();
4841 if (!GetCurrencyDefinition(currency, reserveCurrencies.back()) ||
4842 reserveCurrencies.back().startBlock >= height)
4844 throw JSONRPCError(RPC_INVALID_PARAMETER, "All reserve currencies of a fractional currency must be valid and past the start block " + EncodeDestination(CIdentityID(currency)));
4847 if (reserveCurrencies.back().endBlock && (!newChain.endBlock || reserveCurrencies.back().endBlock < newChain.endBlock))
4849 throw JSONRPCError(RPC_INVALID_PARAMETER, "Reserve currency " + EncodeDestination(CIdentityID(currency)) + " ends its life before the fractional currency's endblock");
4852 if (!reserveCurrencies.back().CanBeReserve())
4854 throw JSONRPCError(RPC_INVALID_PARAMETER, "Currency " + EncodeDestination(CIdentityID(currency)) + " may not be used as a reserve");
4857 if (currency == VERUS_CHAINID)
4859 hasCoreReserve = true;
4862 if (!hasCoreReserve)
4864 throw JSONRPCError(RPC_INVALID_PARAMETER, "Fractional currency requires a reserve of " + VERUS_CHAINNAME + " in addition to any other reserves");
4868 // now, create the outputs:
4869 // 1. Updated identity with active currency
4870 // 2. Currency definition
4871 // 3. Notarization thread
4874 // 6. Initial contribution exports
4875 // 7. Critical identity exports (chain identity, notaries, pre-allocation recipients)
4877 // ensure that the appropriate identity is an input to the transaction,
4878 // and fund the transaction
4879 std::vector<CRecipient> vOutputs;
4881 // first, we need the identity output with currency activated
4882 launchIdentity.ActivateCurrency();
4883 vOutputs.push_back({launchIdentity.IdentityUpdateOutputScript(), 0, false});
4885 // now, create the currency definition output
4887 CCcontract_info *cp;
4888 cp = CCinit(&CC, EVAL_CURRENCY_DEFINITION);
4889 CPubKey pk(ParseHex(CC.CChexstr));
4891 std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_CURRENCY_DEFINITION)),
4892 CKeyID(newChain.GetConditionID(EVAL_CURRENCY_DEFINITION))});
4893 std::vector<CTxDestination> dests({pk});
4895 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCurrencyDefinition>(EVAL_CURRENCY_DEFINITION, dests, 1, &newChain), &indexDests),
4896 CCurrencyDefinition::DEFAULT_OUTPUT_VALUE,
4899 // get initial currency state at this height
4900 CCoinbaseCurrencyState newCurrencyState = ConnectedChains.GetCurrencyState(newChain, chainActive.Height());
4902 // we need to make a notarization, notarize this information and block 0, which is the same as out block 0
4903 // our authorization will be that we are the chain definition
4904 uint256 mmvRoot, nodePreHash;
4906 auto mmr = chainActive.GetMMR();
4907 auto mmv = ChainMerkleMountainView(mmr);
4909 mmvRoot = mmv.GetRoot();
4910 nodePreHash = mmr.GetNode(0).hash;
4912 CTxDestination notaryDest;
4913 extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY;
4914 // use default ID as notary ID if there is one
4915 if (!VERUS_DEFAULTID.IsNull())
4917 notaryDest = VERUS_DEFAULTID;
4919 // use pub key if there is one
4920 else if (USE_EXTERNAL_PUBKEY)
4922 CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
4923 if (pubKey.IsFullyValid())
4925 notaryDest = pubKey.GetID();
4929 CPBaaSNotarization pbn = CPBaaSNotarization(newChain.notarizationProtocol,
4935 mmr.GetNode(0).power,
4944 // make the first chain notarization output
4945 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
4946 CTxDestination notarizationDest;
4948 if (newChain.notarizationProtocol == newChain.NOTARIZATION_AUTO || newChain.notarizationProtocol == newChain.NOTARIZATION_NOTARY_CONFIRM)
4950 notarizationDest = CPubKey(ParseHex(CC.CChexstr));
4952 else if (newChain.notarizationProtocol == newChain.NOTARIZATION_NOTARY_CHAINID)
4954 notarizationDest = CIdentityID(newChainID);
4958 throw JSONRPCError(RPC_INVALID_PARAMETER, "None or notarization protocol specified");
4961 indexDests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_ACCEPTEDNOTARIZATION))});
4962 dests = std::vector<CTxDestination>({notarizationDest});
4964 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn), &indexDests),
4965 newChain.notarizationReward,
4968 // make the finalization output
4969 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
4970 pk = CPubKey(ParseHex(CC.CChexstr));
4972 indexDests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_FINALIZE_NOTARIZATION))});
4973 dests = std::vector<CTxDestination>({pk});
4975 CTransactionFinalization nf;
4977 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf), &indexDests),
4978 CTransactionFinalization::DEFAULT_OUTPUT_VALUE,
4981 std::set<CIdentityID> idExportSet;
4983 if (!newChain.IsToken())
4985 idExportSet = std::set<CIdentityID>({newChainID});
4987 // create chain transfer exports for launch identity, notaries, and preallocation recipients
4988 for (auto ¬ary : newChain.notaries)
4990 idExportSet.insert(notary);
4992 for (auto &oneAlloc : newChain.preAllocation)
4994 idExportSet.insert(oneAlloc.first);
4997 // now, look them all up and create exports for zero value to move the IDs
4998 for (auto &oneID : idExportSet)
5000 CIdentity oneIdentity = (oneID == newChainID) ? launchIdentity : CIdentity::LookupIdentity(oneID);
5001 if (!oneIdentity.IsValid())
5003 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid specified identity: " + EncodeDestination(CIdentityID(oneID)));
5006 // emit a reserve transfer output
5007 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
5008 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
5010 // send a zero output to this ID and pass the full ID to do so
5011 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
5012 indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
5013 CKeyID(newChain.systemID)});
5015 CTransferDestination transferDest = IdentityToTransferDestination(oneIdentity);
5016 CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID,
5017 ASSETCHAINS_CHAINID,
5019 CReserveTransfer::CalculateTransferFee(transferDest),
5023 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests),
5030 // output preallocation
5031 for (auto &oneAlloc : newChain.preAllocation)
5033 // emit a reserve transfer output, which will be imported on successful launch
5034 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
5035 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
5037 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
5038 indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
5039 CKeyID(newChainID)});
5041 CTransferDestination transferDest = DestinationToTransferDestination(CIdentityID(oneAlloc.first));
5042 CReserveTransfer rt = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::PREALLOCATE,
5045 CReserveTransfer::CalculateTransferFee(transferDest),
5049 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests),
5050 CReserveTransfer::CalculateTransferFee(transferDest), false});
5054 // create import and export outputs
5055 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
5056 pk = CPubKey(ParseHex(CC.CChexstr));
5058 CTxDestination proofDest;
5060 if (newChain.proofProtocol == newChain.PROOF_PBAASMMR || newChain.proofProtocol == newChain.PROOF_CHAINID)
5062 proofDest = CPubKey(ParseHex(CC.CChexstr));
5066 throw JSONRPCError(RPC_INVALID_PARAMETER, "None or notarization protocol specified");
5069 // import thread is specific to the chain importing from
5070 dests = std::vector<CTxDestination>({proofDest});
5071 indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_IMPORT))});
5072 // if this is a token on this chain, the transfer that is output here is burned through the export
5073 // and merges with the import thread. we multiply new input times 2, to cover both the import thread output
5074 // and the reserve transfer outputs.
5075 CCrossChainImport cci(newChainID, CCurrencyValueMap(), CCurrencyValueMap());
5076 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests), 0, false});
5079 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
5080 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
5081 indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_EXPORT))});
5082 if (newChain.systemID != ASSETCHAINS_CHAINID)
5084 indexDests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(newChain.systemID, EVAL_CROSSCHAIN_EXPORT)));
5086 CCrossChainExport ccx = CCrossChainExport(newChainID, 0, CCurrencyValueMap(), CCurrencyValueMap());
5087 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests), 0, false});
5089 // make the outputs for initial contributions
5090 if (newChain.contributions.size() && newChain.contributions.size() == newChain.currencies.size())
5092 for (int i = 0; i < newChain.currencies.size(); i++)
5094 if (newChain.contributions[i] > 0)
5096 CAmount contribution = newChain.contributions[i] +
5097 CReserveTransactionDescriptor::CalculateAdditionalConversionFee(newChain.contributions[i]);
5098 CAmount fee = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
5100 CReserveTransfer rt = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::PRECONVERT,
5101 newChain.currencies[i],
5105 DestinationToTransferDestination(CIdentityID(newChainID)));
5107 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
5108 CPubKey pk(ParseHex(CC.CChexstr));
5110 indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
5111 CKeyID(newChain.GetID())});
5112 dests = std::vector<CTxDestination>({pk});
5114 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests),
5115 newChain.currencies[i] == thisChainID ? contribution + fee : 0,
5118 // TODO: send export and conversion fees to reserve deposit, as appropriate, to enable payment
5123 // create the transaction
5125 CMutableTransaction mtx(wtx);
5126 mtx.vin.push_back(idTxIn);
5127 *static_cast<CTransaction*>(&wtx) = mtx;
5130 LOCK2(cs_main, pwalletMain->cs_wallet);
5132 CReserveKey reserveKey(pwalletMain);
5138 CTxDestination chainDefSource = CIdentityID(newChainID);
5141 if ((errorRet = pwalletMain->CreateReserveTransaction(vOutputs,
5149 &chainDefSource)) != pwalletMain->RPC_OK)
5151 throw JSONRPCError(RPC_TRANSACTION_ERROR, newChain.name + ": " + failReason);
5155 UniValue uvret(UniValue::VOBJ);
5156 std::vector<CCurrencyDefinition> curDefs = CCurrencyDefinition::GetCurrencyDefinitions(wtx);
5157 uvret.push_back(Pair("currencydefinition", (curDefs.size() ? curDefs[0] : CCurrencyDefinition()).ToUniValue()));
5159 uvret.push_back(Pair("basenotarization", CPBaaSNotarization(wtx).ToUniValue()));
5161 uvret.push_back(Pair("txid", wtx.GetHash().GetHex()));
5163 UniValue txJSon(UniValue::VOBJ);
5164 TxToJSON(wtx, uint256(), txJSon);
5165 uvret.push_back(Pair("tx", txJSon));
5167 string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
5168 uvret.push_back(Pair("hex", strHex));
5173 UniValue registernamecommitment(const UniValue& params, bool fHelp)
5175 if (fHelp || (params.size() < 2 && params.size() > 3))
5177 throw runtime_error(
5178 "registernamecommitment \"name\" \"controladdress\" (\"referralidentity\")\n"
5179 "\nRegisters a name commitment, which is required as a source for the name to be used when registering an identity. The name commitment hides the name itself\n"
5180 "while ensuring that the miner who mines in the registration cannot front-run the name unless they have also registered a name commitment for the same name or\n"
5181 "are willing to forfeit the offer of payment for the chance that a commitment made now will allow them to register the name in the future.\n"
5184 "\"name\" (string, required) the unique name to commit to. creating a name commitment is not a registration, and if one is\n"
5185 " created for a name that exists, it may succeed, but will never be able to be used.\n"
5186 "\"controladdress\" (address, required) address that will control this commitment\n"
5187 "\"referralidentity\" (identity, optional)friendly name or identity address that is provided as a referral mechanism and to lower network cost of the ID\n"
5191 " \"txid\" : \"hexid\"\n"
5192 " \"namereservation\" :\n"
5194 " \"name\" : \"namestr\", (string) the unique name in this commitment\n"
5195 " \"salt\" : \"hexstr\", (hex) salt used to hide the commitment\n"
5196 " \"referral\": \"identityaddress\", (base58) address of the referring identity if there is one\n"
5197 " \"parent\" : \"namestr\", (string) name of the parent if not Verus or Verus test\n"
5198 " \"nameid\" : \"address\", (base58) identity address for this identity if it is created\n"
5203 + HelpExampleCli("registernamecommitment", "\"name\"")
5204 + HelpExampleRpc("registernamecommitment", "\"name\"")
5208 CheckIdentityAPIsValid();
5211 std::string name = CleanName(uni_get_str(params[0]), parent, true);
5213 // if either we have an invalid name or an implied parent, that is not valid
5214 if (name == "" || !(parent == ASSETCHAINS_CHAINID) || name != uni_get_str(params[0]))
5216 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid name for commitment. Names must not have leading or trailing spaces and must not include any of the following characters between parentheses (\\/:*?\"<>|@)");
5219 parent = ConnectedChains.ThisChain().GetID();
5221 CTxDestination dest = DecodeDestination(uni_get_str(params[1]));
5222 if (dest.which() == COptCCParams::ADDRTYPE_INVALID)
5224 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid control address for commitment");
5227 // create the transaction with native coin as input
5228 LOCK2(cs_main, pwalletMain->cs_wallet);
5230 CIdentityID referrer;
5231 if (params.size() > 2)
5233 CTxDestination referDest = DecodeDestination(uni_get_str(params[2]));
5234 if (referDest.which() != COptCCParams::ADDRTYPE_ID)
5236 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid referral identity for commitment, must be a currently registered friendly name or i-address");
5238 referrer = CIdentityID(GetDestinationID(referDest));
5239 if (!CIdentity::LookupIdentity(referrer).IsValidUnrevoked())
5241 throw JSONRPCError(RPC_INVALID_PARAMETER, "Referral identity for commitment must be a currently valid, unrevoked friendly name or i-address");
5245 CNameReservation nameRes(name, referrer, GetRandHash());
5246 CCommitmentHash commitment(nameRes.GetCommitment());
5248 CConditionObj<CCommitmentHash> condObj(EVAL_IDENTITY_COMMITMENT, std::vector<CTxDestination>({dest}), 1, &commitment);
5249 std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeMofNCCScript(condObj, &dest), CCommitmentHash::DEFAULT_OUTPUT_AMOUNT, false}});
5252 if (CIdentity::LookupIdentity(CIdentity::GetID(name, parent)).IsValid())
5254 throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity already exists.");
5257 CReserveKey reserveKey(pwalletMain);
5262 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
5264 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create commitment transaction: " + failReason);
5266 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5268 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5271 UniValue ret(UniValue::VOBJ);
5272 ret.push_back(Pair("txid", wtx.GetHash().GetHex()));
5273 ret.push_back(Pair("namereservation", nameRes.ToUniValue()));
5277 UniValue registeridentity(const UniValue& params, bool fHelp)
5279 if (fHelp || params.size() < 1 || params.size() > 2)
5281 throw runtime_error(
5282 "registeridentity \"jsonidregistration\" feeoffer\n"
5287 " \"txid\" : \"hexid\", (hex) the transaction ID of the name committment for this ID name\n"
5288 " \"namereservation\" :\n"
5290 " \"name\": \"namestr\", (string) the unique name in this commitment\n"
5291 " \"salt\": \"hexstr\", (hex) salt used to hide the commitment\n"
5292 " \"referrer\": \"identityID\", (name@ or address) must be a valid ID to use as a referrer to receive a discount\n"
5296 " \"name\": \"namestr\", (string) the unique name for this identity\n"
5300 "feeoffer (amount, optional) amount to offer miner/staker for the registration fee, if missing, uses standard price\n\n"
5303 " transactionid (hexstr)\n"
5306 + HelpExampleCli("registeridentity", "jsonidregistration")
5307 + HelpExampleRpc("registeridentity", "jsonidregistration")
5311 CheckIdentityAPIsValid();
5313 // all names have a parent of the current chain
5314 uint160 parent = ConnectedChains.ThisChain().GetID();
5316 uint256 txid = uint256S(uni_get_str(find_value(params[0], "txid")));
5317 CNameReservation reservation(find_value(params[0], "namereservation"));
5319 CIdentity newID(find_value(params[0], "identity"));
5320 if (!newID.IsValid())
5322 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity");
5326 CAmount minFeeOffer = reservation.referral.IsNull() ?
5327 ConnectedChains.ThisChain().IDFullRegistrationAmount() :
5328 ConnectedChains.ThisChain().IDReferredRegistrationAmount();
5330 if (params.size() > 1)
5332 feeOffer = AmountFromValue(params[1]);
5336 feeOffer = minFeeOffer;
5339 if (feeOffer < minFeeOffer)
5341 throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee offer must be at least " + ValueFromAmount(minFeeOffer).write());
5344 uint160 impliedParent, resParent;
5345 if (txid.IsNull() ||
5346 CleanName(reservation.name, resParent) != CleanName(newID.name, impliedParent) ||
5347 resParent != impliedParent ||
5348 impliedParent != ASSETCHAINS_CHAINID)
5350 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity description or mismatched reservation.");
5353 // lookup commitment to be sure that we can register this identity
5354 LOCK2(cs_main, pwalletMain->cs_wallet);
5357 uint32_t commitmentHeight;
5360 int commitmentOutput;
5362 // must be present and in a mined block
5365 if (!myGetTransaction(txid, txOut, hashBlk) || hashBlk.IsNull())
5367 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment transaction id");
5370 auto indexIt = mapBlockIndex.find(hashBlk);
5371 if (indexIt == mapBlockIndex.end() || indexIt->second->GetHeight() > chainActive.Height() || chainActive[indexIt->second->GetHeight()]->GetBlockHash() != indexIt->second->GetBlockHash())
5373 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment");
5376 commitmentHeight = indexIt->second->GetHeight();
5378 for (int i = 0; i < txOut.vout.size(); i++)
5381 if (txOut.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_COMMITMENT && p.vData.size())
5383 commitmentOutput = i;
5384 ::FromVector(p.vData[0], ch);
5388 if (ch.hash.IsNull())
5390 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment hash");
5394 if (ch.hash != reservation.GetCommitment().hash)
5396 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment salt or referral ID");
5399 // when creating an ID, the parent is always the current chains, and it is invalid to specify a parent
5400 if (newID.parent != parent)
5402 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid to specify alternate parent when creating an identity. Parent is determined by the current blockchain.");
5405 newID.parent = parent;
5407 CIdentity dupID = newID.LookupIdentity(newID.GetID());
5408 if (dupID.IsValid())
5410 throw JSONRPCError(RPC_VERIFY_ALREADY_IN_CHAIN, "Identity already exists.");
5413 // make sure we have a revocation and recovery authority defined
5414 CIdentity revocationAuth = (newID.revocationAuthority.IsNull() || newID.revocationAuthority == newID.GetID()) ? newID : newID.LookupIdentity(newID.revocationAuthority);
5415 CIdentity recoveryAuth = (newID.recoveryAuthority.IsNull() || newID.recoveryAuthority == newID.GetID()) ? newID : newID.LookupIdentity(newID.recoveryAuthority);
5417 if (!recoveryAuth.IsValidUnrevoked() || !revocationAuth.IsValidUnrevoked())
5419 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked recovery, or revocation identity.");
5422 // now we have a new and valid primary, revocation, and recovery identity, as well as a valid reservation
5423 newID.revocationAuthority = revocationAuth.GetID();
5424 newID.recoveryAuthority = recoveryAuth.GetID();
5426 // create the identity definition transaction & reservation key output
5427 CConditionObj<CNameReservation> condObj(EVAL_IDENTITY_RESERVATION, std::vector<CTxDestination>({CIdentityID(newID.GetID())}), 1, &reservation);
5428 CTxDestination resIndexDest = CKeyID(CCrossChainRPCData::GetConditionID(newID.GetID(), EVAL_IDENTITY_RESERVATION));
5429 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5431 // add referrals, Verus supports referrals
5432 if ((ConnectedChains.ThisChain().IDReferrals() || IsVerusActive()) && !reservation.referral.IsNull())
5434 uint32_t referralHeight;
5436 CTransaction referralIdTx;
5437 auto referralIdentity = newID.LookupIdentity(reservation.referral, commitmentHeight - 1);
5438 if (referralIdentity.IsValidUnrevoked())
5440 if (!newID.LookupFirstIdentity(reservation.referral, &referralHeight, &referralTxIn, &referralIdTx).IsValid())
5442 throw JSONRPCError(RPC_DATABASE_ERROR, "Database or blockchain data error, \"" + referralIdentity.name + "\" seems valid, but first instance is not found in index");
5445 // create outputs for this referral and up to n identities back in the referral chain
5446 outputs.push_back({referralIdentity.TransparentOutput(referralIdentity.GetID()), ConnectedChains.ThisChain().IDReferralAmount(), false});
5447 feeOffer -= ConnectedChains.ThisChain().IDReferralAmount();
5448 int afterId = referralTxIn.prevout.n + 1;
5449 for (int i = afterId; i < referralIdTx.vout.size() && (i - afterId) < (ConnectedChains.ThisChain().idReferralLevels - 1); i++)
5451 CTxDestination nextID;
5452 COptCCParams p, master;
5454 if (referralIdTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
5456 p.evalCode == EVAL_NONE &&
5457 p.vKeys.size() == 1 &&
5458 (p.vData.size() == 1 ||
5459 (p.vData.size() == 2 &&
5460 p.vKeys[0].which() == COptCCParams::ADDRTYPE_ID &&
5461 (master = COptCCParams(p.vData[1])).IsValid() &&
5462 master.evalCode == EVAL_NONE)))
5464 outputs.push_back({newID.TransparentOutput(CIdentityID(GetDestinationID(p.vKeys[0]))), ConnectedChains.ThisChain().IDReferralAmount(), false});
5465 feeOffer -= ConnectedChains.ThisChain().IDReferralAmount();
5475 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked referral identity at time of commitment");
5479 CScript reservationOutScript = MakeMofNCCScript(condObj, &resIndexDest);
5480 outputs.push_back({reservationOutScript, CNameReservation::DEFAULT_OUTPUT_AMOUNT, false});
5482 // make one dummy output, which CreateTransaction will leave as last, and we will remove to add its output to the fee
5483 // this serves to keep the change output after our real reservation output
5484 outputs.push_back({reservationOutScript, feeOffer, false});
5488 CReserveKey reserveKey(pwalletMain);
5493 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5495 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create identity transaction: " + failReason);
5498 // add commitment output
5499 CMutableTransaction mtx(wtx);
5500 mtx.vin.push_back(CTxIn(txid, commitmentOutput));
5502 // remove the fee offer output
5503 mtx.vout.pop_back();
5505 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5508 CCoinsViewCache view(pcoinsTip);
5509 for (int i = 0; i < wtx.vin.size(); i++)
5512 SignatureData sigdata;
5515 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5520 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5522 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5526 LogPrintf("%s: failure to sign identity registration tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5527 printf("%s: failure to sign identity registration tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5528 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5530 UpdateTransaction(mtx, i, sigdata);
5533 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5535 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5537 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5540 // including definitions and claims thread
5541 return UniValue(wtx.GetHash().GetHex());
5544 UniValue updateidentity(const UniValue& params, bool fHelp)
5546 if (fHelp || params.size() < 1 || params.size() > 2)
5548 throw runtime_error(
5549 "updateidentity \"jsonidentity\" (returntx)\n"
5553 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
5558 + HelpExampleCli("updateidentity", "\'{\"name\" : \"myname\"}\'")
5559 + HelpExampleRpc("updateidentity", "\'{\"name\" : \"myname\"}\'")
5563 CheckIdentityAPIsValid();
5566 bool returnTx = false;
5567 CIdentity newID(params[0]);
5569 if (!newID.IsValid())
5571 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
5574 if (params.size() > 1)
5576 returnTx = uni_get_bool(params[1], false);
5583 LOCK2(cs_main, pwalletMain->cs_wallet);
5585 if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
5587 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
5590 // create the identity definition transaction
5591 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5594 CReserveKey reserveKey(pwalletMain);
5599 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5601 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
5604 CMutableTransaction mtx(wtx);
5605 mtx.vin.push_back(idTxIn);
5606 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5609 CCoinsViewCache view(pcoinsTip);
5610 for (int i = 0; i < wtx.vin.size(); i++)
5613 SignatureData sigdata;
5616 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5621 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5623 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5627 LogPrintf("%s: failure to sign identity update tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5628 printf("%s: failure to sign identity update tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5629 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5631 UpdateTransaction(mtx, i, sigdata);
5634 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5638 return EncodeHexTx(wtx);
5640 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5642 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5644 return wtx.GetHash().GetHex();
5647 UniValue revokeidentity(const UniValue& params, bool fHelp)
5649 if (fHelp || params.size() < 1 || params.size() > 2)
5651 throw runtime_error(
5652 "revokeidentity \"nameorID\" (returntx)\n"
5656 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
5661 + HelpExampleCli("revokeidentity", "\"nameorID\"")
5662 + HelpExampleRpc("revokeidentity", "\"nameorID\"")
5666 CheckIdentityAPIsValid();
5669 bool returnTx = false;
5670 CTxDestination idDest = DecodeDestination(uni_get_str(params[0]));
5672 if (idDest.which() != COptCCParams::ADDRTYPE_ID)
5674 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
5677 CIdentityID idID(GetDestinationID(idDest));
5679 if (params.size() > 1)
5681 returnTx = uni_get_bool(params[1], false);
5688 LOCK2(cs_main, pwalletMain->cs_wallet);
5690 if (!(oldID = CIdentity::LookupIdentity(idID, 0, &idHeight, &idTxIn)).IsValid())
5692 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + EncodeDestination(idID));
5695 CIdentity newID(oldID);
5698 // create the identity definition transaction
5699 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5702 CReserveKey reserveKey(pwalletMain);
5707 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5709 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
5711 CMutableTransaction mtx(wtx);
5713 // add the spend of the last ID transaction output
5714 mtx.vin.push_back(idTxIn);
5716 // all of the reservation output is actually the fee offer, so zero the output
5717 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5720 CCoinsViewCache view(pcoinsTip);
5721 for (int i = 0; i < wtx.vin.size(); i++)
5724 SignatureData sigdata;
5727 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5732 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5734 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5738 LogPrintf("%s: failure to sign identity revocation tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5739 printf("%s: failure to sign identity revocation tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5740 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5742 UpdateTransaction(mtx, i, sigdata);
5745 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5749 return EncodeHexTx(wtx);
5751 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5753 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5755 return wtx.GetHash().GetHex();
5758 UniValue recoveridentity(const UniValue& params, bool fHelp)
5760 if (fHelp || params.size() < 1 || params.size() > 2)
5762 throw runtime_error(
5763 "recoveridentity \"jsonidentity\" (returntx)\n"
5767 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
5772 + HelpExampleCli("recoveridentity", "\'{\"name\" : \"myname\"}\'")
5773 + HelpExampleRpc("recoveridentity", "\'{\"name\" : \"myname\"}\'")
5776 CheckIdentityAPIsValid();
5779 bool returnTx = false;
5780 CIdentity newID(params[0]);
5782 if (!newID.IsValid())
5784 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
5787 if (params.size() > 1)
5789 returnTx = uni_get_bool(params[1], false);
5796 LOCK2(cs_main, pwalletMain->cs_wallet);
5798 if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
5800 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
5803 if (!oldID.IsRevoked())
5805 throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity must be revoked in order to recover : " + newID.name);
5808 newID.flags &= ~CIdentity::FLAG_REVOKED;
5810 // create the identity definition transaction
5811 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5814 CReserveKey reserveKey(pwalletMain);
5819 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5821 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
5823 CMutableTransaction mtx(wtx);
5825 // add the spend of the last ID transaction output
5826 mtx.vin.push_back(idTxIn);
5828 // all of the reservation output is actually the fee offer, so zero the output
5829 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5832 CCoinsViewCache view(pcoinsTip);
5833 for (int i = 0; i < wtx.vin.size(); i++)
5836 SignatureData sigdata;
5839 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5844 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5846 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5850 LogPrintf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5851 printf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5852 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5854 UpdateTransaction(mtx, i, sigdata);
5857 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5861 return EncodeHexTx(wtx);
5863 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5865 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5867 return wtx.GetHash().GetHex();
5870 UniValue getidentity(const UniValue& params, bool fHelp)
5872 if (fHelp || params.size() != 1)
5874 throw runtime_error(
5875 "getidentity \"name\"\n"
5883 + HelpExampleCli("getidentity", "\"name@\"")
5884 + HelpExampleRpc("getidentity", "\"name@\"")
5888 CheckIdentityAPIsValid();
5890 CTxDestination idID = DecodeDestination(uni_get_str(params[0]));
5891 if (idID.which() != COptCCParams::ADDRTYPE_ID)
5893 throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity parameter must be valid friendly name or identity address: \"" + uni_get_str(params[0]) + "\"");
5900 bool canSign = false, canSpend = false;
5904 LOCK(pwalletMain->cs_wallet);
5906 std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
5907 if (pwalletMain->GetIdentity(GetDestinationID(idID), keyAndIdentity))
5909 canSign = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SIGN;
5910 canSpend = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SPEND;
5911 identity = static_cast<CIdentity>(keyAndIdentity.second);
5917 uint160 identityID = GetDestinationID(idID);
5918 identity = CIdentity::LookupIdentity(CIdentityID(identityID), 0, &height, &idTxIn);
5920 if (!identity.IsValid() && identityID == VERUS_CHAINID)
5922 std::vector<CTxDestination> primary({CTxDestination(CKeyID(uint160()))});
5923 std::vector<std::pair<uint160, uint256>> contentmap;
5924 identity = CIdentity(CIdentity::VERSION_PBAAS,
5925 CIdentity::FLAG_ACTIVECURRENCY,
5928 ConnectedChains.ThisChain().parent,
5931 ConnectedChains.ThisChain().GetID(),
5932 ConnectedChains.ThisChain().GetID(),
5933 std::vector<libzcash::SaplingPaymentAddress>());
5936 UniValue ret(UniValue::VOBJ);
5939 if (identity.IsValid() && identity.name == CleanName(identity.name, parent, true))
5941 ret.push_back(Pair("identity", identity.ToUniValue()));
5942 ret.push_back(Pair("status", identity.IsRevoked() ? "revoked" : "active"));
5943 ret.push_back(Pair("canspendfor", canSpend));
5944 ret.push_back(Pair("cansignfor", canSign));
5945 ret.push_back(Pair("blockheight", (int64_t)height));
5946 ret.push_back(Pair("txid", idTxIn.prevout.hash.GetHex()));
5947 ret.push_back(Pair("vout", (int32_t)idTxIn.prevout.n));
5952 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Identity not found");
5956 UniValue IdentityPairToUni(const std::pair<CIdentityMapKey, CIdentityMapValue> &identity)
5958 UniValue oneID(UniValue::VOBJ);
5960 if (identity.first.IsValid() && identity.second.IsValid())
5962 oneID.push_back(Pair("identity", identity.second.ToUniValue()));
5963 oneID.push_back(Pair("blockheight", (int64_t)identity.first.blockHeight));
5964 oneID.push_back(Pair("txid", identity.second.txid.GetHex()));
5965 if (identity.second.IsRevoked())
5967 oneID.push_back(Pair("status", "revoked"));
5968 oneID.push_back(Pair("canspendfor", bool(0)));
5969 oneID.push_back(Pair("cansignfor", bool(0)));
5973 oneID.push_back(Pair("status", "active"));
5974 oneID.push_back(Pair("canspendfor", bool(identity.first.flags & identity.first.CAN_SPEND)));
5975 oneID.push_back(Pair("cansignfor", bool(identity.first.flags & identity.first.CAN_SIGN)));
5981 UniValue listidentities(const UniValue& params, bool fHelp)
5983 if (fHelp || params.size() > 3)
5985 throw runtime_error(
5986 "listidentities (includecansign) (includewatchonly)\n"
5990 " \"includecanspend\" (bool, optional, default=true) Include identities for which we can spend/authorize\n"
5991 " \"includecansign\" (bool, optional, default=true) Include identities that we can only sign for but not spend\n"
5992 " \"includewatchonly\" (bool, optional, default=false) Include identities that we can neither sign nor spend, but are either watched or are co-signers with us\n"
5997 + HelpExampleCli("listidentities", "\'{\"name\" : \"myname\"}\'")
5998 + HelpExampleRpc("listidentities", "\'{\"name\" : \"myname\"}\'")
6002 CheckIdentityAPIsValid();
6004 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine, imsigner, notmine;
6006 bool includeCanSpend = params.size() > 0 ? uni_get_bool(params[0], true) : true;
6007 bool includeCanSign = params.size() > 1 ? uni_get_bool(params[1], true) : true;
6008 bool includeWatchOnly = params.size() > 2 ? uni_get_bool(params[2], false) : false;
6010 if (pwalletMain->GetIdentities(mine, imsigner, notmine))
6012 UniValue ret(UniValue::VARR);
6013 if (includeCanSpend)
6015 for (auto identity : mine)
6018 if (identity.second.IsValid() && identity.second.name == CleanName(identity.second.name, parent, true))
6020 ret.push_back(IdentityPairToUni(identity));
6026 for (auto identity : imsigner)
6029 if (identity.second.IsValid() && identity.second.name == CleanName(identity.second.name, parent, true))
6031 ret.push_back(IdentityPairToUni(identity));
6035 if (includeWatchOnly)
6037 for (auto identity : notmine)
6040 if (identity.second.IsValid() && identity.second.name == CleanName(identity.second.name, parent, true))
6042 ret.push_back(IdentityPairToUni(identity));
6050 return NullUniValue;
6054 UniValue addmergedblock(const UniValue& params, bool fHelp)
6056 if (fHelp || params.size() != 5)
6058 throw runtime_error(
6059 "addmergedblock \"hexdata\" ( \"jsonparametersobject\" )\n"
6060 "\nAdds a fully prepared block and its header to the current merge mining queue of this daemon.\n"
6061 "Parameters determine the action to take if adding this block would exceed the available merge mining slots.\n"
6062 "Default action to take if adding would exceed available space is to replace the choice with the least ROI if this block provides more.\n"
6065 "1. \"hexdata\" (string, required) the hex-encoded, complete, unsolved block data to add. nTime, and nSolution are replaced.\n"
6066 "2. \"name\" (string, required) chain name symbol\n"
6067 "3. \"rpchost\" (string, required) host address for RPC connection\n"
6068 "4. \"rpcport\" (int, required) port address for RPC connection\n"
6069 "5. \"userpass\" (string, required) credentials for login to RPC\n"
6072 "\"deserialize-invalid\" - block could not be deserialized and was rejected as invalid\n"
6073 "\"blocksfull\" - block did not exceed others in estimated ROI, and there was no room for an additional merge mined block\n"
6076 + HelpExampleCli("addmergedblock", "\"hexdata\" \'{\"currencyid\" : \"hexstring\", \"rpchost\" : \"127.0.0.1\", \"rpcport\" : portnum}\'")
6077 + HelpExampleRpc("addmergedblock", "\"hexdata\" \'{\"currencyid\" : \"hexstring\", \"rpchost\" : \"127.0.0.1\", \"rpcport\" : portnum, \"estimatedroi\" : (verusreward/hashrate)}\'")
6081 CheckPBaaSAPIsValid();
6083 // check to see if we should replace any existing block or add a new one. if so, add this to the merge mine vector
6084 string name = params[1].get_str();
6087 throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide chain name to merge mine");
6090 string rpchost = params[2].get_str();
6091 int32_t rpcport = params[3].get_int();
6092 string rpcuserpass = params[4].get_str();
6094 if (rpchost == "" || rpcport == 0 || rpcuserpass == "")
6096 throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide valid RPC connection parameters to merge mine");
6099 uint160 chainID = CCrossChainRPCData::GetID(name);
6101 // confirm data from blockchain
6102 CRPCChainData chainData;
6103 CCurrencyDefinition chainDef;
6104 if (ConnectedChains.GetChainInfo(chainID, chainData))
6106 chainDef = chainData.chainDefinition;
6109 if (!chainDef.IsValid() && !GetCurrencyDefinition(name, chainDef))
6111 throw JSONRPCError(RPC_INVALID_PARAMETER, "chain not found");
6116 if (!DecodeHexBlk(blk, params[0].get_str()))
6117 return "deserialize-invalid";
6119 CPBaaSMergeMinedChainData blkData = CPBaaSMergeMinedChainData(chainDef, rpchost, rpcport, rpcuserpass, blk);
6121 return ConnectedChains.AddMergedBlock(blkData) ? NullUniValue : "blocksfull";
6124 UniValue submitmergedblock(const UniValue& params, bool fHelp)
6126 if (fHelp || params.size() < 1 || params.size() > 2)
6127 throw runtime_error(
6128 "submitmergedblock \"hexdata\" ( \"jsonparametersobject\" )\n"
6129 "\nAttempts to submit one more more new blocks to one or more networks.\n"
6130 "Each merged block submission may be valid for Verus and/or up to 8 merge mined chains.\n"
6131 "The submitted block consists of a valid block for this chain, along with embedded headers of up to 8 other chains.\n"
6132 "If the hash for this header meets targets of other chains that have been added with 'addmergedblock', this API will\n"
6133 "submit those blocks to the specified URL endpoints with an RPC 'submitblock' request."
6134 "\nAttempts to submit one more more new blocks to one or more networks.\n"
6135 "The 'jsonparametersobject' parameter is currently ignored.\n"
6136 "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
6139 "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n"
6140 "2. \"jsonparametersobject\" (string, optional) object of optional parameters\n"
6142 " \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n"
6145 "\"duplicate\" - node already has valid copy of block\n"
6146 "\"duplicate-invalid\" - node already has block, but it is invalid\n"
6147 "\"duplicate-inconclusive\" - node already has block but has not validated it\n"
6148 "\"inconclusive\" - node has not validated the block, it may not be on the node's current best chain\n"
6149 "\"rejected\" - block was rejected as invalid\n"
6150 "For more information on submitblock parameters and results, see: https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki#block-submission\n"
6152 + HelpExampleCli("submitblock", "\"mydata\"")
6153 + HelpExampleRpc("submitblock", "\"mydata\"")
6156 CheckPBaaSAPIsValid();
6159 //LogPrintStr("Hex block submission: " + params[0].get_str());
6160 if (!DecodeHexBlk(block, params[0].get_str()))
6161 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
6163 uint256 hash = block.GetHash();
6164 bool fBlockPresent = false;
6167 BlockMap::iterator mi = mapBlockIndex.find(hash);
6168 if (mi != mapBlockIndex.end()) {
6169 CBlockIndex *pindex = mi->second;
6172 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
6174 if (pindex->nStatus & BLOCK_FAILED_MASK)
6175 return "duplicate-invalid";
6176 // Otherwise, we might only have the header - process the block before returning
6177 fBlockPresent = true;
6182 CValidationState state;
6183 submitblock_StateCatcher sc(block.GetHash());
6184 RegisterValidationInterface(&sc);
6185 //printf("submitblock, height=%d, coinbase sequence: %d, scriptSig: %s\n", chainActive.LastTip()->GetHeight()+1, block.vtx[0].vin[0].nSequence, block.vtx[0].vin[0].scriptSig.ToString().c_str());
6186 bool fAccepted = ProcessNewBlock(1, chainActive.LastTip()->GetHeight()+1, state, Params(), NULL, &block, true, NULL);
6187 UnregisterValidationInterface(&sc);
6190 if (fAccepted && !sc.found)
6191 return "duplicate-inconclusive";
6197 return "inconclusive";
6200 return BIP22ValidationResult(state);
6203 UniValue getmergedblocktemplate(const UniValue& params, bool fHelp)
6205 if (fHelp || params.size() > 1)
6206 throw runtime_error(
6207 "getblocktemplate ( \"jsonrequestobject\" )\n"
6208 "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
6209 "It returns data needed to construct a block to work on.\n"
6210 "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
6213 "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n"
6215 " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n"
6216 " \"capabilities\":[ (array, optional) A list of strings\n"
6217 " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
6225 " \"version\" : n, (numeric) The block version\n"
6226 " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
6227 " \"finalsaplingroothash\" : \"xxxx\", (string) The hash of the final sapling root\n"
6228 " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
6230 " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
6231 " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n"
6232 " \"depends\" : [ (array) array of numbers \n"
6233 " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
6236 " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
6237 " \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n"
6238 " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
6242 // " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
6243 // " \"flags\" : \"flags\" (string) \n"
6245 // " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
6246 " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
6247 " \"target\" : \"xxxx\", (string) The hash target\n"
6248 " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
6249 " \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
6250 " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
6253 " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n"
6254 " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n"
6255 " \"sizelimit\" : n, (numeric) limit of block size\n"
6256 " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
6257 " \"bits\" : \"xxx\", (string) compressed target of next block\n"
6258 " \"height\" : n (numeric) The height of the next block\n"
6262 + HelpExampleCli("getblocktemplate", "")
6263 + HelpExampleRpc("getblocktemplate", "")
6266 CheckPBaaSAPIsValid();
6270 // Wallet or miner address is required because we support coinbasetxn
6271 if (GetArg("-mineraddress", "").empty()) {
6272 #ifdef ENABLE_WALLET
6274 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
6277 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "verusd compiled without wallet and -mineraddress not set");
6281 std::string strMode = "template";
6282 UniValue lpval = NullUniValue;
6283 // TODO: Re-enable coinbasevalue once a specification has been written
6284 bool coinbasetxn = true;
6285 if (params.size() > 0)
6287 const UniValue& oparam = params[0].get_obj();
6288 const UniValue& modeval = find_value(oparam, "mode");
6289 if (modeval.isStr())
6290 strMode = modeval.get_str();
6291 else if (modeval.isNull())
6296 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
6297 lpval = find_value(oparam, "longpollid");
6299 if (strMode == "proposal")
6301 const UniValue& dataval = find_value(oparam, "data");
6302 if (!dataval.isStr())
6303 throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
6306 if (!DecodeHexBlk(block, dataval.get_str()))
6307 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
6309 uint256 hash = block.GetHash();
6310 BlockMap::iterator mi = mapBlockIndex.find(hash);
6311 if (mi != mapBlockIndex.end()) {
6312 CBlockIndex *pindex = mi->second;
6315 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
6317 if (pindex->nStatus & BLOCK_FAILED_MASK)
6318 return "duplicate-invalid";
6320 return "duplicate-inconclusive";
6323 CBlockIndex* const pindexPrev = chainActive.LastTip();
6324 // TestBlockValidity only supports blocks built on the current Tip
6325 if (block.hashPrevBlock != pindexPrev->GetBlockHash())
6326 return "inconclusive-not-best-prevblk";
6327 CValidationState state;
6328 TestBlockValidity(state, Params(), block, pindexPrev, false, true);
6329 return BIP22ValidationResult(state);
6333 if (strMode != "template")
6334 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
6339 fvNodesEmpty = vNodes.empty();
6341 if (Params().MiningRequiresPeers() && (IsNotInSync() || fvNodesEmpty))
6343 throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Cannot get a block template while no peers are connected or chain not in sync!");
6346 //if (IsInitialBlockDownload())
6347 // throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks...");
6349 static unsigned int nTransactionsUpdatedLast;
6351 if (!lpval.isNull())
6353 // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
6354 uint256 hashWatchedChain;
6355 boost::system_time checktxtime;
6356 unsigned int nTransactionsUpdatedLastLP;
6360 // Format: <hashBestChain><nTransactionsUpdatedLast>
6361 std::string lpstr = lpval.get_str();
6363 hashWatchedChain.SetHex(lpstr.substr(0, 64));
6364 nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
6368 // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
6369 hashWatchedChain = chainActive.LastTip()->GetBlockHash();
6370 nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
6373 // Release the wallet and main lock while waiting
6374 LEAVE_CRITICAL_SECTION(cs_main);
6376 checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
6378 boost::unique_lock<boost::mutex> lock(csBestBlock);
6379 while (chainActive.LastTip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
6381 if (!cvBlockChange.timed_wait(lock, checktxtime))
6383 // Timeout: Check transactions for update
6384 if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
6386 checktxtime += boost::posix_time::seconds(10);
6390 ENTER_CRITICAL_SECTION(cs_main);
6392 if (!IsRPCRunning())
6393 throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
6394 // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
6398 static CBlockIndex* pindexPrev;
6399 static int64_t nStart;
6400 static CBlockTemplate* pblocktemplate;
6401 if (pindexPrev != chainActive.LastTip() ||
6402 (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
6404 // Clear pindexPrev so future calls make a new block, despite any failures from here on
6407 // Store the pindexBest used before CreateNewBlockWithKey, to avoid races
6408 nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
6409 CBlockIndex* pindexPrevNew = chainActive.LastTip();
6415 delete pblocktemplate;
6416 pblocktemplate = NULL;
6418 #ifdef ENABLE_WALLET
6419 CReserveKey reservekey(pwalletMain);
6420 pblocktemplate = CreateNewBlockWithKey(reservekey,chainActive.LastTip()->GetHeight()+1,KOMODO_MAXGPUCOUNT);
6422 pblocktemplate = CreateNewBlockWithKey();
6424 if (!pblocktemplate)
6425 throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory or no available utxo for staking");
6427 // Need to update only after we know CreateNewBlockWithKey succeeded
6428 pindexPrev = pindexPrevNew;
6430 CBlock* pblock = &pblocktemplate->block; // pointer for convenience
6433 UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
6434 pblock->nNonce = uint256();
6436 UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
6438 UniValue txCoinbase = NullUniValue;
6439 UniValue transactions(UniValue::VARR);
6440 map<uint256, int64_t> setTxIndex;
6442 BOOST_FOREACH (const CTransaction& tx, pblock->vtx) {
6443 uint256 txHash = tx.GetHash();
6444 setTxIndex[txHash] = i++;
6446 if (tx.IsCoinBase() && !coinbasetxn)
6449 UniValue entry(UniValue::VOBJ);
6451 entry.push_back(Pair("data", EncodeHexTx(tx)));
6453 entry.push_back(Pair("hash", txHash.GetHex()));
6455 UniValue deps(UniValue::VARR);
6456 BOOST_FOREACH (const CTxIn &in, tx.vin)
6458 if (setTxIndex.count(in.prevout.hash))
6459 deps.push_back(setTxIndex[in.prevout.hash]);
6461 entry.push_back(Pair("depends", deps));
6463 int index_in_template = i - 1;
6464 entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
6465 entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
6467 if (tx.IsCoinBase()) {
6468 // Show founders' reward if it is required
6469 //if (pblock->vtx[0].vout.size() > 1) {
6470 // Correct this if GetBlockTemplate changes the order
6471 // entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue));
6473 CAmount nReward = GetBlockSubsidy(chainActive.LastTip()->GetHeight()+1, Params().GetConsensus());
6474 entry.push_back(Pair("coinbasevalue", nReward));
6475 entry.push_back(Pair("required", true));
6478 transactions.push_back(entry);
6481 UniValue aux(UniValue::VOBJ);
6482 aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
6484 arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
6486 static UniValue aMutable(UniValue::VARR);
6487 if (aMutable.empty())
6489 aMutable.push_back("time");
6490 aMutable.push_back("transactions");
6491 aMutable.push_back("prevblock");
6494 UniValue result(UniValue::VOBJ);
6495 result.push_back(Pair("capabilities", aCaps));
6496 result.push_back(Pair("version", pblock->nVersion));
6497 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
6498 result.push_back(Pair("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex()));
6499 result.push_back(Pair("transactions", transactions));
6501 assert(txCoinbase.isObject());
6502 result.push_back(Pair("coinbasetxn", txCoinbase));
6504 result.push_back(Pair("coinbaseaux", aux));
6505 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
6507 result.push_back(Pair("longpollid", chainActive.LastTip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
6508 if ( ASSETCHAINS_STAKED != 0 )
6510 arith_uint256 POWtarget; int32_t PoSperc;
6511 POWtarget = komodo_PoWtarget(&PoSperc,hashTarget,(int32_t)(pindexPrev->GetHeight()+1),ASSETCHAINS_STAKED);
6512 result.push_back(Pair("target", POWtarget.GetHex()));
6513 result.push_back(Pair("PoSperc", (int64_t)PoSperc));
6514 result.push_back(Pair("ac_staked", (int64_t)ASSETCHAINS_STAKED));
6515 result.push_back(Pair("origtarget", hashTarget.GetHex()));
6516 } else result.push_back(Pair("target", hashTarget.GetHex()));
6517 result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
6518 result.push_back(Pair("mutable", aMutable));
6519 result.push_back(Pair("noncerange", "00000000ffffffff"));
6520 result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
6521 result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
6522 result.push_back(Pair("curtime", pblock->GetBlockTime()));
6523 result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
6524 result.push_back(Pair("height", (int64_t)(pindexPrev->GetHeight()+1)));
6526 //fprintf(stderr,"return complete template\n");
6530 static const CRPCCommand commands[] =
6531 { // category name actor (function) okSafeMode
6532 // --------------------- ------------------------ ----------------------- ----------
6533 { "identity", "registernamecommitment", ®isternamecommitment, true },
6534 { "identity", "registeridentity", ®isteridentity, true },
6535 { "identity", "updateidentity", &updateidentity, true },
6536 { "identity", "revokeidentity", &revokeidentity, true },
6537 { "identity", "recoveridentity", &recoveridentity, true },
6538 { "identity", "getidentity", &getidentity, true },
6539 { "identity", "listidentities", &listidentities, true },
6540 { "multichain", "definecurrency", &definecurrency, true },
6541 { "multichain", "listcurrencies", &listcurrencies, true },
6542 { "multichain", "getcurrency", &getcurrency, true },
6543 { "multichain", "getnotarizationdata", &getnotarizationdata, true },
6544 { "multichain", "getcrossnotarization", &getcrossnotarization, true },
6545 { "multichain", "submitacceptednotarization", &submitacceptednotarization, true },
6546 { "multichain", "paynotarizationrewards", &paynotarizationrewards, true },
6547 { "multichain", "getinitialcurrencystate", &getinitialcurrencystate, true },
6548 { "multichain", "getcurrencystate", &getcurrencystate, true },
6549 { "multichain", "getsaplingtree", &getsaplingtree, true },
6550 { "multichain", "sendcurrency", &sendcurrency, true },
6551 { "multichain", "getpendingtransfers", &getpendingtransfers, true },
6552 { "multichain", "getexports", &getexports, true },
6553 { "multichain", "getimports", &getimports, true },
6554 { "multichain", "reserveexchange", &reserveexchange, true },
6555 { "multichain", "getlatestimportsout", &getlatestimportsout, true },
6556 { "multichain", "getlastimportin", &getlastimportin, true },
6557 { "multichain", "refundfailedlaunch", &refundfailedlaunch, true },
6558 { "multichain", "refundfailedlaunch", &refundfailedlaunch, true },
6559 { "multichain", "getmergedblocktemplate", &getmergedblocktemplate, true },
6560 { "multichain", "addmergedblock", &addmergedblock, true }
6563 void RegisterPBaaSRPCCommands(CRPCTable &tableRPC)
6565 for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
6566 tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);