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 "pbaas/crosschainrpc.h"
38 #include "pbaas/identity.h"
39 #include "transaction_builder.h"
43 extern int32_t ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_LWMAPOS;
44 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
45 extern uint64_t ASSETCHAINS_STAKED;
46 extern int32_t KOMODO_MININGTHREADS;
47 extern bool VERUS_MINTBLOCKS;
48 extern uint8_t NOTARY_PUBKEY33[33];
49 extern uint160 ASSETCHAINS_CHAINID;
50 extern uint160 VERUS_CHAINID;
51 extern std::string VERUS_CHAINNAME;
52 extern int32_t USE_EXTERNAL_PUBKEY;
53 extern std::string NOTARY_PUBKEY;
55 arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
57 std::set<uint160> ClosedPBaaSChains({CCrossChainRPCData::GetChainID("RESERVEWITHPREMINE"), CCrossChainRPCData::GetChainID("FAILCHAIN"), CCrossChainRPCData::GetChainID("SLC"), CCrossChainRPCData::GetChainID("RNPM"), CCrossChainRPCData::GetChainID("RNPNMR"), CCrossChainRPCData::GetChainID("MONKINS")});
59 // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
60 static UniValue BIP22ValidationResult(const CValidationState& state)
65 std::string strRejectReason = state.GetRejectReason();
67 throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
68 if (state.IsInvalid())
70 if (strRejectReason.empty())
72 return strRejectReason;
74 // Should be impossible
78 class submitblock_StateCatcher : public CValidationInterface
83 CValidationState state;
85 submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {};
88 virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
89 if (block.GetHash() != hash)
96 bool GetChainDefinition(uint160 chainID, CPBaaSChainDefinition &chainDef, int32_t *pDefHeight)
99 if (chainID == ConnectedChains.ThisChain().GetChainID())
101 chainDef = ConnectedChains.ThisChain();
109 if (!IsVerusActive())
111 if (ConnectedChains.NotaryChain().IsValid() && (chainID == ConnectedChains.NotaryChain().chainDefinition.GetChainID()))
113 chainDef = ConnectedChains.NotaryChain().chainDefinition;
122 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
125 if (!ClosedPBaaSChains.count(chainID) && GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, addressIndex))
127 for (auto txidx : addressIndex)
131 if (GetTransaction(txidx.first.txhash, tx, blkHash))
133 chainDef = CPBaaSChainDefinition(tx);
134 if (found = chainDef.IsValid() && chainDef.GetChainID() == chainID)
136 // TODO: if we need to calculate the total contribution, do so
137 // we can get it from either a total of all exports, or the block 1 notarization if there is one
140 auto it = mapBlockIndex.find(blkHash);
141 *pDefHeight = it->second->GetHeight();
151 bool GetChainDefinition(string &name, CPBaaSChainDefinition &chainDef)
153 return GetChainDefinition(CCrossChainRPCData::GetChainID(name), chainDef);
156 void GetDefinedChains(vector<CPBaaSChainDefinition> &chains, bool includeExpired)
161 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
163 if (GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, addressIndex))
165 for (auto txidx : addressIndex)
169 if (GetTransaction(txidx.first.txhash, tx, blkHash))
171 chains.push_back(CPBaaSChainDefinition(tx));
173 UniValue valStr(UniValue::VSTR);
175 // TODO: remove/comment this if statement, as it is redundant with the one below
176 if (!valStr.read(chains.back().ToUniValue().write()))
178 printf("Invalid characters in blockchain definition: %s\n", chains.back().ToUniValue().write().c_str());
181 // remove after to use less storage
182 if (!valStr.read(chains.back().ToUniValue().write()) || ClosedPBaaSChains.count(chains.back().GetChainID()) || (!includeExpired && chains.back().endBlock != 0 && chains.back().endBlock < chainActive.Height()))
191 bool CConnectedChains::GetLastImport(const uint160 &chainID,
192 CTransaction &lastImport,
193 CTransaction &crossChainExport,
194 CCrossChainImport &ccImport,
195 CCrossChainExport &ccCrossExport)
197 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
199 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
201 LOCK2(cs_main, mempool.cs);
203 // get last import from the specified chain
204 if (!GetAddressUnspent(keyID, 1, unspentOutputs))
209 // make sure it isn't just a burned transaction to that address, drop out on first match
210 const std::pair<CAddressUnspentKey, CAddressUnspentValue> *pOutput = NULL;
212 for (const auto &output : unspentOutputs)
214 if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
225 if (!myGetTransaction(pOutput->first.txhash, lastImport, hashBlk) || !(lastImport.vout.size() && lastImport.vout.back().scriptPubKey.IsOpReturn()))
229 ccImport = CCrossChainImport(p.vData[0]);
230 auto opRetArr = RetrieveOpRetArray(lastImport.vout.back().scriptPubKey);
231 if (!opRetArr.size() || opRetArr[0]->objectType != CHAINOBJ_TRANSACTION)
233 DeleteOpRetObjects(opRetArr);
237 crossChainExport = ((CChainObject<CTransaction> *)opRetArr[0])->object;
238 DeleteOpRetObjects(opRetArr);
239 if (!(ccCrossExport = CCrossChainExport(crossChainExport)).IsValid())
247 // returns newly created import transactions to the specified chain from exports on this chain specified chain
248 // nHeight is the height for which we have an MMR that the chainID chain considers confirmed for this chain. it will
249 // accept proofs of any transactions with that MMR and height.
250 // Parameters shuold be validated before this call.
251 bool CConnectedChains::CreateLatestImports(const CPBaaSChainDefinition &chainDef,
252 const CTransaction &lastCrossChainImport,
253 const CTransaction &importTxTemplate,
254 const CTransaction &lastConfirmedNotarization,
255 CAmount totalAvailableInput,
256 std::vector<CTransaction> &newImports)
258 uint160 chainID = chainDef.GetChainID();
260 CPBaaSNotarization lastConfirmed(lastConfirmedNotarization);
261 if (!lastConfirmed.IsValid() || (chainActive.LastTip() == NULL) || lastConfirmed.notarizationHeight > chainActive.LastTip()->GetHeight())
263 LogPrintf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
264 printf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
268 CCrossChainImport lastCCI(lastCrossChainImport);
269 if (!lastCCI.IsValid())
271 LogPrintf("%s: Invalid lastCrossChainImport transaction\n", __func__);
272 printf("%s: Invalid lastCrossChainImport transaction\n", __func__);
276 std::vector<CBaseChainObject *> chainObjs;
277 // either a fully valid import with an export or the first import either in block 1 or the chain definition
278 // on the Verus chain
279 if (!(lastCrossChainImport.vout.back().scriptPubKey.IsOpReturn() &&
280 (chainObjs = RetrieveOpRetArray(lastCrossChainImport.vout.back().scriptPubKey)).size() >= 2 &&
281 chainObjs[0]->objectType == CHAINOBJ_TRANSACTION &&
282 chainObjs[1]->objectType == CHAINOBJ_CROSSCHAINPROOF) &&
283 !(chainObjs.size() == 0 && CPBaaSChainDefinition(lastCrossChainImport).IsValid()))
285 DeleteOpRetObjects(chainObjs);
286 LogPrintf("%s: Invalid last import tx\n", __func__);
287 printf("%s: Invalid last import tx\n", __func__);
291 bool isVerusActive = IsVerusActive();
293 LOCK2(cs_main, mempool.cs);
295 uint256 lastExportHash;
296 CTransaction lastExportTx;
299 uint32_t blkHeight = 0;
301 if (chainObjs.size())
303 lastExportTx = ((CChainObject<CTransaction> *)chainObjs[0])->object;
304 lastExportHash = lastExportTx.GetHash();
305 BlockMap::iterator blkMapIt;
306 DeleteOpRetObjects(chainObjs);
307 if (myGetTransaction(lastExportHash, tx, blkHash) &&
308 (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() &&
312 blkHeight = blkMapIt->second->GetHeight();
317 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
319 // we cannot get export to a chain that has shut down
320 // if the chain definition is spent, a chain is inactive
321 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, unspentOutputs) && unspentOutputs.size())
323 CPBaaSChainDefinition localChainDef;
325 for (auto txidx : unspentOutputs)
328 if (txidx.second.script.IsPayToCryptoCondition(p) &&
330 p.evalCode == EVAL_PBAASDEFINITION &&
332 (localChainDef = CPBaaSChainDefinition(p.vData[0])).IsValid() &&
333 ((isVerusActive && localChainDef.GetChainID() == chainID) ||
334 (!isVerusActive && localChainDef.GetChainID() == thisChain.GetChainID() && chainID == notaryChain.GetChainID())))
336 if (myGetTransaction(txidx.first.txhash, tx, blkHash))
338 CCrossChainExport ccx(tx);
339 if (ccx.IsValid() && (isVerusActive || tx.IsCoinBase()))
343 lastExportHash = txidx.first.txhash;
344 blkHeight = txidx.second.blockHeight;
353 LogPrintf("%s: No export thread found\n", __func__);
354 printf("%s: No export thread found\n", __func__);
358 bool importToReserveChain = !isVerusActive && chainID != thisChain.GetChainID() && chainID == notaryChain.GetChainID();
360 // which transaction are we in this block?
361 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
363 // look for new exports
364 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
368 // get all export transactions including and since this one up to the confirmed height
369 if (blkHeight <= lastConfirmed.notarizationHeight && GetAddressIndex(keyID, 1, addressIndex, blkHeight, lastConfirmed.notarizationHeight))
371 // find this export, then check the next one that spends it and use it if still valid
373 uint256 lastHash = lastExportHash;
375 // indexed by input hash
376 std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
378 // validate, order, and relate them with their inputs
379 for (auto utxo : addressIndex)
381 // if current tx spends lastHash, then we have our next valid transaction to create an import with
382 CTransaction tx, inputtx;
383 uint256 blkHash1, blkHash2;
384 BlockMap::iterator blkIt;
385 CCrossChainExport ccx;
387 if (!utxo.first.spending &&
388 !(utxo.first.txhash == lastExportHash) &&
389 myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
390 (ccx = CCrossChainExport(tx)).IsValid() &&
394 myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
395 inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
397 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
399 validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
403 CTransaction lastImport(lastCrossChainImport);
404 CMutableTransaction newImportTx(importTxTemplate);
406 for (auto aixIt = validExports.find(lastExportHash);
407 aixIt != validExports.end();
408 aixIt = validExports.find(lastExportHash))
410 // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
411 // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
412 // 2. Creates reserve outputs for unconverted imports
413 // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
414 // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
415 // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
416 // then signs the transaction considers it the latest import and the new export the latest export and
417 // loops until there are no more confirmed, consecutive, valid export transactions to export
419 // 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
420 assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
422 CCrossChainExport ccx(aixIt->second.second);
423 lastCCI = CCrossChainImport(lastImport);
424 if (!lastCCI.IsValid() || !ccx.IsValid())
426 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());
427 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());
431 // if no prepared input, make one
432 if (!newImportTx.vin.size())
434 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), 0)); // must spend output 0
436 newImportTx.vout.clear();
437 newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
439 // emit a reserve exchange output
440 // we will send using a reserve output, fee will be paid by converting from reserve
445 CAmount availableReserveFees = ccx.totalFees;
446 CAmount exportFees = ccx.CalculateExportFee();
447 CAmount importFees = ccx.CalculateImportFee();
450 CReserveTransactionDescriptor rtxd;
452 std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
454 if (!rtxd.AddReserveTransferImportOutputs(chainDef, exportOutputs, newImportTx.vout))
456 LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
457 printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
459 DeleteOpRetObjects(exportOutputs);
464 DeleteOpRetObjects(exportOutputs);
466 // emit a crosschain import output as summary
467 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
469 pk = CPubKey(ParseHex(CC.CChexstr));
471 if (rtxd.ReserveFees() != (ccx.totalFees - exportFees))
473 LogPrintf("%s: ERROR - import does not match amount, rtxd.ReserveFees()=%lu, totalImport=%lu, importFees=%lu, exportFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, rtxd.ReserveFees(), ccx.totalAmount + ccx.totalFees, importFees, exportFees, ccx.totalAmount, ccx.totalFees);
474 printf("%s: ERROR - import does not match amount, rtxd.ReserveFees()=%lu, totalImport=%lu, importFees=%lu, exportFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, rtxd.ReserveFees(), ccx.totalAmount + ccx.totalFees, importFees, exportFees, ccx.totalAmount, ccx.totalFees);
477 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_CROSSCHAIN_IMPORT)))});
478 CCrossChainImport cci = CCrossChainImport(ConnectedChains.ThisChain().GetChainID(), ccx.totalAmount + ccx.totalFees);
480 totalAvailableInput -= (importToReserveChain ? rtxd.reserveIn : rtxd.nativeIn);
481 newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, totalAvailableInput, pk, dests, cci);
483 if (totalAvailableInput < 0)
485 LogPrintf("%s: ERROR - importing more native currency than available on import thread for %s\n", __func__, chainDef.name.c_str());
486 printf("%s: ERROR - importing more native currency than available on import thread for %s\n", __func__, chainDef.name.c_str());
490 // add the opret, which is the export transaction this imports
491 std::vector<CBaseChainObject *> chainObjects;
492 CChainObject<CTransaction> exportTx(CHAINOBJ_TRANSACTION, aixIt->second.second);
493 chainObjects.push_back(&exportTx);
495 // add a proof of the export transaction at the notarization height
497 if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
499 LogPrintf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[lastConfirmed.notarizationHeight]->GetBlockHash().GetHex().c_str());
500 printf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[lastConfirmed.notarizationHeight]->GetBlockHash().GetHex().c_str());
504 CMerkleBranch exportProof(aixIt->second.first.index, block.GetMerkleBranch(aixIt->second.first.index));
505 auto mmrView = chainActive.GetMMV();
506 mmrView.resize(lastConfirmed.notarizationHeight);
508 if (mmrView.GetRoot() != lastConfirmed.mmrRoot)
510 LogPrintf("%s: notarization mmrRoot, %s, does not match the mmrRoot of the chain, %s\n", __func__, lastConfirmed.mmrRoot.GetHex().c_str(), mmrView.GetRoot().GetHex().c_str());
511 printf("%s: notarization mmrRoot, %s, does not match the mmrRoot of the chain, %s\n", __func__, lastConfirmed.mmrRoot.GetHex().c_str(), mmrView.GetRoot().GetHex().c_str());
516 printf("%s: created import %s for export %s\n", __func__, cci.ToUniValue().write().c_str(), aixIt->second.second.GetHash().GetHex().c_str());
520 chainActive[aixIt->second.first.blockHeight]->AddMerkleProofBridge(exportProof);
521 mmrView.GetProof(exportProof, aixIt->second.first.blockHeight);
523 CChainObject<CCrossChainProof> exportXProof(CHAINOBJ_CROSSCHAINPROOF, CCrossChainProof(lastConfirmedNotarization.GetHash(), exportProof));
524 chainObjects.push_back(&exportXProof);
526 // add the opret with the transaction and its proof that references the notarization with the correct MMR
527 newImportTx.vout.push_back(CTxOut(0, StoreOpRetArray(chainObjects)));
529 // 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
530 // work our way forward
531 newImports.push_back(newImportTx);
532 lastImport = newImportTx;
533 newImportTx.vin.clear();
534 newImportTx.vout.clear();
535 lastExportHash = aixIt->second.first.txhash;
541 void CheckPBaaSAPIsValid()
543 if (!chainActive.LastTip() ||
544 CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_PBAAS)
546 throw JSONRPCError(RPC_INVALID_REQUEST, "PBaaS not activated on blockchain.");
550 void CheckIdentityAPIsValid()
552 if (!chainActive.LastTip() ||
553 CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
555 throw JSONRPCError(RPC_INVALID_REQUEST, "Identity APIs not activated on blockchain.");
559 uint160 GetChainIDFromParam(const UniValue ¶m)
568 name = param.get_str();
569 if (name.size() > KOMODO_ASSETCHAIN_MAXLEN - 1)
574 // if it's a 40 byte hex number, consider it a chainID, not a name, names should not be 40 byte hex numbers
575 if (name.size() == 40)
577 chainID.SetHex(param.get_str());
580 catch(const std::exception& e)
584 if (chainID.IsNull())
586 chainID = CCrossChainRPCData::GetChainID(param.get_str());
592 UniValue getchaindefinition(const UniValue& params, bool fHelp)
594 if (fHelp || params.size() != 1)
597 "getchaindefinition \"chainname\"\n"
598 "\nReturns a complete definition for any given chain if it is registered on the blockchain. If the chain requested\n"
599 "\nis NULL, chain definition of the current chain is returned.\n"
602 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
606 " \"chaindefinition\" : {\n"
607 " \"version\" : \"n\", (int) version of this chain definition\n"
608 " \"name\" : \"string\", (string) name or symbol of the chain, same as passed\n"
609 " \"address\" : \"string\", (string) cryptocurrency address to send fee and non-converted premine\n"
610 " \"chainid\" : \"hex-string\", (string) 40 char string that represents the chain ID, calculated from the name\n"
611 " \"premine\" : \"n\", (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
612 " \"convertible\" : \"xxxx\" (bool) if this currency is a fractional reserve currency of Verus\n"
613 " \"launchfee\" : \"n\", (int) (launchfee * total converted) / 100000000 sent directly to premine address\n"
614 " \"startblock\" : \"n\", (int) block # on this chain, which must be notarized into block one of the chain\n"
615 " \"endblock\" : \"n\", (int) block # after which, this chain's useful life is considered to be over\n"
616 " \"eras\" : \"[obj, ...]\", (objarray) different chain phases of rewards and convertibility\n"
618 " \"reward\" : \"[n, ...]\", (int) reward start for each era in native coin\n"
619 " \"decay\" : \"[n, ...]\", (int) exponential or linear decay of rewards during each era\n"
620 " \"halving\" : \"[n, ...]\", (int) blocks between halvings during each era\n"
621 " \"eraend\" : \"[n, ...]\", (int) block marking the end of each era\n"
622 " \"eraoptions\" : \"[n, ...]\", (int) options for each era (reserved)\n"
624 " \"nodes\" : \"[obj, ..]\", (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
626 " \"nodeaddress\" : \"txid\", (string, optional) internet, TOR, or other supported address for node\n"
627 " \"paymentaddress\" : \"n\", (int, optional) rewards payment address\n"
630 " \"bestnotarization\" : {\n"
632 " \"besttxid\" : \"txid\"\n"
634 " \"confirmednotarization\" : {\n"
636 " \"confirmedtxid\" : \"txid\"\n"
640 + HelpExampleCli("getchaindefinition", "\"chainname\"")
641 + HelpExampleRpc("getchaindefinition", "\"chainname\"")
645 CheckPBaaSAPIsValid();
647 UniValue ret(UniValue::VOBJ);
649 uint160 chainID = GetChainIDFromParam(params[0]);
651 if (chainID.IsNull())
653 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
656 CPBaaSChainDefinition chainDef;
660 if (GetChainDefinition(chainID, chainDef))
662 CChainNotarizationData cnd;
663 GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
664 ret = chainDef.ToUniValue();
666 int32_t confirmedHeight = -1, bestHeight = -1;
667 confirmedHeight = cnd.vtx.size() && cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed].second.notarizationHeight : -1;
668 bestHeight = cnd.vtx.size() && cnd.bestChain != -1 ? cnd.vtx[cnd.forks[cnd.bestChain].back()].second.notarizationHeight : -1;
670 ret.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
671 if (confirmedHeight != -1)
673 ret.push_back(Pair("lastconfirmedtxid", cnd.vtx[cnd.lastConfirmed].first.GetHex().c_str()));
674 ret.push_back(Pair("lastconfirmedcurrencystate", cnd.vtx[cnd.lastConfirmed].second.currencyState.ToUniValue()));
676 ret.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
677 if (bestHeight != -1)
679 ret.push_back(Pair("besttxid", cnd.vtx[cnd.forks[cnd.bestChain].back()].first.GetHex().c_str()));
680 ret.push_back(Pair("bestcurrencystate", cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.ToUniValue()));
690 UniValue getpendingchaintransfers(const UniValue& params, bool fHelp)
692 if (fHelp || params.size() > 1)
695 "getpendingchaintransfers \"chainname\"\n"
696 "\nReturns all pending transfers for a particular chain that have not yet been aggregated into an export\n"
699 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
706 + HelpExampleCli("getpendingchaintransfers", "\"chainname\"")
707 + HelpExampleRpc("getpendingchaintransfers", "\"chainname\"")
711 CheckPBaaSAPIsValid();
713 uint160 chainID = GetChainIDFromParam(params[0]);
715 if (chainID.IsNull())
717 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
720 CPBaaSChainDefinition chainDef;
725 if ((IsVerusActive() && GetChainDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetChainID() == chainID)
727 // look for new exports
728 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> inputDescriptors;
730 if (GetUnspentChainTransfers(inputDescriptors, chainID))
732 UniValue ret(UniValue::VARR);
734 for (auto &desc : inputDescriptors)
737 if (desc.second.first.scriptPubKey.IsPayToCryptoCondition(p))
739 UniValue oneExport(UniValue::VOBJ);
741 oneExport.push_back(Pair("chainid", desc.first.GetHex()));
742 oneExport.push_back(Pair("txid", desc.second.first.txIn.prevout.hash.GetHex()));
743 oneExport.push_back(Pair("n", (int32_t)desc.second.first.txIn.prevout.n));
744 oneExport.push_back(Pair("valueout", desc.second.first.nValue));
745 oneExport.push_back(Pair("reservetransfer", desc.second.second.ToUniValue()));
746 ret.push_back(oneExport);
757 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
763 UniValue getchainexports(const UniValue& params, bool fHelp)
765 if (fHelp || params.size() != 1)
768 "getchainexports \"chainname\"\n"
769 "\nReturns all pending export transfers that are not yet provable with confirmed notarizations.\n"
772 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
779 + HelpExampleCli("getchainexports", "\"chainname\"")
780 + HelpExampleRpc("getchainexports", "\"chainname\"")
784 CheckPBaaSAPIsValid();
786 uint160 chainID = GetChainIDFromParam(params[0]);
788 if (chainID.IsNull())
790 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
793 CPBaaSChainDefinition chainDef;
798 if ((IsVerusActive() && GetChainDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetChainID() == chainID)
800 // which transaction are we in this block?
801 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
803 // look for new exports
804 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
806 CChainNotarizationData cnd;
807 if (GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd))
809 // get all export transactions including and since this one up to the confirmed height
810 if (GetAddressIndex(keyID, 1, addressIndex, cnd.IsConfirmed() ? cnd.vtx[cnd.lastConfirmed].second.crossHeight : defHeight))
812 UniValue ret(UniValue::VARR);
814 for (auto &idx : addressIndex)
817 CTransaction exportTx;
818 if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
820 std::vector<CBaseChainObject *> opretTransfers;
821 CCrossChainExport ccx;
822 if ((ccx = CCrossChainExport(exportTx)).IsValid() && exportTx.vout.back().scriptPubKey.IsOpReturn())
824 UniValue oneExport(UniValue::VOBJ);
825 UniValue transferArray(UniValue::VARR);
826 opretTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
827 oneExport.push_back(Pair("blockheight", idx.first.blockHeight));
828 oneExport.push_back(Pair("exportid", idx.first.txhash.GetHex()));
829 oneExport.push_back(Pair("description", ccx.ToUniValue()));
830 for (auto oneTransfer : opretTransfers)
832 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
834 transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
837 DeleteOpRetObjects(opretTransfers);
838 oneExport.push_back(Pair("transfers", transferArray));
839 ret.push_back(oneExport);
852 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
858 UniValue getchainimports(const UniValue& params, bool fHelp)
860 if (fHelp || params.size() != 1)
863 "getchainimports \"chainname\"\n"
864 "\nReturns all imports from a specific chain.\n"
867 "1. \"chainname\" (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
874 + HelpExampleCli("getchainimports", "\"chainname\"")
875 + HelpExampleRpc("getchainimports", "\"chainname\"")
879 CheckPBaaSAPIsValid();
881 uint160 chainID = GetChainIDFromParam(params[0]);
883 if (chainID.IsNull())
885 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
888 CPBaaSChainDefinition chainDef;
893 if ((IsVerusActive() && GetChainDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetChainID() == chainID)
895 // which transaction are we in this block?
896 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
898 // look for new exports
899 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
903 CChainNotarizationData cnd;
904 // get all export transactions including and since this one up to the confirmed height
905 if (GetAddressIndex(keyID, 1, addressIndex))
907 UniValue ret(UniValue::VARR);
909 for (auto &idx : addressIndex)
912 CTransaction importTx;
913 if (!idx.first.spending && myGetTransaction(idx.first.txhash, importTx, blkHash))
915 CCrossChainImport cci;
916 if ((cci = CCrossChainImport(importTx)).IsValid() && importTx.vout.back().scriptPubKey.IsOpReturn())
918 std::vector<CBaseChainObject *> opretImports;
919 CTransaction exportTx;
920 CCrossChainExport ccx;
922 opretImports = RetrieveOpRetArray(importTx.vout.back().scriptPubKey);
924 if (opretImports.size() >= 2 &&
925 opretImports[0]->objectType == CHAINOBJ_TRANSACTION &&
926 (ccx = CCrossChainExport(exportTx = ((CChainObject<CTransaction> *)opretImports[0])->object)).IsValid() &&
928 exportTx.vout.size() &&
929 exportTx.vout.back().scriptPubKey.IsOpReturn())
931 std::vector<CBaseChainObject *> opretTransfers;
933 UniValue oneImport(UniValue::VOBJ);
934 UniValue transferArray(UniValue::VARR);
935 opretTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
936 oneImport.push_back(Pair("blockheight", idx.first.blockHeight));
937 oneImport.push_back(Pair("importid", idx.first.txhash.GetHex()));
938 oneImport.push_back(Pair("description", cci.ToUniValue()));
939 for (auto oneTransfer : opretTransfers)
941 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
943 transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
946 DeleteOpRetObjects(opretTransfers);
947 oneImport.push_back(Pair("transfers", transferArray));
948 ret.push_back(oneImport);
950 DeleteOpRetObjects(opretImports);
962 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
967 UniValue getdefinedchains(const UniValue& params, bool fHelp)
969 if (fHelp || params.size() > 1)
972 "getdefinedchains (includeexpired)\n"
973 "\nReturns a complete definition for any given chain if it is registered on the blockchain. If the chain requested\n"
974 "\nis NULL, chain definition of the current chain is returned.\n"
977 "1. \"includeexpired\" (bool, optional) if true, include chains that are no longer active\n"
982 " \"version\" : \"n\", (int) version of this chain definition\n"
983 " \"name\" : \"string\", (string) name or symbol of the chain, same as passed\n"
984 " \"address\" : \"string\", (string) cryptocurrency address to send fee and non-converted premine\n"
985 " \"chainid\" : \"hex-string\", (string) 40 char string that represents the chain ID, calculated from the name\n"
986 " \"premine\" : \"n\", (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
987 " \"convertible\" : \"xxxx\" (bool) if this currency is a fractional reserve currency of Verus\n"
988 " \"launchfee\" : \"n\", (int) (launchfee * total converted) / 100000000 sent directly to premine address\n"
989 " \"startblock\" : \"n\", (int) block # on this chain, which must be notarized into block one of the chain\n"
990 " \"endblock\" : \"n\", (int) block # after which, this chain's useful life is considered to be over\n"
991 " \"eras\" : \"[obj, ...]\", (objarray) different chain phases of rewards and convertibility\n"
993 " \"reward\" : \"[n, ...]\", (int) reward start for each era in native coin\n"
994 " \"decay\" : \"[n, ...]\", (int) exponential or linear decay of rewards during each era\n"
995 " \"halving\" : \"[n, ...]\", (int) blocks between halvings during each era\n"
996 " \"eraend\" : \"[n, ...]\", (int) block marking the end of each era\n"
997 " \"eraoptions\" : \"[n, ...]\", (int) options for each era (reserved)\n"
999 " \"nodes\" : \"[obj, ..]\", (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
1001 " \"nodeaddress\" : \"txid\", (string, optional) internet, TOR, or other supported address for node\n"
1002 " \"paymentaddress\" : \"n\", (int, optional) rewards payment address\n"
1008 + HelpExampleCli("getdefinedchains", "true")
1009 + HelpExampleRpc("getdefinedchains", "true")
1013 CheckPBaaSAPIsValid();
1015 UniValue ret(UniValue::VARR);
1017 bool includeExpired = params[0].isBool() ? params[0].get_bool() : false;
1019 vector<CPBaaSChainDefinition> chains;
1020 GetDefinedChains(chains, includeExpired);
1022 for (auto def : chains)
1024 UniValue oneChain(UniValue::VOBJ);
1025 oneChain.push_back(Pair("chaindefinition", def.ToUniValue()));
1027 CChainNotarizationData cnd;
1028 GetNotarizationData(def.GetChainID(), IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
1030 int32_t confirmedHeight = -1, bestHeight = -1;
1031 confirmedHeight = cnd.vtx.size() && cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed].second.notarizationHeight : -1;
1032 bestHeight = cnd.vtx.size() && cnd.bestChain != -1 ? cnd.vtx[cnd.forks[cnd.bestChain].back()].second.notarizationHeight : -1;
1034 oneChain.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
1035 if (confirmedHeight != -1)
1037 oneChain.push_back(Pair("lastconfirmedtxid", cnd.vtx[cnd.lastConfirmed].first.GetHex().c_str()));
1038 oneChain.push_back(Pair("lastconfirmedcurrencystate", cnd.vtx[cnd.lastConfirmed].second.currencyState.ToUniValue()));
1040 oneChain.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
1041 if (bestHeight != -1)
1043 oneChain.push_back(Pair("besttxid", cnd.vtx[cnd.forks[cnd.bestChain].back()].first.GetHex().c_str()));
1044 oneChain.push_back(Pair("bestcurrencystate", cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.ToUniValue()));
1046 ret.push_back(oneChain);
1051 // 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
1052 // NULL, only transfers to that chain are returned
1053 bool GetChainTransfers(multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> &inputDescriptors, uint160 chainFilter, int start, int end, uint32_t flags)
1057 flags = CReserveTransfer::VALID;
1059 bool nofilter = chainFilter.IsNull();
1062 // look for unspent chain transfer outputs for all chains
1063 keyID = CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_RESERVE_TRANSFER);
1065 // which transaction are we in this block?
1066 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1067 std::set<uint256> countedTxes; // don't count twice
1069 LOCK2(cs_main, mempool.cs);
1071 if (!GetAddressIndex(keyID, 1, addressIndex, start, end))
1077 for (auto it = addressIndex.begin(); it != addressIndex.end(); it++)
1082 // each tx gets counted once
1083 if (countedTxes.count(it->first.txhash))
1087 countedTxes.insert(it->first.txhash);
1089 if (myGetTransaction(it->first.txhash, ntx, blkHash))
1091 for (int i = 0; i < ntx.vout.size(); i++)
1093 // if this is a transfer output, optionally to this chain, add it to the input vector
1095 CReserveTransfer rt;
1096 if (ntx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
1097 p.evalCode == EVAL_RESERVE_TRANSFER &&
1098 p.vData.size() && (rt = CReserveTransfer(p.vData[0])).IsValid() &&
1099 p.vKeys.size() > 1 &&
1100 (nofilter || GetDestinationID(p.vKeys[1]) == chainFilter) &&
1101 (rt.flags & flags) == flags)
1103 inputDescriptors.insert(make_pair(GetDestinationID(p.vKeys[1]),
1104 make_pair(CInputDescriptor(ntx.vout[i].scriptPubKey, ntx.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))), rt)));
1110 LogPrintf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1111 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1119 // returns all unspent chain transfer outputs with an optional chainFilter. if the chainFilter is not
1120 // NULL, only transfers to that chain are returned
1121 bool GetUnspentChainTransfers(multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> &inputDescriptors, uint160 chainFilter)
1123 bool nofilter = chainFilter.IsNull();
1126 // look for unspent chain transfer outputs for all chains
1127 keyID = CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_RESERVE_TRANSFER);
1129 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1131 LOCK2(cs_main, mempool.cs);
1133 if (!GetAddressUnspent(keyID, 1, unspentOutputs))
1139 CCoinsViewCache view(pcoinsTip);
1141 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1145 if (view.GetCoins(it->first.txhash, coins))
1147 for (int i = 0; i < coins.vout.size(); i++)
1149 if (coins.IsAvailable(i))
1151 // if this is a transfer output, optionally to this chain, add it to the input vector
1152 // chain filter was applied in index search
1154 CReserveTransfer rt;
1155 if (::IsPayToCryptoCondition(coins.vout[i].scriptPubKey, p) && p.evalCode == EVAL_RESERVE_TRANSFER &&
1156 p.vData.size() && (rt = CReserveTransfer(p.vData[0])).IsValid() &&
1157 p.vKeys.size() > 1 &&
1158 (nofilter || GetDestinationID(p.vKeys[1]) == chainFilter))
1160 inputDescriptors.insert(make_pair(GetDestinationID(p.vKeys[1]),
1161 make_pair(CInputDescriptor(coins.vout[i].scriptPubKey, coins.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))), rt)));
1168 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1175 // returns all unspent chain transfer outputs with an optional chainFilter. if the chainFilter is not
1176 // NULL, only transfers to that chain are returned
1177 bool GetUnspentChainExports(uint160 chainID, multimap<uint160, pair<int, CInputDescriptor>> &exportOutputs)
1179 // look for unspent notarization finalization outputs for the requested chain
1180 CKeyID keyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT));
1182 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1184 LOCK2(cs_main, mempool.cs);
1186 if (!GetAddressUnspent(keyID, 1, unspentOutputs))
1192 CCoinsViewCache view(pcoinsTip);
1194 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1198 if (view.GetCoins(it->first.txhash, coins))
1200 for (int i = 0; i < coins.vout.size(); i++)
1202 if (coins.IsAvailable(i))
1204 // if this is an export output, optionally to this chain, add it to the input vector
1206 CCrossChainExport cx;
1207 if (::IsPayToCryptoCondition(coins.vout[i].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
1208 p.vData.size() && (cx = CCrossChainExport(p.vData[0])).IsValid())
1210 exportOutputs.insert(make_pair(cx.chainID,
1211 make_pair(coins.nHeight, CInputDescriptor(coins.vout[i].scriptPubKey, coins.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))))));
1218 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1226 bool GetNotarizationData(uint160 chainID, uint32_t ecode, CChainNotarizationData ¬arizationData, vector<pair<CTransaction, uint256>> *optionalTxOut)
1228 notarizationData.version = PBAAS_VERSION;
1230 // look for unspent notarization finalization outputs for the requested chain
1231 CKeyID keyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZENOTARIZATION));
1233 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1234 CPBaaSChainDefinition chainDef;
1236 bool isEarned = ecode == EVAL_EARNEDNOTARIZATION;
1238 LOCK2(cs_main, mempool.cs);
1240 if (!GetAddressUnspent(keyID, 1, unspentOutputs))
1246 multimap<int32_t, pair<uint256, CPBaaSNotarization>> sorted;
1247 multimap<int32_t, pair<CTransaction, uint256>> sortedTxs;
1249 notarizationData.lastConfirmed = 0;
1251 // filter out all transactions that do not spend from the notarization thread, or originate as the
1253 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1255 // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1259 if (myGetTransaction(it->first.txhash, ntx, blkHash))
1261 if (!chainDef.IsValid())
1263 // try to make a chain definition out of each transaction, and keep the first one that is valid
1264 chainDef = CPBaaSChainDefinition(ntx);
1266 CPBaaSNotarization notarization = CPBaaSNotarization(ntx);
1267 if (notarization.IsValid())
1269 auto blkit = mapBlockIndex.find(blkHash);
1270 if (blkit != mapBlockIndex.end())
1272 // sort by block height, index by transaction id
1273 sorted.insert(make_pair(blkit->second->GetHeight(), make_pair(it->first.txhash, notarization)));
1276 sortedTxs.insert(make_pair(blkit->second->GetHeight(), make_pair(ntx, blkHash)));
1279 // 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
1280 if (notarization.prevHeight == 0 && !isEarned)
1282 notarizationData.lastConfirmed = -1;
1288 printf("cannot retrieve transaction %s, may need to reindex\n", it->first.txhash.GetHex().c_str());
1295 //printf("no notarizations found\n");
1299 if (!chainDef.IsValid())
1301 // the first entry of all forks must reference a confirmed transaction if there is one
1302 CTransaction rootTx;
1304 auto prevHash = sorted.begin()->second.second.prevNotarization;
1305 if (!prevHash.IsNull())
1307 if (!myGetTransaction(prevHash, rootTx, blkHash))
1312 // ensure that we have a finalization output
1314 CPBaaSNotarization notarization;
1315 CNotarizationFinalization finalization;
1316 uint32_t notarizeIdx, finalizeIdx;
1318 if (GetNotarizationAndFinalization(ecode, CMutableTransaction(rootTx), notarization, ¬arizeIdx, &finalizeIdx))
1320 notarizationData.vtx.insert(notarizationData.vtx.begin(), make_pair(prevHash, notarization));
1321 notarizationData.lastConfirmed = 0;
1324 optionalTxOut->insert(optionalTxOut->begin(), make_pair(rootTx, blkHash));
1327 // debugging, this else is not needed
1330 printf("previous transaction does not have both notarization and finalizaton outputs\n");
1337 notarizationData.lastConfirmed = -1;
1345 // we still have the chain definition in our forks, so no notarization has been confirmed yet
1346 notarizationData.lastConfirmed = -1;
1350 multimap<uint256, pair<int32_t, int32_t>> references; // associates the txid, the fork index, and the index in the fork
1352 for (auto p : sorted)
1354 notarizationData.vtx.push_back(make_pair(p.second.first, p.second.second));
1359 for (auto p : sortedTxs)
1361 optionalTxOut->push_back(p.second);
1365 // we now have all unspent notarizations sorted by block height, and the last confirmed notarization as first, if there
1366 // is one. if there is a confirmed notarization, all forks should refer to it, or they are invalid and should be spent.
1368 // find roots and create a chain from each
1369 for (int32_t i = 0; i < notarizationData.vtx.size(); i++)
1371 auto &nzp = notarizationData.vtx[i];
1372 auto it = nzp.second.prevNotarization.IsNull() ? references.end() : references.find(nzp.second.prevNotarization);
1374 int32_t chainIdx = 0;
1377 // do we refer to a notarization that is already in a fork?
1378 if (it != references.end())
1380 std::vector<int32_t> &fork = notarizationData.forks[it->second.first];
1382 // if it is the end of the fork, put this entry there, if not the end, copy up to it and start another fork
1383 if (it->second.second == (fork.size() - 1))
1386 chainIdx = it->second.first;
1387 posIdx = fork.size() - 1;
1391 notarizationData.forks.push_back(vector<int32_t>(&fork[0], &fork[it->second.second] + 1));
1392 notarizationData.forks.back().push_back(i);
1393 chainIdx = notarizationData.forks.size() - 1;
1394 posIdx = notarizationData.forks.back().size() - 1;
1399 // start a new fork that references no one else
1400 notarizationData.forks.push_back(vector<int32_t>(0));
1401 notarizationData.forks.back().push_back(i);
1402 chainIdx = notarizationData.forks.size() - 1;
1403 posIdx = notarizationData.forks.back().size() - 1;
1405 references.insert(make_pair(nzp.first, make_pair(chainIdx, posIdx)));
1410 // now, we should have all forks in vectors
1411 // they should all have roots that point to the same confirmed or initial notarization, which should be enforced by chain rules
1412 // the best chain should simply be the tip with most power
1413 for (int i = 0; i < notarizationData.forks.size(); i++)
1415 CChainPower curPower = ExpandCompactPower(notarizationData.vtx[notarizationData.forks[i].back()].second.compactPower, i);
1416 if (curPower > best)
1421 notarizationData.bestChain = best.nHeight;
1426 UniValue getnotarizationdata(const UniValue& params, bool fHelp)
1428 if (fHelp || params.size() < 1 || params.size() > 2)
1430 throw runtime_error(
1431 "getnotarizationdata \"chainid\" accepted\n"
1432 "\nReturns the latest PBaaS notarization data for the specifed chainid.\n"
1435 "1. \"chainid\" (string, required) the hex-encoded ID or string name search for notarizations on\n"
1436 "2. \"accepted\" (bool, optional) accepted, not earned notarizations, default: true if on\n"
1437 " VRSC or VRSCTEST, false otherwise\n"
1441 " \"version\" : n, (numeric) The notarization protocol version\n"
1445 + HelpExampleCli("getnotarizationdata", "\"chainid\" true")
1446 + HelpExampleRpc("getnotarizationdata", "\"chainid\"")
1450 CheckPBaaSAPIsValid();
1453 CChainNotarizationData nData;
1456 if (IsVerusActive())
1458 ecode = EVAL_ACCEPTEDNOTARIZATION;
1462 ecode = EVAL_EARNEDNOTARIZATION;
1465 chainID = GetChainIDFromParam(params[0]);
1467 if (chainID.IsNull())
1469 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chainid");
1472 if (params.size() > 1)
1474 if (!params[1].get_bool())
1476 ecode = EVAL_EARNEDNOTARIZATION;
1480 if (GetNotarizationData(chainID, ecode, nData))
1482 return nData.ToUniValue();
1486 return NullUniValue;
1490 // get inputs for all of the unspent reward outputs sent to a specific reward type for the range specified
1491 // returns the total input amount
1492 CAmount GetUnspentRewardInputs(const CPBaaSChainDefinition &chainDef, vector<CInputDescriptor> &inputs, int32_t serviceCode, int32_t height)
1495 uint160 chainID = chainDef.GetChainID();
1497 // look for unspent outputs that match the addressout hashed with service code
1498 CKeyID keyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, serviceCode), EVAL_SERVICEREWARD));
1500 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1502 if (GetAddressUnspent(keyID, 1, unspentOutputs))
1504 // spend all billing periods prior or equal to this one
1505 int billingPeriod = height / chainDef.billingPeriod;
1507 // we need to look through the inputs to ensure that they are
1508 // actual service reward outputs in the correct billing periof, since we don't currently prevent other types of transaction outputs from being
1509 // sent to the same address, though doing so would burn its value anyhow
1512 for (auto output : unspentOutputs)
1514 // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1517 if (pcoinsTip->GetCoins(output.first.txhash, coins))
1519 for (auto txout : coins.vout)
1522 if (!txout.IsNull() && IsPayToCryptoCondition(txout.scriptPubKey, p) && p.evalCode == EVAL_SERVICEREWARD)
1524 FromVector(p.vData[0], sr);
1527 inputs.push_back(CInputDescriptor(txout.scriptPubKey, txout.nValue, CTxIn(output.first.txhash, output.first.index)));
1528 retval += txout.nValue;
1533 LogPrintf("GetUnspentRewardInputs: cannot retrieve transaction %s\n", output.first.txhash.GetHex().c_str());
1534 printf("GetUnspentRewardInputs: cannot retrieve transaction %s\n", output.first.txhash.GetHex().c_str());
1543 // this adds any new notarization rewards that have been sent to the notarization reward pool for this
1544 // billing period since last accepted notarization, up to a maximum number of inputs
1545 CAmount AddNewNotarizationRewards(CPBaaSChainDefinition &chainDef, vector<CInputDescriptor> &inputs, CMutableTransaction mnewTx, int32_t height)
1547 // get current chain info
1549 newIn = GetUnspentRewardInputs(chainDef, inputs, SERVICE_NOTARIZATION, height);
1550 for (auto input : inputs)
1552 mnewTx.vin.push_back(input.txIn);
1557 UniValue submitacceptednotarization(const UniValue& params, bool fHelp)
1559 if (fHelp || params.size() != 1)
1561 throw runtime_error(
1562 "submitacceptednotarization \"hextx\"\n"
1563 "\nFinishes an almost complete notarization transaction based on the notary chain and the current wallet or pubkey.\n"
1564 "\nIf successful in submitting the transaction based on all rules, a transaction ID is returned, otherwise, NULL.\n"
1567 "1. \"hextx\" (hexstring, required) partial hex-encoded notarization transaction to submit\n"
1568 " transaction should have only one notarization and one opret output\n"
1571 "txid (hexstring) transaction ID of submitted transaction\n"
1574 + HelpExampleCli("submitacceptednotarization", "\"hextx\"")
1575 + HelpExampleRpc("submitacceptednotarization", "\"hextx\"")
1579 CheckPBaaSAPIsValid();
1581 // decode the transaction and ensure that it is formatted as expected
1582 CTransaction notarization;
1583 CPBaaSNotarization pbn;
1585 if (!DecodeHexTx(notarization, params[0].get_str()) ||
1586 notarization.vin.size() ||
1587 notarization.vout.size() != 2 ||
1588 !(pbn = CPBaaSNotarization(notarization)).IsValid() ||
1589 !notarization.vout.back().scriptPubKey.IsOpReturn())
1591 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid notarization transaction");
1594 CPBaaSChainDefinition chainDef;
1595 int32_t chainDefHeight;
1596 if (!GetChainDefinition(pbn.chainID, chainDef, &chainDefHeight))
1598 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain notarization");
1601 // ensure we are still eligible to submit
1602 // finalize all transactions we can and send the notarization reward, plus all orphaned finalization outputs
1603 // to the confirmed recipient
1605 CChainNotarizationData nData;
1606 vector<pair<CTransaction, uint256>> txesBlkHashes;
1612 success = GetNotarizationData(pbn.chainID, EVAL_ACCEPTEDNOTARIZATION, nData, &txesBlkHashes);
1615 // get notarization data and check all transactions
1619 //LogPrintf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
1620 //printf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
1622 // if any notarization exists that is accepted and more recent than the last one, but one we still agree with,
1623 // we cannot submit, aside from that, we will prepare and submit
1624 set<uint256> priorBlocks;
1625 map<uint256, CPBaaSNotarization *> notarizationData;
1627 // printf("opRet: %s\n", notarization.vout[notarization.vout.size() - 1].scriptPubKey.ToString().c_str());
1629 auto chainObjects = RetrieveOpRetArray(notarization.vout[notarization.vout.size() - 1].scriptPubKey);
1631 bool stillValid = false;
1632 if (chainObjects.size() && chainObjects.back()->objectType == CHAINOBJ_PRIORBLOCKS)
1634 // once here, default to true
1637 CPriorBlocksCommitment &pbc = ((CChainObject<CPriorBlocksCommitment> *)chainObjects.back())->object;
1638 for (auto prior : pbc.priorBlocks)
1640 priorBlocks.insert(prior);
1643 for (auto it = nData.vtx.rbegin(); it != nData.vtx.rend(); it++)
1645 // if any existing, accepted notarization is a subset of us, don't post, we will be rejected anyhow
1646 if (priorBlocks.count(it->second.notarizationPreHash))
1653 notarizationData.insert(make_pair(it->first, &it->second));
1658 DeleteOpRetObjects(chainObjects);
1660 auto lastIt = notarizationData.find(pbn.prevNotarization);
1662 if (!stillValid || (lastIt == notarizationData.end()))
1664 //printf("Notarization not matched or invalidated by prior notarization\n");
1665 throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization not matched or invalidated by prior notarization");
1668 if (pbn.prevHeight != lastIt->second->notarizationHeight)
1670 printf("Notarization heights not matched with previous notarization\n");
1671 throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization heights not matched with previous notarization");
1674 if (pbn.prevHeight != 0 && (pbn.prevHeight + CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED > pbn.notarizationHeight))
1676 //printf("Less than minimum number of blocks between notarizations\n");
1677 throw JSONRPCError(RPC_VERIFY_REJECTED, "Less than minimum number of blocks between notarizations");
1680 // valid, spend notarization outputs, all needed finalizations, add any applicable reward output for
1681 // confirmation to confirmed notary and miner of block,
1682 // add our output address and submit
1683 CMutableTransaction mnewTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
1684 vector<CInputDescriptor> notarizationInputs;
1685 for (auto input : notarization.vin)
1687 mnewTx.vin.push_back(input);
1689 for (auto output : notarization.vout)
1691 mnewTx.vout.push_back(output);
1694 CTransaction lastTx = txesBlkHashes.back().first;
1696 int32_t confirmedInput = -1;
1697 int32_t confirmedIndex;
1698 CTxDestination payee;
1700 uint32_t notarizationIdx = -1, finalizationIdx = -1;
1701 CPBaaSNotarization dummy;
1703 notarizationInputs = AddSpendsAndFinalizations(nData, pbn.prevNotarization, mnewTx, &confirmedInput, &confirmedIndex, &payee);
1705 // if we got our inputs, add finalization
1706 if (notarizationInputs.size())
1709 CCcontract_info *cp;
1710 cp = CCinit(&CC, EVAL_FINALIZENOTARIZATION);
1712 // use public key of cc
1713 CPubKey pk(ParseHex(CC.CChexstr));
1714 CKeyID id = CCrossChainRPCData::GetConditionID(pbn.chainID, EVAL_FINALIZENOTARIZATION);
1715 std::vector<CTxDestination> dests({id});
1717 // insert a finalization as second to last vout
1718 cp = CCinit(&CC, EVAL_FINALIZENOTARIZATION);
1719 pk = CPubKey(ParseHex(CC.CChexstr));
1720 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(pbn.chainID, EVAL_FINALIZENOTARIZATION))});
1722 CNotarizationFinalization nf(confirmedInput);
1724 mnewTx.vout.insert(mnewTx.vout.begin() + (mnewTx.vout.size() - 1), MakeCC1of1Vout(EVAL_FINALIZENOTARIZATION, CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE, pk, dests, nf));
1727 if (notarizationInputs.size() && GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, mnewTx, dummy, ¬arizationIdx, &finalizationIdx))
1729 // we need to add outputs to pay the reward to the confirmed notary and block miner/staker of that notarization
1730 // the rest goes back into the notarization thread
1731 // first input should be the notarization thread
1732 CTransaction newTx(mnewTx);
1733 CTransaction confirmedTx;
1734 CPBaaSNotarization confirmedPBN;
1735 CBlockIndex *pindex = NULL;
1737 CBlock confirmedBlock;
1741 BlockMap::iterator it;
1743 LOCK2(cs_main, mempool.cs);
1745 CCoinsViewCache view(pcoinsTip);
1746 int64_t dummyInterest;
1747 valueIn = view.GetValueIn(chainActive.LastTip()->GetHeight(), &dummyInterest, newTx, chainActive.LastTip()->nTime);
1751 throw JSONRPCError(RPC_TRANSACTION_REJECTED, "unable to spend necessary transaction outputs");
1754 if (confirmedInput != -1)
1756 // get data from confirmed tx and block that contains confirmed tx
1757 confirmedTx = txesBlkHashes[confirmedIndex].first;
1758 hashBlock = txesBlkHashes[confirmedIndex].second;
1759 if ((it = mapBlockIndex.find(hashBlock)) != mapBlockIndex.end())
1761 pindex = mapBlockIndex.find(hashBlock)->second;
1764 // add all inputs that might provide notary reward and calculate notary reward based on that plus current
1765 // notarization input value divided by number of blocks left in billing period, times blocks per notarization
1768 valueIn += AddNewNotarizationRewards(chainDef, notarizationInputs, mnewTx, pindex->GetHeight());
1772 LogPrintf("submitacceptednotarization: cannot find chain %s, possible corrupted database\n", chainDef.name.c_str());
1773 printf("submitacceptednotarization: cannot find chain %s, possible corrupted database\n", chainDef.name.c_str());
1778 // recipient of notary rewards and miner to share it with
1779 // notary recipient is the one from the confirmed notarization
1780 // and miner recipient is from the block it was mined into
1781 CTxDestination notaryRecipient, minerRecipient;
1783 // get recipients of any reward output
1784 if (confirmedInput != -1)
1787 if (pindex && ReadBlockFromDisk(confirmedBlock, pindex, Params().GetConsensus()) &&
1788 (confirmedPBN = CPBaaSNotarization(confirmedTx)).IsValid() &&
1789 ExtractDestination(confirmedBlock.vtx[0].vout[0].scriptPubKey, minerRecipient, false))
1791 notaryRecipient = CTxDestination(CKeyID(confirmedPBN.notaryKeyID));
1795 throw JSONRPCError(RPC_DATABASE_ERROR, "unable to retrieve confirmed notarization data");
1799 // minimum amount must go to main thread and finalization, then divide what is left among blocks in the billing period
1800 uint64_t blocksLeft = chainDef.billingPeriod - ((confirmedPBN.notarizationHeight - chainDef.startBlock) % chainDef.billingPeriod);
1801 CAmount valueOut = 0;
1802 CAmount notaryValueOut;
1804 if (valueIn > (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1))
1806 if (confirmedInput != -1)
1808 if (valueIn < (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1)))
1810 valueOut = valueIn - (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1);
1814 valueOut = (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (valueIn - (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1))) / blocksLeft;
1817 notaryValueOut = valueIn - ((CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1) + valueOut);
1824 if (notaryValueOut >= (PBAAS_MINNOTARIZATIONOUTPUT << 1))
1826 // pay the confirmed notary with
1827 // notarization reward for this billing period / remaining blocks in the billing period * min blocks in notarization
1828 // the finalization out has minimum, the notarization out has all the remainder
1829 // outputs we should have here:
1830 // 1) notarization out
1831 // 2) finalization out
1835 // 66% of output to notary address
1836 // 33% of output to primary address of block reward
1837 auto insertIt = mnewTx.vout.begin() + (finalizationIdx + 1);
1840 CAmount minerOutput = valueOut / 3;
1841 CAmount notaryOutput = valueOut / 3 * 2;
1842 mnewTx.vout.insert(insertIt, CTxOut(minerOutput, GetScriptForDestination(minerRecipient)));
1843 mnewTx.vout.insert(insertIt, CTxOut(notaryOutput, GetScriptForDestination(notaryRecipient)));
1848 notaryValueOut += valueOut;
1852 if ((notaryValueOut + valueOut + CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE) > valueIn)
1854 notaryValueOut = valueIn;
1855 printf("Not enough funds to notarize %s\n", chainDef.name.c_str());
1856 LogPrintf("Not enough funds to notarize %s\n", chainDef.name.c_str());
1860 CCcontract_info *cp;
1862 // make the output for the other chain's notarization
1863 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1865 // use public key of cc
1866 CPubKey pk(ParseHex(CC.CChexstr));
1867 CKeyID id = CCrossChainRPCData::GetConditionID(pbn.chainID, EVAL_ACCEPTEDNOTARIZATION);
1868 std::vector<CTxDestination> dests({id});
1870 mnewTx.vout[notarizationIdx] = MakeCC1of1Vout(EVAL_ACCEPTEDNOTARIZATION, notaryValueOut, pk, dests, pbn);
1872 CTransaction ntx(mnewTx);
1874 uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
1876 // sign the transaction and submit
1877 for (int i = 0; i < ntx.vin.size(); i++)
1880 SignatureData sigdata;
1882 const CScript *pScriptPubKey;
1884 // if this is our coinbase input, we won't find it elsewhere
1885 if (i < notarizationInputs.size())
1887 pScriptPubKey = ¬arizationInputs[i].scriptPubKey;
1888 value = notarizationInputs[i].nValue;
1890 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, value, SIGHASH_ALL), *pScriptPubKey, sigdata, consensusBranchId);
1894 fprintf(stderr,"submitacceptednotarization: failure to sign accepted notarization\n");
1895 throw JSONRPCError(RPC_VERIFY_ERROR, "Failed to sign notarizaton for " + chainDef.name);
1897 UpdateTransaction(mnewTx, i, sigdata);
1902 // add to mempool and submit transaction
1903 CTransaction tx(mnewTx);
1905 CValidationState state;
1906 bool fMissingInputs;
1909 LOCK2(cs_main, mempool.cs);
1910 accepted = AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs);
1913 if (state.GetRejectReason() != "")
1915 printf("Cannot enter notarization into mempool for chain %s, %s\n", chainDef.name.c_str(), state.GetRejectReason().c_str());
1917 if (state.IsInvalid()) {
1918 throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
1920 if (fMissingInputs) {
1921 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
1923 throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
1928 RelayTransaction(tx);
1931 return newTx.GetHash().GetHex();
1934 throw JSONRPCError(RPC_VERIFY_REJECTED, "Failed to get notarizaton data for chainID: " + pbn.chainID.GetHex());
1937 UniValue getcrossnotarization(const UniValue& params, bool fHelp)
1939 if (fHelp || params.size() < 2 || params.size() > 3)
1941 throw runtime_error(
1942 "getcrossnotarization \"chainid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'\n"
1943 "\nReturns the latest PBaaS notarization transaction found in the list of transaction IDs or nothing if not found\n"
1946 "1. \"chainid\" (string, required) the hex-encoded chainid to search for notarizations on\n"
1947 "2. \"txidlist\" (stringarray, optional) list of transaction ids to check in preferred order, first found is returned\n"
1948 "3. \"accepted\" (bool, optional) accepted, not earned notarizations, default: true if on\n"
1949 " VRSC or VRSCTEST, false otherwise\n"
1953 " \"crosstxid\" : \"xxxx\", (hexstring) cross-transaction id of the notarization that matches, which is one in the arguments\n"
1954 " \"txid\" : \"xxxx\", (hexstring) transaction id of the notarization that was found\n"
1955 " \"rawtx\" : \"hexdata\", (hexstring) entire matching transaction data, serialized\n"
1956 " \"newtx\" : \"hexdata\" (hexstring) the proposed notarization transaction with an opret and opretproof\n"
1960 + HelpExampleCli("getcrossnotarization", "\"chainid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
1961 + HelpExampleRpc("getcrossnotarization", "\"chainid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
1967 UniValue ret(UniValue::VOBJ);
1969 if (IsVerusActive())
1971 ecode = EVAL_ACCEPTEDNOTARIZATION;
1975 ecode = EVAL_EARNEDNOTARIZATION;
1978 if (params[0].type() == UniValue::VSTR)
1982 chainID.SetHex(params[0].get_str());
1984 catch(const std::exception& e)
1989 if (chainID.IsNull())
1991 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chainid");
1994 if (params.size() > 2)
1996 if (!params[2].get_bool())
1998 ecode = EVAL_EARNEDNOTARIZATION;
2003 if (ecode == EVAL_ACCEPTEDNOTARIZATION)
2005 crosscode = EVAL_EARNEDNOTARIZATION;
2009 crosscode = EVAL_ACCEPTEDNOTARIZATION;
2012 if (params[1].type() != UniValue::VARR)
2014 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid second parameter object type: " + itostr(params[1].type()));
2017 vector<UniValue> values = params[1].getValues();
2019 for (int32_t i = 0; i < values.size(); i++)
2021 auto txid = uint256S(values[i].get_str());
2024 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter for notarization ID: " + values[i].get_str());
2029 CChainNotarizationData nData;
2033 vector<pair<CTransaction, uint256>> nTxes;
2035 // get notarization data and check all transactions
2036 if (GetNotarizationData(chainID, ecode, nData, &nTxes))
2039 CPBaaSNotarization ourLast;
2043 // loop in reverse through list, as most recent is at end
2044 for (int32_t i = nData.vtx.size() - 1; i >= 0; i--)
2046 const pair<uint256, CPBaaSNotarization> &nzp = nData.vtx[i];
2047 tx = nTxes[i].first;
2048 blkHash = nTxes[i].second;
2049 auto nit = txids.find(nzp.second.crossNotarization);
2050 if (i == 0 || !(nit == txids.end()))
2053 // we have the first matching transaction, return it
2054 ret.push_back(Pair("crosstxid", nzp.second.crossNotarization.GetHex()));
2055 ret.push_back(Pair("txid", nzp.first.GetHex()));
2056 ret.push_back(Pair("rawtx", EncodeHexTx(tx)));
2061 // now make the basic notarization for this chain that the other chain daemon can complete
2062 // after it is returned
2065 // make sure our MMR matches our tip height, etc.
2068 CPBaaSNotarization prevNotarization(tx);
2070 if(!prevNotarization.IsValid())
2072 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Invalid prior notarization");
2075 int32_t proofheight = chainActive.Height();
2076 ChainMerkleMountainView mmv(chainActive.GetMMR(), proofheight);
2077 uint256 mmrRoot = mmv.GetRoot();
2078 uint256 preHash = mmv.mmr.GetNode(proofheight).hash;
2080 CMerkleBranch blockProof;
2081 chainActive.GetBlockProof(mmv, blockProof, proofheight);
2083 // prove the last notarization txid with new MMR, which also provides its blockhash and power as part of proof
2085 CBlockIndex *pnindex = mapBlockIndex.find(blkHash)->second;
2087 if(!pnindex || !ReadBlockFromDisk(block, pnindex, Params().GetConsensus(), 0))
2089 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
2092 int32_t prevHeight = pnindex->GetHeight();
2094 // which transaction are we in this block?
2095 CKeyID keyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZENOTARIZATION));
2096 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2098 if (!GetAddressIndex(keyID, 1, addressIndex, prevHeight, prevHeight))
2100 throw JSONRPCError(RPC_INTERNAL_ERROR, "Address index read error - possible corruption in address index");
2103 uint256 txHash = tx.GetHash();
2104 unsigned int txIndex;
2105 for (txIndex = 0; txIndex < addressIndex.size(); txIndex++)
2107 if (addressIndex[txIndex].first.txhash == txHash)
2113 if (txIndex == addressIndex.size())
2115 throw JSONRPCError(RPC_INTERNAL_ERROR, "Notarization not found in address index - possible corruption");
2119 // get index in the block as our transaction index for proofs
2120 txIndex = addressIndex[txIndex].first.index;
2123 // if bock headers are merge mined, keep header refs, not headers
2125 // create and store the notarization proof of chain
2126 vector<CBaseChainObject *> chainObjects;
2129 // first, provide the latest block header in the opret...
2130 CBlockHeader bh = chainActive[proofheight]->GetBlockHeader();
2131 CChainObject<CBlockHeader> latestHeaderObj(CHAINOBJ_HEADER, bh);
2132 chainObjects.push_back(&latestHeaderObj);
2133 orp.AddObject(CHAINOBJ_HEADER, chainActive[proofheight]->GetBlockHash());
2135 // prove it with the latest MMR root
2136 CChainObject<CMerkleBranch> latestHeaderProof(CHAINOBJ_PROOF, blockProof);
2137 chainObjects.push_back(&latestHeaderProof);
2138 orp.AddObject(bh, chainActive[proofheight]->GetBlockHash());
2140 // include the last notarization tx, minus its opret in the new notarization's opret
2141 CMutableTransaction mtx(tx);
2142 if (mtx.vout[mtx.vout.size() - 1].scriptPubKey.IsOpReturn())
2144 mtx.vout.pop_back();
2146 CTransaction strippedTx(mtx);
2148 // get a proof of the prior notarizaton from the MMR root of this notarization
2149 CMerkleBranch txProof(txIndex, block.GetMerkleBranch(txIndex));
2150 chainActive.GetMerkleProof(mmv, txProof, prevHeight);
2152 // add the cross transaction from this chain to return
2153 CChainObject<CTransaction> strippedTxObj(CHAINOBJ_TRANSACTION, strippedTx);
2154 chainObjects.push_back(&strippedTxObj);
2155 orp.AddObject(CHAINOBJ_TRANSACTION, tx.GetHash());
2157 // add proof of the transaction
2158 CChainObject<CMerkleBranch> txProofObj(CHAINOBJ_PROOF, txProof);
2159 chainObjects.push_back(&txProofObj);
2160 orp.AddObject(CHAINOBJ_PROOF, txHash);
2162 // add the MMR block nodes between the last notarization and this one, containing root that combines merkle, block, and compact power hashes
2163 CPriorBlocksCommitment priorBlocks;
2164 int numPriorBlocks = proofheight - ourLast.crossHeight;
2166 if (numPriorBlocks > PBAAS_MAXPRIORBLOCKS || numPriorBlocks > (proofheight - 1))
2167 numPriorBlocks = PBAAS_MAXPRIORBLOCKS > (proofheight - 1) ? ((proofheight - 1) < 1 ? 0 : (proofheight - 1)) : PBAAS_MAXPRIORBLOCKS;
2169 // push back the merkle, block hash, and block power commitments for prior blocks to ensure no
2170 // unintended notary overlap
2171 for (int i = numPriorBlocks; i >= 0; i--)
2173 priorBlocks.priorBlocks.push_back(mmv.mmr.GetNode(proofheight - i).hash);
2176 CChainObject<CPriorBlocksCommitment> priorBlocksObj(CHAINOBJ_PRIORBLOCKS, priorBlocks);
2177 chainObjects.push_back(&priorBlocksObj);
2178 orp.AddObject(CHAINOBJ_PRIORBLOCKS, ::GetHash(priorBlocks));
2180 // get node keys and addresses
2181 vector<CNodeData> nodes;
2182 const static int MAX_NODES = 2;
2186 if (!vNodes.empty())
2188 for (int i = 0; i < vNodes.size(); i++)
2191 vNodes[i]->copyStats(stats);
2192 if (vNodes[i]->fSuccessfullyConnected && !vNodes[i]->fInbound)
2194 CBitcoinAddress bca(CKeyID(vNodes[i]->hashPaymentAddress));
2195 nodes.push_back(CNodeData(vNodes[i]->addr.ToString(), bca.ToString()));
2201 // reduce number to max by removing randomly
2202 while (nodes.size() > MAX_NODES)
2204 int toErase = GetRandInt(nodes.size() - 1);
2205 nodes.erase(nodes.begin() + toErase);
2209 if (USE_EXTERNAL_PUBKEY)
2211 CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
2212 if (pubKey.IsFullyValid())
2214 pkID = pubKey.GetID();
2219 static bool printMsg = true;
2222 printf("No notary public key recipient has been set, so this node cannot receive rewards for notarization\n");
2223 LogPrintf("No notary public key recipient has been set, so this node cannot receive rewards for notarization\n");
2228 CBlockIndex *nzIndex = chainActive[proofheight];
2229 CCurrencyState currencyState = ConnectedChains.GetCurrencyState(proofheight);
2231 // TODO: get currency state as of proofHeight
2232 if (ConnectedChains.ThisChain().ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE)
2235 if (!ReadBlockFromDisk(block, nzIndex, Params().GetConsensus(), false))
2237 printf("Cannot read block from disk at height %d\n", nzIndex->GetHeight());
2238 LogPrintf("Cannot read block from disk at height %d\n", nzIndex->GetHeight());
2243 if (proofheight != 1)
2249 // get the current block's MMR root and proof height
2250 CPBaaSNotarization notarization = CPBaaSNotarization(CPBaaSNotarization::CURRENT_VERSION,
2251 ASSETCHAINS_CHAINID,
2256 ArithToUint256(GetCompactPower(nzIndex->nNonce, nzIndex->nBits, nzIndex->nVersion)),
2259 tx.GetHash(), prevNotarization.notarizationHeight,
2263 // we now have the chain objects, all associated proofs, and notarization data, make an appropriate transaction template with opret
2264 // and return it. notarization will need to be completed, so the only thing we really need to construct on this chain is the opret
2265 CMutableTransaction newNotarization = CreateNewContextualCMutableTransaction(Params().GetConsensus(), proofheight);
2268 CCcontract_info *cp;
2270 // make the output for the other chain's notarization
2271 cp = CCinit(&CC, crosscode);
2272 // use public key of cc
2273 CPubKey pk(ParseHex(CC.CChexstr));
2274 CKeyID id = CCrossChainRPCData::GetConditionID(chainID, crosscode);
2275 std::vector<CTxDestination> dests({id});
2277 newNotarization.vout.push_back(MakeCC1of1Vout(crosscode, CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE, pk, dests, notarization));
2279 // make the unspent finalization output
2280 cp = CCinit(&CC, EVAL_FINALIZENOTARIZATION);
2281 pk = CPubKey(ParseHex(CC.CChexstr));
2282 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZENOTARIZATION))});
2284 CNotarizationFinalization nf;
2285 newNotarization.vout.push_back(MakeCC1of1Vout(EVAL_FINALIZENOTARIZATION, DEFAULT_TRANSACTION_FEE, pk, dests, nf));
2287 newNotarization.vout.push_back(CTxOut(0, StoreOpRetArray(chainObjects)));
2289 CTransaction newTx(newNotarization);
2290 ret.push_back(Pair("newtx", EncodeHexTx(newTx)));
2296 UniValue paynotarizationrewards(const UniValue& params, bool fHelp)
2298 if (fHelp || (params.size() != 2 && params.size() != 3))
2300 throw runtime_error(
2301 "paynotarizationrewards \"chainid\" \"amount\" \"billingperiod\"\n"
2302 "\nAdds some amount of funds to a specific billing period of a PBaaS chain, which will be released\n"
2303 "\nin the form of payments to notaries whose notarizations are confirmed.\n"
2310 + HelpExampleCli("paynotarizationrewards", "\"hextx\"")
2311 + HelpExampleRpc("paynotarizationrewards", "\"hextx\"")
2314 CheckPBaaSAPIsValid();
2320 throw JSONRPCError(RPC_INVALID_PARAMETER, "Paying notarization rewards requires an active wallet");
2323 chainID = GetChainIDFromParam(params[0]);
2324 if (chainID.IsNull())
2326 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or chainid");
2329 CAmount amount = AmountFromValue(params[1]);
2330 uint32_t billingPeriod = 0;
2332 if (params.size() == 3)
2334 uint32_t billingPeriod = uni_get_int(params[2]);
2337 CServiceReward sr = CServiceReward(SERVICE_NOTARIZATION, billingPeriod);
2340 CCcontract_info *cp;
2341 cp = CCinit(&CC, EVAL_SERVICEREWARD);
2343 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2345 std::vector<CTxDestination> dests({CKeyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, SERVICE_NOTARIZATION), EVAL_SERVICEREWARD))});
2347 std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeCC1of1Vout(EVAL_SERVICEREWARD, amount, pk, dests, sr).scriptPubKey, amount}});
2350 // create the transaction with native coin as input
2351 LOCK2(cs_main, pwalletMain->cs_wallet);
2353 CReserveKey reserveKey(pwalletMain);
2358 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2360 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainID.GetHex() + ": " + failReason);
2362 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2364 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2366 return UniValue(wtx.GetHash().GetHex());
2369 UniValue listreservetransactions(const UniValue& params, bool fHelp)
2371 if (fHelp || params.size() != 1)
2373 throw runtime_error(
2374 "listreservetransactions (maxnumber) (minconfirmations)\n"
2375 "\nLists all reserve coin transactions sent to/from the current wallet.\n"
2382 + HelpExampleCli("listreservetransactions", "100 0")
2383 + HelpExampleRpc("listreservetransactions", "100 0")
2386 // lists all transactions in a wallet that are
2387 return NullUniValue;
2390 CCoinbaseCurrencyState GetInitialCurrencyState(CPBaaSChainDefinition &chainDef, int32_t definitionHeight)
2392 std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
2393 CAmount preconvertedAmount = 0, fees = 0;
2394 bool isReserve = chainDef.ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE;
2396 if (GetChainTransfers(transferInputs, chainDef.GetChainID(), definitionHeight, chainDef.startBlock, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
2398 for (auto transfer : transferInputs)
2400 // total amount will be transferred to the chain, with fee split between aggregator and miner in
2402 preconvertedAmount += transfer.second.second.nValue;
2403 fees += transfer.second.second.nFees;
2407 uint32_t Flags = isReserve ? CCurrencyState::VALID + CCurrencyState::ISRESERVE : CCurrencyState::VALID;
2408 CCurrencyState currencyState(chainDef.conversion, 0, 0, 0, preconvertedAmount, Flags);
2410 CAmount preconvertedNative = currencyState.ReserveToNative(preconvertedAmount, chainDef.conversion);
2411 currencyState.InitialSupply = preconvertedNative;
2412 currencyState.Supply += preconvertedNative;
2414 return CCoinbaseCurrencyState(currencyState, preconvertedAmount, 0, CReserveOutput(CReserveOutput::VALID, fees), chainDef.conversion, fees, fees);
2417 UniValue reserveexchange(const UniValue& params, bool fHelp)
2419 if (fHelp || params.size() != 1)
2421 throw runtime_error(
2422 "reserveexchange '[{\"toreserve\": 1, \"recipient\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'\n"
2423 "\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"
2424 "\nFunds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
2428 " \"toreserve\" : \"bool\", (bool, optional) if present, conversion is to the underlying reserve (Verus), if false, from Verus\n"
2429 " \"recipient\" : \"Rxxx\", (string, required) recipient of converted funds or funds that failed to convert\n"
2430 " \"amount\" : \"n\", (int64, required) amount of source coins that will be converted, depending on the toreserve flag, the rest is change\n"
2431 " \"limit\" : \"n\", (int64, optional) price in reserve limit, below which for buys and above which for sells, execution will occur\n"
2432 " \"validbefore\" : \"n\", (int, optional) block before which this can execute as a conversion, otherwise, it executes as a send with normal network fee\n"
2433 " \"subtractfee\" : \"bool\", (bool, optional) if true, reduce amount to destination by the fee amount, otherwise, add from inputs to cover fee"
2437 " \"txid\" : \"transactionid\" (string) The transaction id.\n"
2440 + HelpExampleCli("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
2441 + HelpExampleRpc("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
2445 CheckPBaaSAPIsValid();
2446 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Not yet implemented. Use sendreserve in this release.");
2449 UniValue sendreserve(const UniValue& params, bool fHelp)
2451 if (fHelp || params.size() != 1)
2453 throw runtime_error(
2454 "sendreserve '{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0, \"convert\": 1}' (returntx)\n"
2455 "\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"
2456 "\nFunds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
2460 " \"name\" : \"xxxx\", (string, optional) Verus ecosystem-wide name/symbol of chain to send to, if absent, current chain is assumed\n"
2461 " \"paymentaddress\" : \"Rxxx\", (string, required) transaction recipient address\n"
2462 " \"refundaddress\" : \"Rxxx\", (string, optional) if a pre-convert is not mined in time, funds can be spent by the owner of this address\n"
2463 " \"amount\" : \"n.n\", (value, required) coins that will be moved and sent to address on PBaaS chain, network and conversion fees additional\n"
2464 " \"tonative\" : \"false\", (bool, optional) auto-convert from Verus to PBaaS currency at market price\n"
2465 " \"toreserve\" : \"false\", (bool, optional) auto-convert from PBaaS to Verus reserve currency at market price\n"
2466 " \"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"
2467 " \"subtractfee\" : \"bool\", (bool, optional) if true, reduce amount to destination by the transfer and conversion fee amount. normal network fees are never subtracted"
2469 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
2472 " \"txid\" : \"transactionid\" (string) The transaction id.\n"
2475 + HelpExampleCli("sendreserve", "'{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}'")
2476 + HelpExampleRpc("sendreserve", "'{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}'")
2480 CheckPBaaSAPIsValid();
2482 // each object represents a send, and all sends are aggregated into one transaction to improve potential for scaling when moving funds between
2483 // and across multiple chains.
2485 // each output will require an additional standard cross-chain fee that will be divided evenly in two ways,
2486 // between the transaction aggregator -- the miner or staker who creates the aggregating export,
2487 // and the transaction importer on the alternate chain who posts each exported bundle.
2489 vector<CRecipient> outputs;
2490 vector<bool> vConvert;
2492 if ((!params[0].isObject()))
2494 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. Must provide a single object that represents valid outputs. see help.");
2497 bool isVerusActive = IsVerusActive();
2498 uint160 thisChainID = ConnectedChains.ThisChain().GetChainID();
2502 // default double fee for miner of chain definition tx
2503 // one output for definition, one for finalization
2504 string name = uni_get_str(find_value(params[0], "name"), "");
2505 string paymentAddr = uni_get_str(find_value(params[0], "paymentaddress"), "");
2506 string refundAddr = uni_get_str(find_value(params[0], "refundaddress"), paymentAddr);
2507 CAmount amount = AmountFromValue(find_value(params[0], "amount"));
2509 bool tonative = uni_get_int(find_value(params[0], "tonative"), false);
2510 bool toreserve = uni_get_int(find_value(params[0], "toreserve"), false);
2511 bool preconvert = uni_get_int(find_value(params[0], "preconvert"), false);
2513 bool subtractFee = uni_get_int(find_value(params[0], "subtractfee"), false);
2514 uint32_t flags = CReserveOutput::VALID;
2516 bool sameChain = false;
2519 name = ASSETCHAINS_SYMBOL;
2523 if (name == "" || paymentAddr == "" || amount < 0)
2525 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters");
2528 CBitcoinAddress ba(DecodeDestination(paymentAddr));
2531 if (!ba.IsValid() || !ba.GetKeyID(kID))
2533 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid payment address");
2536 CTxDestination refundDest = DecodeDestination(refundAddr);
2537 CBitcoinAddress refunda(refundDest);
2540 if (!refunda.IsValid() || !refunda.GetKeyID(refundID))
2542 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid refund address");
2545 uint160 chainID = CCrossChainRPCData::GetChainID(name);
2547 CPBaaSChainDefinition chainDef;
2548 int32_t definitionHeight;
2550 // validate that the target chain is still running
2551 if (!GetChainDefinition(chainID, chainDef, &definitionHeight) || !chainDef.IsValid())
2553 throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified is not a valid chain");
2556 bool isReserve = chainDef.ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE;
2558 // non-reserve conversions only work before the chain launches, then they determine the premine allocation
2559 // premine claim transactions are basically export transactions of a pre-converted amount of native coin with a neutral
2560 // impact on the already accounted for supply based on conversion conditions and contributions to the blockchain before
2562 int32_t height = chainActive.Height();
2563 bool beforeStart = chainDef.startBlock > height;
2564 CCoinbaseCurrencyState currencyState;
2566 UniValue ret = NullUniValue;
2570 if (chainID == thisChainID)
2572 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send reserve or convert, except on a PBaaS fractional reserve chain. Use sendtoaddress or z_sendmany.");
2574 else // ensure the PBaaS chain is a fractional reserve or that it's convertible and this is a conversion
2576 if (tonative || preconvert)
2578 // if chain hasn't started yet, we use the conversion as a ratio over satoshis to participate in the pre-mine
2582 if (chainDef.conversion <= 0)
2584 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string(ASSETCHAINS_SYMBOL) + " is not convertible to " + chainDef.name + ".");
2587 currencyState = GetInitialCurrencyState(chainDef, definitionHeight);
2591 throw JSONRPCError(RPC_INVALID_PARAMETER, "Until " + chainDef.name + " launches, you must use \"preconvert\" rather than \"tonative\" for conversion.");
2594 flags |= CReserveTransfer::PRECONVERT;
2595 } else if (!isReserve || preconvert)
2597 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot preconvert " + std::string(ASSETCHAINS_SYMBOL) + " after chain launch");
2601 flags |= CReserveTransfer::CONVERT;
2604 else if (!isReserve)
2606 // only reserve currencies can have reserve sent to them
2607 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string(ASSETCHAINS_SYMBOL) + " is not a reserve for " + chainDef.name + " and cannot be sent to its chain.");
2612 CCcontract_info *cp;
2613 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2615 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2617 // send the entire amount to a reserve transfer output bound to this chain
2618 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(chainID)});
2620 // start with default fee for this send
2621 CAmount transferFee = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
2623 if (flags & CReserveTransfer::PRECONVERT)
2625 arith_uint256 bigAmount(amount);
2626 static const arith_uint256 bigsatoshi(CReserveExchange::SATOSHIDEN);
2627 arith_uint256 feeRate(chainDef.launchFee);
2629 if (chainDef.launchFee)
2635 amount = (bigAmount = ((bigAmount * bigsatoshi) / (bigsatoshi - feeRate))).GetLow64();
2637 launchFee = ((bigAmount * feeRate) / bigsatoshi).GetLow64();
2639 amount -= launchFee;
2641 // add the output to this transaction
2642 CTxDestination feeOutAddr(chainDef.address);
2643 outputs.push_back(CRecipient({GetScriptForDestination(feeOutAddr), launchFee, false}));
2648 // add the fee amount that will make the conversion correct after subtracting it again
2649 amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2652 if (currencyState.ReserveIn + amount > chainDef.maxpreconvert)
2654 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string(ASSETCHAINS_SYMBOL) + " contribution maximum does not allow this conversion. Maximum participation remaining is " + ValueFromAmount(chainDef.maxpreconvert - currencyState.ReserveIn).write());
2657 else if (flags & CReserveTransfer::CONVERT && !subtractFee)
2659 // add the fee amount that will make the conversion correct after subtracting it again
2660 amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2665 amount += transferFee;
2668 if (amount <= (transferFee << 1))
2670 throw JSONRPCError(RPC_INVALID_PARAMETER, "Must send more than twice the cost of the fee. Fee for this transaction is " + ValueFromAmount(transferFee).write());
2673 transferFee = CReserveTransfer::CalculateFee(flags, amount, chainDef);
2674 amount -= transferFee;
2676 // create the transfer object
2677 CReserveTransfer rt(flags, amount, transferFee, kID);
2681 // if preconversion and we have a minpreconvert, prepare for refund output
2682 if (flags & CReserveTransfer::PRECONVERT && chainDef.minpreconvert)
2684 LOCK2(cs_main, pwalletMain->cs_wallet);
2685 CPubKey pk2(GetDestinationBytes(refundDest));
2686 if (!pk2.IsFullyValid() && !pwalletMain->GetPubKey(refundID, pk2))
2688 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot retrieve public key for refund address. Refund address must either be public key or address that is accessible from the current wallet.");
2690 dests.push_back(pk2);
2691 ccOut = MakeCC1of2Vout(EVAL_RESERVE_TRANSFER, amount + transferFee, pk, pk2, dests, (CReserveTransfer)rt);
2695 // cast object to most derived class to ensure template works on all compilers
2696 ccOut = MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, amount + transferFee, pk, dests, (CReserveTransfer)rt);
2699 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount + transferFee, false}));
2701 // create the transaction with native coin as input
2702 LOCK2(cs_main, pwalletMain->cs_wallet);
2704 CReserveKey reserveKey(pwalletMain);
2709 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2711 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2713 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2715 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2717 ret = UniValue(wtx.GetHash().GetHex());
2721 if (chainID == thisChainID)
2724 CCcontract_info *cp;
2726 // if we are on a fractional reserve chain, a conversion will convert FROM the fractional reserve currency TO
2727 // the reserve. this will basically turn into a reserveexchange transaction
2732 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + ConnectedChains.ThisChain().name + " into " + ConnectedChains.NotaryChain().chainDefinition.name + ".");
2735 CCoinsViewCache view(pcoinsTip);
2737 // we will send using a reserve output, fee will be paid by converting from reserve
2738 cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
2739 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2741 std::vector<CTxDestination> dests = std::vector<CTxDestination>({kID, CTxDestination(pk)});
2745 amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2748 CReserveExchange rex(flags + CReserveExchange::TO_RESERVE, amount);
2750 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_EXCHANGE, amount, dests, rex, pk);
2751 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount, false}));
2753 // create a transaction with native coin as input
2755 LOCK2(cs_main, pwalletMain->cs_wallet);
2757 CReserveKey reserveKey(pwalletMain);
2762 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2764 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2766 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2768 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2770 ret = UniValue(wtx.GetHash().GetHex());
2775 // convert from reserve to native (buy)
2776 cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
2777 CPubKey pk(ParseHex(CC.CChexstr));
2779 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(kID), CTxDestination(pk)});
2783 amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2786 CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, amount);
2788 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_EXCHANGE, 0, dests, rex, pk);
2789 outputs.push_back(CRecipient({ccOut.scriptPubKey, 0, false}));
2791 // create a transaction with reserve coin as input
2793 LOCK2(cs_main, pwalletMain->cs_wallet);
2795 CReserveKey reserveKey(pwalletMain);
2800 if (!pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2802 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2804 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2806 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit reserve transaction " + wtx.GetHash().GetHex());
2808 ret = UniValue(wtx.GetHash().GetHex());
2813 // we will send using a reserve output, fee will be paid by converting from reserve
2814 cp = CCinit(&CC, EVAL_RESERVE_OUTPUT);
2816 std::vector<CTxDestination> dests = std::vector<CTxDestination>({kID});
2818 // create the transfer object
2819 CReserveOutput ro(flags, amount);
2821 // native amount in the output is 0
2822 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_OUTPUT, 0, dests, ro);
2824 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount, subtractFee}));
2826 // create a transaction with reserve coin as input
2828 LOCK2(cs_main, pwalletMain->cs_wallet);
2830 CReserveKey reserveKey(pwalletMain);
2835 if (!pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2837 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2839 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2841 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2843 ret = UniValue(wtx.GetHash().GetHex());
2847 else if (chainID == ConnectedChains.NotaryChain().GetChainID())
2849 // send Verus from this PBaaS chain to the Verus chain, if converted, the inputs will be the PBaaS native coin, and we will convert to
2850 // Verus for the send
2853 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert to native and send. Can only send " + chainDef.name + " reserve to the chain.");
2860 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + ConnectedChains.ThisChain().name + " into " + ConnectedChains.NotaryChain().chainDefinition.name + ".");
2863 CAmount conversionFee;
2866 conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(amount);
2867 amount -= conversionFee;
2871 conversionFee = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2874 CCoinsViewCache view(pcoinsTip);
2875 CReserveExchange rex(flags + CReserveExchange::TO_RESERVE + CReserveExchange::SEND_OUTPUT, amount + conversionFee);
2877 // we will send using a reserve output, fee will be paid by converting from reserve
2879 CCcontract_info *cp;
2880 cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
2881 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2883 std::vector<CTxDestination> dests = std::vector<CTxDestination>({kID});
2885 // native amount in output is 0
2886 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_EXCHANGE, amount + conversionFee, dests, rex, pk);
2888 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount + conversionFee, false}));
2890 // create a transaction with native coin as input
2892 LOCK2(cs_main, pwalletMain->cs_wallet);
2894 CReserveKey reserveKey(pwalletMain);
2899 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2901 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2903 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2905 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2907 ret = UniValue(wtx.GetHash().GetHex());
2912 // we will send using from reserve input without conversion
2913 // output will be a reserve transfer
2915 CCcontract_info *cp;
2916 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2918 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2920 // send the entire amount to a reserve transfer output of the specific chain
2921 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(chainID)});
2923 CAmount transferFee = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
2926 amount -= transferFee;
2929 // create the transfer object
2930 CReserveTransfer rt(flags, amount, CReserveTransfer::DEFAULT_PER_STEP_FEE << 1, kID);
2932 CTxOut ccOut = MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, 0, pk, dests, (CReserveTransfer)rt);
2933 outputs.push_back(CRecipient({ccOut.scriptPubKey, 0, subtractFee}));
2935 // create a transaction with reserve coin as input
2937 LOCK2(cs_main, pwalletMain->cs_wallet);
2939 CReserveKey reserveKey(pwalletMain);
2944 if (!pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2946 throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2949 printf("newTx outputs:\n");
2950 for (auto outp : wtx.vout)
2952 UniValue scrOut(UniValue::VOBJ);
2953 ScriptPubKeyToJSON(outp.scriptPubKey, scrOut, false);
2954 printf("%s\n", scrOut.write(1, 2).c_str());
2957 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2959 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2961 ret = UniValue(wtx.GetHash().GetHex());
2967 throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified is not the current chain or this chain's reserve");
2973 bool GetLastImportIn(uint160 chainID, CTransaction &lastImportTx)
2975 // look for unspent chain transfer outputs for all chains
2976 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
2978 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
2982 if (!GetAddressUnspent(keyID, 1, unspentOutputs))
2987 for (auto output : unspentOutputs)
2989 // find the first one that is either in block 1, part of a chain definition transaction, or spends a prior
2990 // import output in input 0, which cannot be done unless the output is rooted in a valid import thread. anyone can try to send
2991 // coins to this address without being part of the chain, but they will be wasted and unspendable.
2993 if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
2995 // we actually don't care what is in the transaction here, only that it is a valid cross chain import tx
2996 // that is either the first in a chain or spends a valid cross chain import output
2997 CCrossChainImport cci(p.vData[0]);
2998 CTransaction lastTx;
3000 if (cci.IsValid() && myGetTransaction(output.first.txhash, lastTx, blkHash))
3002 if (output.second.blockHeight == 1 && lastTx.IsCoinBase())
3004 lastImportTx = lastTx;
3009 if (IsVerusActive())
3011 // if this is the Verus chain, then we need to either be part of a chain definition transaction
3012 // or spend a valid import output
3013 if (CPBaaSChainDefinition(lastTx).IsValid())
3015 lastImportTx = lastTx;
3018 // if we get here, we must spend a valid import output
3019 CTransaction inputTx;
3020 uint256 inputBlkHash;
3021 if (lastTx.vin.size() && myGetTransaction(lastTx.vin[0].prevout.hash, inputTx, inputBlkHash) && CCrossChainImport(lastTx).IsValid())
3023 lastImportTx = lastTx;
3034 UniValue getlastimportin(const UniValue& params, bool fHelp)
3036 if (fHelp || params.size() != 1)
3038 throw runtime_error(
3039 "getlastimportin \"fromname\"\n"
3040 "\nThis returns the last import transaction from the chain specified and a blank transaction template to use when making new\n"
3041 "\nimport transactions. Since the typical use for this call is to make new import transactions from the other chain that will be then\n"
3042 "\nbroadcast to this chain, we include the template by default.\n"
3045 " \"fromname\" (string, required) name of the chain to get the last import transaction in from\n"
3049 " \"lastimporttransaction\": \"hex\" Hex encoded serialized import transaction\n"
3050 " \"lastconfirmednotarization\" : \"hex\" Hex encoded last confirmed notarization transaction\n"
3051 " \"importtxtemplate\": \"hex\" Hex encoded import template for new import transactions\n"
3052 " \"totalimportavailable\": \"amount\" Total amount if native import currency available to import as native\n"
3056 + HelpExampleCli("getlastimportin", "jsondefinition")
3057 + HelpExampleRpc("getlastimportin", "jsondefinition")
3060 // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3061 // import transactions using the importtxtemplate as a template
3062 CheckPBaaSAPIsValid();
3064 uint160 chainID = GetChainIDFromParam(params[0]);
3066 if (chainID.IsNull())
3068 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3071 CTransaction lastImportTx, lastConfirmedTx;
3073 CChainNotarizationData cnd;
3074 std::vector<std::pair<CTransaction, uint256>> txesOut;
3078 if (!GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd, &txesOut))
3080 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No chain notarizations for " + uni_get_str(params[0]) + " found");
3083 UniValue ret(UniValue::VNULL);
3085 if (cnd.IsConfirmed())
3087 lastConfirmedTx = txesOut[cnd.lastConfirmed].first;
3088 if (GetLastImportIn(chainID, lastImportTx))
3090 ret = UniValue(UniValue::VOBJ);
3091 CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
3092 // find the import output
3095 for (importIdx = 0; importIdx < lastImportTx.vout.size(); importIdx++)
3097 if (lastImportTx.vout[importIdx].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
3102 if (importIdx >= lastImportTx.vout.size())
3104 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid lastImport transaction");
3107 txTemplate.vin.push_back(CTxIn(lastImportTx.GetHash(), importIdx, CScript()));
3109 // if Verus is active, our template import will gather all RESERVE_DEPOSITS that it can to spend into the
3111 if (IsVerusActive())
3113 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > reserveDeposits;
3115 LOCK2(cs_main, mempool.cs);
3117 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_DEPOSIT)), 1, reserveDeposits))
3119 for (auto deposit : reserveDeposits)
3122 if (deposit.second.script.IsPayToCryptoCondition(p) && p.evalCode == EVAL_RESERVE_DEPOSIT)
3124 txTemplate.vin.push_back(CTxIn(deposit.first.txhash, deposit.first.index, CScript()));
3130 CCoinsViewCache view(pcoinsTip);
3131 int64_t dummyInterest;
3132 CAmount totalImportAvailable = view.GetValueIn(cnd.vtx[cnd.lastConfirmed].second.notarizationHeight, &dummyInterest, txTemplate);
3134 ret.push_back(Pair("lastimporttransaction", EncodeHexTx(lastImportTx)));
3135 ret.push_back(Pair("lastconfirmednotarization", EncodeHexTx(lastConfirmedTx)));
3136 ret.push_back(Pair("importtxtemplate", EncodeHexTx(txTemplate)));
3137 ret.push_back(Pair("totalimportavailable", totalImportAvailable));
3143 UniValue getlatestimportsout(const UniValue& params, bool fHelp)
3145 if (fHelp || params.size() != 1)
3147 throw runtime_error(
3148 "getlatestimportsout \"name\" \"lastimporttransaction\" \"importtxtemplate\"\n"
3149 "\nThis creates and returns all new imports for the specified chain that are exported from this blockchain.\n"
3153 " \"name\" : \"string\" (string, required) name of the chain to get the export transactions for\n"
3154 " \"lastimporttransaction\" : \"hex\" (hex, required) the last import transaction to follow, return list starts with import that spends this\n"
3155 " \"lastconfirmednotarization\" : \"hex\" (hex, required) the last confirmed notarization transaction\n"
3156 " \"importtxtemplate\" : \"hex\" (hex, required) template transaction to use, so we create a transaction compatible with the other chain\n"
3157 " \"totalimportavailable\": \"amount\" (number, required) Total amount if native import currency available to import as native\n"
3161 "UniValue array of hex transactions"
3164 + HelpExampleCli("getlatestimportsout", "jsonargs")
3165 + HelpExampleRpc("getlatestimportsout", "jsonargs")
3169 // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3170 // import transactions using the importtxtemplate as a template
3171 CheckPBaaSAPIsValid();
3173 bool isVerusActive = IsVerusActive();
3175 uint160 chainID = GetChainIDFromParam(find_value(params[0], "name"));
3177 if (chainID.IsNull())
3179 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3182 // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3183 // import transactions using the importtxtemplate as a template
3184 CPBaaSChainDefinition chainDef;
3186 if (!GetChainDefinition(chainID, chainDef))
3188 throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain definition not found");
3191 std::string lastImportHex = uni_get_str(find_value(params[0], "lastimporttx"));
3192 CTransaction lastImportTx;
3194 std::string templateTxHex = uni_get_str(find_value(params[0], "importtxtemplate"));
3195 CTransaction templateTx;
3197 std::string lastConfirmedTxHex = uni_get_str(find_value(params[0], "lastconfirmednotarization"));
3198 CTransaction lastConfirmedNotarization;
3200 CAmount totalImportAvailable = uni_get_int64(find_value(params[0], "totalimportavailable"));
3202 std::vector<CBaseChainObject *> chainObjs;
3203 if (!(DecodeHexTx(lastImportTx, lastImportHex) &&
3204 DecodeHexTx(templateTx, templateTxHex) &&
3205 DecodeHexTx(lastConfirmedNotarization, lastConfirmedTxHex) &&
3206 CCrossChainImport(lastImportTx).IsValid() &&
3207 (CPBaaSChainDefinition(lastImportTx).IsValid() ||
3208 (lastImportTx.vout.back().scriptPubKey.IsOpReturn() &&
3209 (chainObjs = RetrieveOpRetArray(lastImportTx.vout.back().scriptPubKey)).size() >= 2 &&
3210 chainObjs[0]->objectType == CHAINOBJ_TRANSACTION &&
3211 chainObjs[1]->objectType == CHAINOBJ_CROSSCHAINPROOF))))
3213 DeleteOpRetObjects(chainObjs);
3214 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid last import tx");
3217 DeleteOpRetObjects(chainObjs);
3219 std::vector<CTransaction> newImports;
3220 if (!ConnectedChains.CreateLatestImports(chainDef, lastImportTx, templateTx, lastConfirmedNotarization, totalImportAvailable, newImports))
3222 throw JSONRPCError(RPC_INVALID_PARAMETER, "Failure creating new imports");
3224 UniValue ret(UniValue::VARR);
3226 for (auto import : newImports)
3228 ret.push_back(EncodeHexTx(import));
3233 // refunds a failed launch if it is eligible and not already refunded. if it fails to refund, it returns false
3234 // and a string with the reason for failure
3235 bool RefundFailedLaunch(uint160 chainID, CTransaction &lastImportTx, std::vector<CTransaction> &newRefunds, std::string &errorReason)
3237 int32_t defHeight = 0;
3238 CPBaaSChainDefinition chainDef;
3239 CTransaction chainDefTx;
3240 uint160 thisChainID = ConnectedChains.ThisChain().GetChainID();
3242 if (!GetChainDefinition(chainID, chainDef, &defHeight) || chainID == thisChainID)
3244 errorReason = "chain-not-found-or-ineligible";
3252 // validate refund eligibility and that the chain is even active for refunds
3253 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
3254 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
3255 if (GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, addressIndex, defHeight, defHeight) && addressIndex.size())
3256 //if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, unspentOutputs) && unspentOutputs.size())
3260 for (auto txidx : addressIndex)
3263 if (!myGetTransaction(txidx.first.txhash, chainDefTx, blkHash))
3265 errorReason = "chain-transaction-not-found";
3268 if (chainDefTx.vout[txidx.first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
3270 p.evalCode == EVAL_PBAASDEFINITION &&
3271 p.vData[0].size() &&
3272 (chainDef = CPBaaSChainDefinition(p.vData[0])).IsValid() &&
3273 chainDef.GetChainID() == chainID)
3283 errorReason = "valid-chain-not-found";
3287 if (!GetLastImportIn(chainID, lastImportTx))
3289 errorReason = "no-import-thread-found";
3293 std::vector<CBaseChainObject *> chainObjs;
3294 // either a fully valid import with an export or the first import either in block 1 or the chain definition
3295 // on the Verus chain
3296 if (!(lastImportTx.vout.back().scriptPubKey.IsOpReturn() &&
3297 (chainObjs = RetrieveOpRetArray(lastImportTx.vout.back().scriptPubKey)).size() >= 2 &&
3298 chainObjs[0]->objectType == CHAINOBJ_TRANSACTION &&
3299 chainObjs[1]->objectType == CHAINOBJ_CROSSCHAINPROOF) &&
3300 !(chainObjs.size() == 0 && CPBaaSChainDefinition(lastImportTx).IsValid()))
3302 DeleteOpRetObjects(chainObjs);
3303 errorReason = "invalid-import-thread-found";
3307 bool isVerusActive = IsVerusActive();
3309 uint256 lastExportHash;
3310 CTransaction lastExportTx;
3312 uint32_t blkHeight = defHeight;
3314 if (chainObjs.size())
3318 lastExportTx = ((CChainObject<CTransaction> *)chainObjs[0])->object;
3319 lastExportHash = lastExportTx.GetHash();
3320 BlockMap::iterator blkMapIt;
3321 DeleteOpRetObjects(chainObjs);
3322 if (myGetTransaction(lastExportHash, tx, blkHash) &&
3323 (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() &&
3327 blkHeight = blkMapIt->second->GetHeight();
3332 // if we haven't processed any refunds yet, setup to payout from the first export and continue
3333 CCrossChainExport ccx(lastImportTx);
3337 lastExportTx = lastImportTx;
3338 lastExportHash = lastImportTx.GetHash();
3344 LogPrintf("%s: No export thread found\n", __func__);
3345 printf("%s: No export thread found\n", __func__);
3349 // we need to get the last import transaction and handle refunds like exports, so we can make incremental progress if necessary and
3350 // know when there are none left
3351 int32_t nHeight = chainActive.Height();
3353 // make sure the chain is qualified for a refund
3354 if (!(chainDef.minpreconvert && chainDef.startBlock < nHeight && GetInitialCurrencyState(chainDef, defHeight).Reserve < chainDef.minpreconvert))
3356 errorReason = "chain-ineligible";
3361 // convert exports intended for the failed chain into imports back to this chain that spend from the import thread
3362 // all reservetransfers are refunded and charged 1/2 of an export fee
3363 uint160 chainID = chainDef.GetChainID();
3365 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
3367 // get all exports for the chain
3368 CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
3370 CBlockIndex *pIndex;
3372 // get all export transactions that were posted to the chain, since none can be sent
3373 // TODO:PBAAS - all sends to a failed chain after start block should fail. this may not want to
3374 // refund all exports, in case we decide to reuse chain names
3375 if (GetAddressIndex(keyID, 1, addressIndex, defHeight, nHeight))
3377 // get all exports from first to last, and make sure they spend from the export thread consecutively
3379 uint256 lastHash = lastExportHash;
3381 // indexed by input hash
3382 std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
3384 // validate, order, and relate them with their inputs
3385 for (auto utxo : addressIndex)
3387 // if current tx spends lastHash, then we have our next valid transaction to create an import with
3388 CTransaction tx, inputtx;
3389 uint256 blkHash1, blkHash2;
3390 BlockMap::iterator blkIt;
3391 CCrossChainExport ccx;
3393 if (!utxo.first.spending &&
3394 !(utxo.first.txhash == lastExportHash) &&
3395 myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
3396 (ccx = CCrossChainExport(tx)).IsValid() &&
3398 (!tx.IsCoinBase() &&
3400 myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
3401 inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) &&
3403 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
3405 validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
3409 CTransaction lastImport(lastImportTx);
3410 CCrossChainImport lastCCI;
3412 for (auto aixIt = validExports.find(lastExportHash);
3413 aixIt != validExports.end();
3414 aixIt = validExports.find(lastExportHash))
3416 // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
3417 // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
3418 // 2. Creates reserve outputs for unconverted imports
3419 // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
3420 // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
3421 // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
3422 // then signs the transaction considers it the latest import and the new export the latest export and
3423 // loops until there are no more confirmed, consecutive, valid export transactions to export
3425 // 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
3426 assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
3428 int32_t lastImportPrevoutN = 0, lastExportPrevoutN = 0;
3429 lastCCI = CCrossChainImport();
3432 for (int i = 0; i < lastImport.vout.size(); i++)
3434 if (lastImport.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size() && (lastCCI = CCrossChainImport(p.vData[0])).IsValid())
3436 lastImportPrevoutN = i;
3441 CCrossChainExport ccx(aixIt->second.second);
3442 if (!lastCCI.IsValid() || !ccx.IsValid())
3444 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());
3445 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());
3449 CMutableTransaction newImportTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
3450 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), lastImportPrevoutN));
3452 // if no prepared input, make one
3453 newImportTx.vout.clear();
3454 newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
3456 // we need to recalculate fees, as there will be no conversions
3458 CReserveTransactionDescriptor rtxd;
3460 std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
3462 // loop through and recalculate every transfer to charge a normal transfer fee and be a normal output
3463 // recalculate the fee output amount to be based on the new export fee calculation after canceling conversions
3465 ccx.totalFees = (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * ccx.numInputs;
3466 for (auto objPtr : exportOutputs)
3468 if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
3470 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
3472 // turn it into a normal transfer, which will create a regular native output
3473 rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
3475 if (rt.flags & CReserveTransfer::FEE_OUTPUT)
3477 // will be recalculated
3479 rt.nValue = ccx.CalculateExportFee();
3484 rt.nValue = (rt.nValue + rt.nFees) - (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1);
3485 rt.nFees = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
3490 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain(), exportOutputs, newImportTx.vout))
3492 LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
3493 printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
3495 DeleteOpRetObjects(exportOutputs);
3500 DeleteOpRetObjects(exportOutputs);
3502 // now add any unspent reserve deposit that came from this export to return it back
3503 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > reserveDeposits;
3504 CAmount totalInput = lastImport.vout[lastImportPrevoutN].nValue;
3505 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_DEPOSIT)), 1, reserveDeposits))
3507 for (auto deposit : reserveDeposits)
3510 if (deposit.second.script.IsPayToCryptoCondition(p) && p.evalCode == EVAL_RESERVE_DEPOSIT)
3512 newImportTx.vin.push_back(CTxIn(deposit.first.txhash, deposit.first.index, CScript()));
3513 totalInput += deposit.second.satoshis;
3518 CAmount totalAvailableInput = totalInput;
3519 CAmount availableReserveFees = ccx.totalFees;
3520 CAmount exportFees = ccx.CalculateExportFee();
3521 CAmount importFees = ccx.CalculateImportFee();
3522 CAmount feesOut = 0;
3525 CCcontract_info *cp;
3528 // emit a crosschain import output as summary
3529 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
3531 pk = CPubKey(ParseHex(CC.CChexstr));
3533 if (rtxd.ReserveFees() != (ccx.totalFees - exportFees))
3535 LogPrintf("%s: ERROR - refund does not match amount, rtxd.ReserveFees()=%lu, totalImport=%lu, importFees=%lu, exportFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, rtxd.ReserveFees(), ccx.totalAmount + ccx.totalFees, importFees, exportFees, ccx.totalAmount, ccx.totalFees);
3536 printf("%s: ERROR - refund does not match amount, rtxd.ReserveFees()=%lu, totalImport=%lu, importFees=%lu, exportFees=%lu, ccx.totalAmount=%lu, ccx.totalFees=%lu\n", __func__, rtxd.ReserveFees(), ccx.totalAmount + ccx.totalFees, importFees, exportFees, ccx.totalAmount, ccx.totalFees);
3539 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT)))});
3540 CCrossChainImport cci = CCrossChainImport(chainID, ccx.totalAmount + ccx.totalFees);
3542 totalAvailableInput -= rtxd.reserveIn;
3543 newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, totalAvailableInput, pk, dests, cci);
3545 if (totalAvailableInput < 0)
3547 LogPrintf("%s: ERROR - importing more native currency than available on import thread\n", __func__);
3548 printf("%s: ERROR - importing more native currency than available on import thread\n", __func__);
3552 // add the opret, which is the export transaction this imports
3553 std::vector<CBaseChainObject *> chainObjects;
3554 CChainObject<CTransaction> exportTx(CHAINOBJ_TRANSACTION, aixIt->second.second);
3555 chainObjects.push_back(&exportTx);
3557 // add a proof of the export transaction at the notarization height
3559 if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
3561 LogPrintf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
3562 printf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
3566 CMerkleBranch exportProof(aixIt->second.first.index, block.GetMerkleBranch(aixIt->second.first.index));
3567 auto mmrView = chainActive.GetMMV();
3568 mmrView.resize(aixIt->second.first.blockHeight);
3570 printf("%s: created refund %s for export %s\n", __func__, cci.ToUniValue().write().c_str(), aixIt->second.second.GetHash().GetHex().c_str());
3572 chainActive[aixIt->second.first.blockHeight]->AddMerkleProofBridge(exportProof);
3573 mmrView.GetProof(exportProof, aixIt->second.first.blockHeight);
3575 CChainObject<CCrossChainProof> exportXProof(CHAINOBJ_CROSSCHAINPROOF, CCrossChainProof(uint256(), exportProof));
3576 chainObjects.push_back(&exportXProof);
3578 // add the opret with the transaction and its proof that references the notarization with the correct MMR
3579 newImportTx.vout.push_back(CTxOut(0, StoreOpRetArray(chainObjects)));
3581 // 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
3582 // work our way forward
3583 newRefunds.push_back(newImportTx);
3584 lastImport = newImportTx;
3585 lastExportHash = aixIt->second.first.txhash;
3592 UniValue refundfailedlaunch(const UniValue& params, bool fHelp)
3594 if (fHelp || params.size() != 1)
3596 throw runtime_error(
3597 "refundfailedlaunch \"chainid\"\n"
3598 "\nRefunds any funds sent to the chain if they are eligible for refund.\n"
3599 "This attempts to refund all transactions for all contributors.\n"
3602 "\"chainid\" (hex or chain name, required) the chain to refund contributions to\n"
3607 + HelpExampleCli("refundfailedlaunch", "\"chainid\"")
3608 + HelpExampleRpc("refundfailedlaunch", "\"chainid\"")
3611 CheckPBaaSAPIsValid();
3615 chainID = GetChainIDFromParam(params[0]);
3616 if (chainID.IsNull())
3618 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or chainid");
3621 if (chainID == ConnectedChains.ThisChain().GetChainID() || chainID == ConnectedChains.NotaryChain().chainDefinition.GetChainID())
3623 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot refund the specified chain");
3626 CTransaction lastImportTx;
3627 std::vector<CTransaction> refundTxes;
3628 std::string failReason;
3630 if (!RefundFailedLaunch(chainID, lastImportTx, refundTxes, failReason))
3632 throw JSONRPCError(RPC_INVALID_REQUEST, failReason);
3635 uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
3637 UniValue ret(UniValue::VARR);
3639 CCoinsViewCache view(pcoinsTip);
3641 // sign and commit the transactions
3642 for (auto tx : refundTxes)
3644 LOCK2(cs_main, mempool.cs);
3646 CMutableTransaction newTx(tx);
3648 // sign the transaction and submit
3650 for (int i = 0; i < tx.vin.size(); i++)
3652 SignatureData sigdata;
3654 CScript outputScript;
3656 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
3658 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
3659 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
3663 CCoinsViewCache view(pcoinsTip);
3665 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
3667 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);
3668 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);
3671 value = coins.vout[tx.vin[i].prevout.n].nValue;
3672 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
3675 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
3679 fprintf(stderr,"refundfailedlaunch: failure to sign refund transaction\n");
3680 LogPrintf("refundfailedlaunch: failure to sign refund transaction\n");
3683 UpdateTransaction(newTx, i, sigdata);
3689 // push to local node and sync with wallets
3690 CValidationState state;
3691 bool fMissingInputs;
3692 CTransaction signedTx(newTx);
3693 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
3694 if (state.IsInvalid()) {
3695 fprintf(stderr,"refundfailedlaunch: rejected by memory pool for %s\n", state.GetRejectReason().c_str());
3696 LogPrintf("refundfailedlaunch: rejected by memory pool for %s\n", state.GetRejectReason().c_str());
3698 if (fMissingInputs) {
3699 fprintf(stderr,"refundfailedlaunch: missing inputs\n");
3700 LogPrintf("refundfailedlaunch: missing inputs\n");
3704 fprintf(stderr,"refundfailedlaunch: rejected by memory pool for\n");
3705 LogPrintf("refundfailedlaunch: rejected by memory pool for\n");
3712 RelayTransaction(signedTx);
3713 ret.push_back(signedTx.GetHash().GetHex());
3720 UniValue getinitialcurrencystate(const UniValue& params, bool fHelp)
3722 if (fHelp || params.size() != 1)
3724 throw runtime_error(
3725 "getinitialcurrencystate \"name\"\n"
3726 "\nReturns the total amount of preconversions that have been confirmed on the blockchain for the specified chain.\n"
3729 " \"name\" (string, required) name or chain ID of the chain to get the export transactions for\n"
3735 " \"initialratio\" : n,\n"
3736 " \"initialsupply\" : n,\n"
3737 " \"emitted\" : n,\n"
3738 " \"supply\" : n,\n"
3739 " \"reserve\" : n,\n"
3740 " \"currentratio\" : n,\n"
3745 + HelpExampleCli("getinitialcurrencystate", "name")
3746 + HelpExampleRpc("getinitialcurrencystate", "name")
3749 CheckPBaaSAPIsValid();
3751 uint160 chainID = GetChainIDFromParam(params[0]);
3753 if (chainID.IsNull())
3755 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3758 CPBaaSChainDefinition chainDef;
3759 int32_t definitionHeight;
3760 if (!GetChainDefinition(chainID, chainDef, &definitionHeight))
3762 throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain " + params[0].get_str() + " not found");
3765 return GetInitialCurrencyState(chainDef, definitionHeight).ToUniValue();
3768 UniValue getcurrencystate(const UniValue& params, bool fHelp)
3770 if (fHelp || params.size() > 1)
3772 throw runtime_error(
3773 "getcurrencystate \"n\"\n"
3774 "\nReturns the total amount of preconversions that have been confirmed on the blockchain for the specified chain.\n"
3777 " \"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"
3778 " If not specified, the latest currency state and height is returned\n"
3784 " \"blocktime\": n,\n"
3785 " \"currencystate\": {\n"
3787 " \"initialratio\" : n,\n"
3788 " \"initialsupply\" : n,\n"
3789 " \"emitted\" : n,\n"
3790 " \"supply\" : n,\n"
3791 " \"reserve\" : n,\n"
3792 " \"currentratio\" : n,\n"
3798 + HelpExampleCli("getcurrencystate", "name")
3799 + HelpExampleRpc("getcurrencystate", "name")
3802 CheckPBaaSAPIsValid();
3805 uint64_t startEnd[3] = {0};
3807 lStart = startEnd[1] = startEnd[0] = chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 1;
3809 if (params.size() == 1)
3811 if (uni_get_int(params[0], -1) == -1 && params[0].isStr())
3813 Split(params[0].get_str(), startEnd, startEnd[0], 3);
3817 if (startEnd[0] > startEnd[1])
3819 startEnd[0] = startEnd[1];
3822 if (startEnd[1] > lStart)
3824 startEnd[1] = lStart;
3827 if (startEnd[1] < startEnd[0])
3829 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range for currency state");
3832 if (startEnd[2] == 0)
3837 if (startEnd[2] > INT_MAX)
3839 startEnd[2] = INT_MAX;
3842 uint32_t start = startEnd[0], end = startEnd[1], step = startEnd[2];
3844 UniValue ret(UniValue::VARR);
3846 for (int i = start; i <= end; i += step)
3848 CCoinbaseCurrencyState currencyState = ConnectedChains.GetCurrencyState(i);
3849 UniValue entry(UniValue::VOBJ);
3850 entry.push_back(Pair("height", i));
3851 entry.push_back(Pair("blocktime", (uint64_t)chainActive.LastTip()->nTime));
3853 entry.push_back(Pair("currencystate", currencyState.ToUniValue()));
3854 ret.push_back(entry);
3859 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
3861 UniValue definechain(const UniValue& params, bool fHelp)
3863 if (fHelp || params.size() != 1)
3865 throw runtime_error(
3866 "definechain '{\"name\": \"BAAS\", ... }'\n"
3867 "\nThis defines a PBaaS chain, provides it with initial notarization fees to support its launch, and prepares it to begin running.\n"
3871 " \"name\" : \"xxxx\", (string, required) unique Verus ecosystem-wide name/symbol of this PBaaS chain\n"
3872 " \"paymentaddress\" : \"Rxxx\", (string, optional) premine and launch fee recipient\n"
3873 " \"premine\" : \"n\", (int, optional) amount of coins that will be premined and distributed to premine address\n"
3874 " \"initialcontribution\" : \"n\", (int, optional) amount of coins as initial contribution\n"
3875 " \"conversion\" : \"n\", (int, optional) amount of coins that may be converted from Verus, price determined by total contribution\n"
3876 " \"launchfee\" : \"n\", (int, optional) VRSC fee for conversion at startup, multiplied by amount, divided by 100000000\n"
3877 " \"startblock\" : \"n\", (int, optional) VRSC block must be notarized into block 1 of PBaaS chain, default curheight + 100\n"
3878 " \"eras\" : \"objarray\", (array, optional) data specific to each era, maximum 3\n"
3880 " \"reward\" : \"n\", (int64, optional) native initial block rewards in each period\n"
3881 " \"decay\" : \"n\", (int64, optional) reward decay for each era\n"
3882 " \"halving\" : \"n\", (int, optional) halving period for each era\n"
3883 " \"eraend\" : \"n\", (int, optional) ending block of each era\n"
3884 " \"eraoptions\" : \"n\", (int, optional) options for each era\n"
3886 " \"notarizationreward\" : \"n\", (int, required) default VRSC notarization reward total for first billing period\n"
3887 " \"billingperiod\" : \"n\", (int, optional) number of blocks in each billing period\n"
3888 " \"nodes\" : \"[obj, ..]\", (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
3890 " \"networkaddress\" : \"txid\", (string, optional) internet, TOR, or other supported address for node\n"
3891 " \"paymentaddress\" : \"n\", (int, optional) rewards payment address\n"
3897 " \"txid\" : \"transactionid\", (string) The transaction id\n"
3898 " \"tx\" : \"json\", (json) The transaction decoded as a transaction\n"
3899 " \"hex\" : \"data\" (string) Raw data for signed transaction\n"
3903 + HelpExampleCli("definechain", "jsondefinition")
3904 + HelpExampleRpc("definechain", "jsondefinition")
3908 CheckPBaaSAPIsValid();
3910 if (!params[0].isObject())
3912 throw JSONRPCError(RPC_INVALID_PARAMETER, "JSON object required. see help.");
3916 throw JSONRPCError(RPC_WALLET_ERROR, "must have active wallet to define PBaaS chain");
3919 UniValue valStr(UniValue::VSTR);
3920 if (!valStr.read(params[0].write()))
3922 throw JSONRPCError(RPC_INVALID_PARAMS, "Invalid characters in blockchain definition");
3925 CPBaaSChainDefinition newChain(params[0]);
3927 if (!newChain.IsValid())
3929 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain definition. see help.");
3932 CPBaaSChainDefinition checkDef;
3933 if (GetChainDefinition(newChain.name, checkDef))
3935 throw JSONRPCError(RPC_INVALID_PARAMETER, newChain.name + " chain already defined. see help.");
3938 if (!newChain.startBlock || newChain.startBlock < (chainActive.Height() + PBAAS_MINSTARTBLOCKDELTA))
3940 newChain.startBlock = chainActive.Height() + PBAAS_MINSTARTBLOCKDELTA;
3943 if (newChain.billingPeriod < CPBaaSChainDefinition::MIN_BILLING_PERIOD || (newChain.notarizationReward / newChain.billingPeriod) < CPBaaSChainDefinition::MIN_PER_BLOCK_NOTARIZATION)
3945 throw JSONRPCError(RPC_INVALID_PARAMS, "Billing period of at least " +
3946 to_string(CPBaaSChainDefinition::MIN_BILLING_PERIOD) +
3947 " blocks and per-block notary rewards of >= 1000000 are required to define a chain\n");
3950 for (int i = 0; i < newChain.rewards.size(); i++)
3952 arith_uint256 reward(newChain.rewards[i]), decay(newChain.rewardsDecay[i]), limit(0x7fffffffffffffff);
3953 if (reward * decay > limit)
3955 throw JSONRPCError(RPC_INVALID_PARAMS, "reward * decay exceeds 64 bit integer limit of 9,223,372,036,854,775,807\n");
3959 vector<CRecipient> outputs;
3961 // default double fee for miner of chain definition tx
3962 // one output for definition, one for finalization
3963 CAmount nReward = newChain.notarizationReward + (DEFAULT_TRANSACTION_FEE * 4);
3966 CCcontract_info *cp;
3968 // make the chain definition output
3969 cp = CCinit(&CC, EVAL_PBAASDEFINITION);
3970 // need to be able to send this to EVAL_PBAASDEFINITION address as a destination, locked by the default pubkey
3971 CPubKey pk(ParseHex(CC.CChexstr));
3973 std::vector<CTxDestination> dests({CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION))});
3974 CTxOut defOut = MakeCC1of1Vout(EVAL_PBAASDEFINITION, DEFAULT_TRANSACTION_FEE, pk, dests, newChain);
3975 outputs.push_back(CRecipient({defOut.scriptPubKey, CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE, false}));
3977 // make the first chain notarization output
3978 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
3980 // we need to make a notarization, notarize this information and block 0, since we know that will be in the new
3981 // chain, our authorization will be that we are the chain definition
3982 uint256 mmvRoot, nodePreHash;
3985 auto mmr = chainActive.GetMMR();
3986 auto mmv = CMerkleMountainView<CMMRPowerNode, CChunkedLayer<CMMRPowerNode>, COverlayNodeLayer<CMMRPowerNode, CChain>>(mmr, mmr.size());
3988 mmvRoot = mmv.GetRoot();
3989 nodePreHash = mmr.GetNode(0).hash;
3993 extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY;
3994 if (USE_EXTERNAL_PUBKEY)
3996 CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
3997 if (pubKey.IsFullyValid())
3999 pkID = pubKey.GetID();
4003 CAmount initialToConvert = 0;
4004 CAmount initialConversion = 0;
4005 CAmount initialReserve = 0;
4006 CAmount initialFee = 0;
4007 CAmount conversionFee = 0;
4009 // add initial conversion payment if present for reserve to reserve
4010 // any initial contribution must be at least 1 coin
4011 if (newChain.initialcontribution > COIN)
4013 arith_uint256 bigConvert(newChain.initialcontribution);
4014 static const arith_uint256 bigsatoshi(CReserveExchange::SATOSHIDEN);
4015 initialFee = ((bigConvert * arith_uint256(newChain.launchFee)) / bigsatoshi).GetLow64();
4016 initialToConvert = newChain.initialcontribution - initialFee;
4017 bigConvert = arith_uint256(initialToConvert);
4018 initialConversion = ((bigConvert * arith_uint256(newChain.conversion)) / bigsatoshi).GetLow64();
4019 // if a reserve chain, converted amount goes to reserves
4020 if (!(newChain.ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE))
4022 initialReserve = initialToConvert;
4027 newChain.initialcontribution = 0;
4030 CCurrencyState currencyState(newChain.conversion, newChain.premine + initialConversion, initialConversion, 0, initialReserve);
4032 CPBaaSNotarization pbn = CPBaaSNotarization(CPBaaSNotarization::CURRENT_VERSION,
4033 newChain.GetChainID(),
4037 ArithToUint256(GetCompactPower(chainActive.Genesis()->nNonce, chainActive.Genesis()->nBits, chainActive.Genesis()->nVersion)),
4044 pk = CPubKey(ParseHex(CC.CChexstr));
4045 dests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_ACCEPTEDNOTARIZATION))});
4046 CTxOut notarizationOut = MakeCC1of1Vout(EVAL_ACCEPTEDNOTARIZATION, nReward, pk, dests, pbn);
4047 outputs.push_back(CRecipient({notarizationOut.scriptPubKey, newChain.notarizationReward, false}));
4049 // make the finalization output
4050 cp = CCinit(&CC, EVAL_FINALIZENOTARIZATION);
4051 pk = CPubKey(ParseHex(CC.CChexstr));
4052 dests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_FINALIZENOTARIZATION))});
4054 CNotarizationFinalization nf;
4055 CTxOut finalizationOut = MakeCC1of1Vout(EVAL_FINALIZENOTARIZATION, DEFAULT_TRANSACTION_FEE, pk, dests, nf);
4056 outputs.push_back(CRecipient({finalizationOut.scriptPubKey, CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE, false}));
4058 // create import and export threads
4059 // import - only spendable for reserve currency or currency with preconversion to allow import of conversions, this output will include
4060 // all pre-converted coins
4061 // chain definition - always
4062 // make the chain definition output
4064 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
4066 pk = CPubKey(ParseHex(CC.CChexstr));
4068 CKeyID newChainID(newChain.GetChainID());
4070 // import thread is specific to the chain importing from
4071 dests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_IMPORT)));
4073 CTxOut importThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, DEFAULT_TRANSACTION_FEE, pk, dests, CCrossChainImport(newChainID, 0));
4074 outputs.push_back(CRecipient({importThreadOut.scriptPubKey, DEFAULT_TRANSACTION_FEE, false}));
4076 // export - currently only spendable for reserve currency, but added for future capabilities
4078 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
4080 pk = CPubKey(ParseHex(CC.CChexstr));
4081 dests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_EXPORT)));
4083 CTxOut exportThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, DEFAULT_TRANSACTION_FEE, pk, dests, CCrossChainExport(newChainID, 0, 0, 0));
4084 outputs.push_back(CRecipient({exportThreadOut.scriptPubKey, DEFAULT_TRANSACTION_FEE, false}));
4086 // make the reserve transfer and fee outputs if there is an initial contribution
4087 if (newChain.initialcontribution)
4089 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
4090 pk = CPubKey(ParseHex(CC.CChexstr));
4091 assert(pk.IsFullyValid());
4093 dests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(newChainID)});
4095 CAmount fee = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(initialToConvert);
4096 CAmount contribution = (initialToConvert + fee) - CReserveTransactionDescriptor::CalculateConversionFee(initialToConvert + fee);
4097 fee += CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
4099 CReserveTransfer rt = CReserveTransfer(CReserveTransfer::PRECONVERT + CReserveTransfer::VALID, contribution, fee, newChain.address);
4100 CTxOut reserveTransferOut = MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, initialToConvert + fee, pk, dests, (CReserveTransfer)rt);
4101 outputs.push_back(CRecipient({reserveTransferOut.scriptPubKey, initialToConvert + fee, false}));
4103 // if there is a fee output, send it to the payment address
4106 CTxDestination feeOutAddr(newChain.address);
4107 outputs.push_back(CRecipient({GetScriptForDestination(feeOutAddr), initialFee, false}));
4111 // create the transaction
4114 LOCK2(cs_main, pwalletMain->cs_wallet);
4116 CReserveKey reserveKey(pwalletMain);
4121 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
4123 throw JSONRPCError(RPC_TRANSACTION_ERROR, newChain.name + ": " + failReason);
4127 UniValue uvret(UniValue::VOBJ);
4128 uvret.push_back(Pair("chaindefinition", CPBaaSChainDefinition(wtx).ToUniValue()));
4130 uvret.push_back(Pair("basenotarization", CPBaaSNotarization(wtx).ToUniValue()));
4132 uvret.push_back(Pair("txid", wtx.GetHash().GetHex()));
4134 UniValue txJSon(UniValue::VOBJ);
4135 TxToJSON(wtx, uint256(), txJSon);
4136 uvret.push_back(Pair("tx", txJSon));
4138 string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
4139 uvret.push_back(Pair("hex", strHex));
4144 UniValue registernamecommitment(const UniValue& params, bool fHelp)
4146 if (fHelp || (params.size() < 2 && params.size() > 3))
4148 throw runtime_error(
4149 "registernamecommitment \"name\" \"controladdress\" (\"referralidentity\")\n"
4150 "\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"
4151 "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"
4152 "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"
4155 "\"name\" (string, required) the unique name to commit to. creating a name commitment is not a registration, and if one is\n"
4156 " created for a name that exists, it may succeed, but will never be able to be used.\n"
4157 "\"controladdress\" (address, required) address that will control this commitment\n"
4158 "\"referralidentity\" (identity, optional)friendly name or identity address that is provided as a referral mechanism and to lower network cost of the ID\n"
4162 " \"txid\" : \"hexid\"\n"
4163 " \"namereservation\" :\n"
4165 " \"name\" : \"namestr\", (string) the unique name in this commitment\n"
4166 " \"salt\" : \"hexstr\", (hex) salt used to hide the commitment\n"
4167 " \"referral\": \"identityaddress\", (base58) address of the referring identity if there is one\n"
4168 " \"parent\" : \"namestr\", (string) name of the parent if not Verus or Verus test\n"
4169 " \"nameid\" : \"address\", (base58) identity address for this identity if it is created\n"
4174 + HelpExampleCli("registernamecommitment", "\"name\"")
4175 + HelpExampleRpc("registernamecommitment", "\"name\"")
4179 CheckIdentityAPIsValid();
4182 std::string name = CleanName(uni_get_str(params[0]), parent, true);
4184 // if either we have an invalid name or an implied parent, that is not valid
4185 if (name == "" || !parent.IsNull() || name != uni_get_str(params[0]))
4187 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 (\\/:*?\"<>|@)");
4190 parent = ConnectedChains.ThisChain().GetChainID();
4192 CTxDestination dest = DecodeDestination(uni_get_str(params[1]));
4193 if (dest.which() == COptCCParams::ADDRTYPE_INVALID)
4195 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid control address for commitment");
4198 // create the transaction with native coin as input
4199 LOCK2(cs_main, pwalletMain->cs_wallet);
4201 CIdentityID referrer;
4202 if (params.size() > 2)
4204 CTxDestination referDest = DecodeDestination(uni_get_str(params[2]));
4205 if (referDest.which() != COptCCParams::ADDRTYPE_ID)
4207 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid referral identity for commitment, must be a currently registered friendly name or i-address");
4209 referrer = CIdentityID(GetDestinationID(referDest));
4210 if (!CIdentity::LookupIdentity(referrer).IsValidUnrevoked())
4212 throw JSONRPCError(RPC_INVALID_PARAMETER, "Referral identity for commitment must be a currently valid, unrevoked friendly name or i-address");
4216 CNameReservation nameRes(name, referrer, GetRandHash());
4217 CCommitmentHash commitment(nameRes.GetCommitment());
4219 CConditionObj<CCommitmentHash> condObj(EVAL_IDENTITY_COMMITMENT, std::vector<CTxDestination>({dest}), 1, &commitment);
4220 std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeMofNCCScript(condObj, &dest), CCommitmentHash::DEFAULT_OUTPUT_AMOUNT, false}});
4223 if (CIdentity::LookupIdentity(CIdentity::GetID(name, parent)).IsValid())
4225 throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity already exists.");
4228 CReserveKey reserveKey(pwalletMain);
4233 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
4235 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create commitment transaction: " + failReason);
4237 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4239 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4242 UniValue ret(UniValue::VOBJ);
4243 ret.push_back(Pair("txid", wtx.GetHash().GetHex()));
4244 ret.push_back(Pair("namereservation", nameRes.ToUniValue()));
4248 UniValue registeridentity(const UniValue& params, bool fHelp)
4250 if (fHelp || params.size() < 1 || params.size() > 2)
4252 throw runtime_error(
4253 "registeridentity \"jsonidregistration\" feeoffer\n"
4258 " \"txid\" : \"hexid\", (hex) the transaction ID of the name committment for this ID name\n"
4259 " \"namereservation\" :\n"
4261 " \"name\": \"namestr\", (string) the unique name in this commitment\n"
4262 " \"salt\": \"hexstr\", (hex) salt used to hide the commitment\n"
4263 " \"referrer\": \"identityID\", (name@ or address) must be a valid ID to use as a referrer to receive a discount\n"
4267 " \"name\": \"namestr\", (string) the unique name for this identity\n"
4271 "feeoffer (amount, optional) amount to offer miner/staker for the registration fee, if missing, uses standard price\n\n"
4274 " transactionid (hexstr)\n"
4277 + HelpExampleCli("registeridentity", "jsonidregistration")
4278 + HelpExampleRpc("registeridentity", "jsonidregistration")
4282 CheckIdentityAPIsValid();
4284 // all names have a parent of the current chain
4285 uint160 parent = ConnectedChains.ThisChain().GetChainID();
4287 uint256 txid = uint256S(uni_get_str(find_value(params[0], "txid")));
4288 CNameReservation reservation(find_value(params[0], "namereservation"));
4290 CIdentity newID(find_value(params[0], "identity"));
4291 if (!newID.IsValid())
4293 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity");
4297 CAmount minFeeOffer = reservation.referral.IsNull() ? CIdentity::FullRegistrationAmount() : CIdentity::ReferredRegistrationAmount();
4299 if (params.size() > 1)
4301 feeOffer = AmountFromValue(params[1]);
4305 feeOffer = minFeeOffer;
4308 if (feeOffer < minFeeOffer)
4310 throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee offer must be at least " + ValueFromAmount(CIdentity::MinRegistrationAmount()).write());
4313 uint160 impliedParent;
4314 if (txid.IsNull() || CleanName(reservation.name, impliedParent) != CleanName(newID.name, impliedParent) || !impliedParent.IsNull())
4316 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity description or mismatched reservation.");
4319 // lookup commitment to be sure that we can register this identity
4320 LOCK2(cs_main, pwalletMain->cs_wallet);
4323 uint32_t commitmentHeight;
4326 int commitmentOutput;
4328 // must be present and in a mined block
4331 if (!myGetTransaction(txid, txOut, hashBlk) || hashBlk.IsNull())
4333 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment transaction id");
4336 auto indexIt = mapBlockIndex.find(hashBlk);
4337 if (indexIt == mapBlockIndex.end() || indexIt->second->GetHeight() > chainActive.Height() || chainActive[indexIt->second->GetHeight()]->GetBlockHash() != indexIt->second->GetBlockHash())
4339 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment");
4342 commitmentHeight = indexIt->second->GetHeight();
4344 for (int i = 0; i < txOut.vout.size(); i++)
4347 if (txOut.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_COMMITMENT && p.vData.size())
4349 commitmentOutput = i;
4350 ::FromVector(p.vData[0], ch);
4354 if (ch.hash.IsNull())
4356 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment hash");
4360 if (ch.hash != reservation.GetCommitment().hash)
4362 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment salt or referral ID");
4365 // when creating an ID, the parent is always the current chains, and it is invalid to specify a parent
4366 if (!newID.parent.IsNull())
4368 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid to specify parent or qualified name when creating an identity. Parent is determined by the current blockchain.");
4371 newID.parent = parent;
4373 CIdentity dupID = newID.LookupIdentity(newID.GetID());
4374 if (dupID.IsValid())
4376 throw JSONRPCError(RPC_VERIFY_ALREADY_IN_CHAIN, "Identity already exists.");
4379 // make sure we have a revocation and recovery authority defined
4380 CIdentity revocationAuth = (newID.revocationAuthority.IsNull() || newID.revocationAuthority == newID.GetID()) ? newID : newID.LookupIdentity(newID.revocationAuthority);
4381 CIdentity recoveryAuth = (newID.recoveryAuthority.IsNull() || newID.recoveryAuthority == newID.GetID()) ? newID : newID.LookupIdentity(newID.recoveryAuthority);
4383 if (!recoveryAuth.IsValidUnrevoked() || !revocationAuth.IsValidUnrevoked())
4385 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked recovery, or revocation identity.");
4388 // now we have a new and valid primary, revocation, and recovery identity, as well as a valid reservation
4389 newID.revocationAuthority = revocationAuth.GetID();
4390 newID.recoveryAuthority = recoveryAuth.GetID();
4392 // create the identity definition transaction & reservation key output
4393 CConditionObj<CNameReservation> condObj(EVAL_IDENTITY_RESERVATION, std::vector<CTxDestination>({CIdentityID(newID.GetID())}), 1, &reservation);
4394 CTxDestination resIndexDest = CKeyID(CCrossChainRPCData::GetConditionID(newID.GetID(), EVAL_IDENTITY_RESERVATION));
4395 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4397 // add referrals, Verus supports referrals
4398 if ((ConnectedChains.ThisChain().IDReferrals() || IsVerusActive()) && !reservation.referral.IsNull())
4400 uint32_t referralHeight;
4402 CTransaction referralIdTx;
4403 auto referralIdentity = newID.LookupIdentity(reservation.referral, commitmentHeight - 1);
4404 if (referralIdentity.IsValidUnrevoked())
4406 if (!newID.LookupFirstIdentity(reservation.referral, &referralHeight, &referralTxIn, &referralIdTx).IsValid())
4408 throw JSONRPCError(RPC_DATABASE_ERROR, "Database or blockchain data error, \"" + referralIdentity.name + "\" seems valid, but first instance is not found in index");
4411 // create outputs for this referral and up to n identities back in the referral chain
4412 outputs.push_back({referralIdentity.TransparentOutput(referralIdentity.GetID()), CIdentity::ReferralAmount(), false});
4413 feeOffer -= CIdentity::ReferralAmount();
4414 int afterId = referralTxIn.prevout.n + 1;
4415 for (int i = afterId; i < referralIdTx.vout.size() && (i - afterId) < (CIdentity::REFERRAL_LEVELS - 1); i++)
4417 CTxDestination nextID;
4418 COptCCParams p, master;
4420 if (referralIdTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
4422 p.evalCode == EVAL_NONE &&
4423 p.vKeys.size() == 1 &&
4424 (p.vData.size() == 1 ||
4425 (p.vData.size() == 2 &&
4426 p.vKeys[0].which() == COptCCParams::ADDRTYPE_ID &&
4427 (master = COptCCParams(p.vData[1])).IsValid() &&
4428 master.evalCode == EVAL_NONE)))
4430 outputs.push_back({newID.TransparentOutput(CIdentityID(GetDestinationID(p.vKeys[0]))), CIdentity::ReferralAmount(), false});
4431 feeOffer -= CIdentity::ReferralAmount();
4441 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked referral identity at time of commitment");
4445 CScript reservationOutScript = MakeMofNCCScript(condObj, &resIndexDest);
4446 outputs.push_back({reservationOutScript, CNameReservation::DEFAULT_OUTPUT_AMOUNT, false});
4448 // make one dummy output, which CreateTransaction will leave as last, and we will remove to add its output to the fee
4449 // this serves to keep the change output after our real reservation output
4450 outputs.push_back({reservationOutScript, feeOffer, false});
4454 CReserveKey reserveKey(pwalletMain);
4459 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4461 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create identity transaction: " + failReason);
4464 // add commitment output
4465 CMutableTransaction mtx(wtx);
4466 mtx.vin.push_back(CTxIn(txid, commitmentOutput));
4468 // remove the fee offer output
4469 mtx.vout.pop_back();
4471 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4474 CCoinsViewCache view(pcoinsTip);
4475 for (int i = 0; i < wtx.vin.size(); i++)
4478 SignatureData sigdata;
4481 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4486 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4488 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()));
4492 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());
4493 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());
4494 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
4496 UpdateTransaction(mtx, i, sigdata);
4499 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4501 if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4503 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4506 // including definitions and claims thread
4507 return UniValue(wtx.GetHash().GetHex());
4510 UniValue updateidentity(const UniValue& params, bool fHelp)
4512 if (fHelp || params.size() < 1 || params.size() > 2)
4514 throw runtime_error(
4515 "updateidentity \"jsonidentity\" (returntx)\n"
4519 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
4524 + HelpExampleCli("updateidentity", "\'{\"name\" : \"myname\"}\'")
4525 + HelpExampleRpc("updateidentity", "\'{\"name\" : \"myname\"}\'")
4529 CheckIdentityAPIsValid();
4532 bool returnTx = false;
4533 CIdentity newID(params[0]);
4535 if (!newID.IsValid())
4537 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
4540 if (params.size() > 1)
4542 returnTx = uni_get_bool(params[1], false);
4549 LOCK2(cs_main, pwalletMain->cs_wallet);
4551 if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
4553 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
4556 // create the identity definition transaction
4557 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4560 CReserveKey reserveKey(pwalletMain);
4565 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4567 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
4570 CMutableTransaction mtx(wtx);
4571 mtx.vin.push_back(idTxIn);
4572 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4575 CCoinsViewCache view(pcoinsTip);
4576 for (int i = 0; i < wtx.vin.size(); i++)
4579 SignatureData sigdata;
4582 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4587 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4589 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()));
4593 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());
4594 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());
4595 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
4597 UpdateTransaction(mtx, i, sigdata);
4600 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4604 return EncodeHexTx(wtx);
4606 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4608 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4610 return wtx.GetHash().GetHex();
4613 UniValue revokeidentity(const UniValue& params, bool fHelp)
4615 if (fHelp || params.size() < 1 || params.size() > 2)
4617 throw runtime_error(
4618 "revokeidentity \"nameorID\" (returntx)\n"
4622 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
4627 + HelpExampleCli("revokeidentity", "\"nameorID\"")
4628 + HelpExampleRpc("revokeidentity", "\"nameorID\"")
4632 CheckIdentityAPIsValid();
4635 bool returnTx = false;
4636 CTxDestination idDest = DecodeDestination(uni_get_str(params[0]));
4638 if (idDest.which() != COptCCParams::ADDRTYPE_ID)
4640 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
4643 CIdentityID idID(GetDestinationID(idDest));
4645 if (params.size() > 1)
4647 returnTx = uni_get_bool(params[1], false);
4654 LOCK2(cs_main, pwalletMain->cs_wallet);
4656 if (!(oldID = CIdentity::LookupIdentity(idID, 0, &idHeight, &idTxIn)).IsValid())
4658 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + EncodeDestination(idID));
4661 CIdentity newID(oldID);
4664 // create the identity definition transaction
4665 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4668 CReserveKey reserveKey(pwalletMain);
4673 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4675 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
4677 CMutableTransaction mtx(wtx);
4679 // add the spend of the last ID transaction output
4680 mtx.vin.push_back(idTxIn);
4682 // all of the reservation output is actually the fee offer, so zero the output
4683 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4686 CCoinsViewCache view(pcoinsTip);
4687 for (int i = 0; i < wtx.vin.size(); i++)
4690 SignatureData sigdata;
4693 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4698 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4700 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()));
4704 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());
4705 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());
4706 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
4708 UpdateTransaction(mtx, i, sigdata);
4711 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4715 return EncodeHexTx(wtx);
4717 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4719 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4721 return wtx.GetHash().GetHex();
4724 UniValue recoveridentity(const UniValue& params, bool fHelp)
4726 if (fHelp || params.size() < 1 || params.size() > 2)
4728 throw runtime_error(
4729 "recoveridentity \"jsonidentity\" (returntx)\n"
4733 " \"returntx\" (bool, optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
4738 + HelpExampleCli("recoveridentity", "\'{\"name\" : \"myname\"}\'")
4739 + HelpExampleRpc("recoveridentity", "\'{\"name\" : \"myname\"}\'")
4742 CheckIdentityAPIsValid();
4745 bool returnTx = false;
4746 CIdentity newID(params[0]);
4748 if (!newID.IsValid())
4750 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
4753 if (params.size() > 1)
4755 returnTx = uni_get_bool(params[1], false);
4762 LOCK2(cs_main, pwalletMain->cs_wallet);
4764 if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
4766 throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
4769 if (!oldID.IsRevoked())
4771 throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity must be revoked in order to recover : " + newID.name);
4774 // create the identity definition transaction
4775 std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4778 CReserveKey reserveKey(pwalletMain);
4783 if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4785 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
4787 CMutableTransaction mtx(wtx);
4789 // add the spend of the last ID transaction output
4790 mtx.vin.push_back(idTxIn);
4792 // all of the reservation output is actually the fee offer, so zero the output
4793 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4796 CCoinsViewCache view(pcoinsTip);
4797 for (int i = 0; i < wtx.vin.size(); i++)
4800 SignatureData sigdata;
4803 if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4808 CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4810 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()));
4814 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());
4815 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());
4816 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
4818 UpdateTransaction(mtx, i, sigdata);
4821 *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4825 return EncodeHexTx(wtx);
4827 else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4829 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4831 return wtx.GetHash().GetHex();
4834 UniValue getidentity(const UniValue& params, bool fHelp)
4836 if (fHelp || params.size() != 1)
4838 throw runtime_error(
4839 "getidentity \"name\"\n"
4847 + HelpExampleCli("getidentity", "\"name@\"")
4848 + HelpExampleRpc("getidentity", "\"name@\"")
4852 CheckIdentityAPIsValid();
4854 CTxDestination idID = DecodeDestination(uni_get_str(params[0]));
4855 if (idID.which() != COptCCParams::ADDRTYPE_ID)
4857 throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity parameter must be valid friendly name or identity address: \"" + uni_get_str(params[0]) + "\"");
4864 bool canSign = false, canSpend = false, found = false;
4868 LOCK(pwalletMain->cs_wallet);
4870 std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
4871 if (pwalletMain->GetIdentity(GetDestinationID(idID), keyAndIdentity))
4874 canSign = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SIGN;
4875 canSpend = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SPEND;
4876 identity = static_cast<CIdentity>(keyAndIdentity.second);
4880 identity = CIdentity::LookupIdentity(CIdentityID(GetDestinationID(idID)), 0, &height, &idTxIn);
4882 UniValue ret(UniValue::VOBJ);
4885 if (identity.IsValid() && identity.name == CleanName(identity.name, parent, true))
4887 ret.push_back(Pair("identity", identity.ToUniValue()));
4888 ret.push_back(Pair("status", identity.IsRevoked() ? "revoked" : "active"));
4889 ret.push_back(Pair("canspendfor", canSpend));
4890 ret.push_back(Pair("cansignfor", canSign));
4891 ret.push_back(Pair("blockheight", (int64_t)height));
4892 ret.push_back(Pair("txid", idTxIn.prevout.hash.GetHex()));
4893 ret.push_back(Pair("vout", (int32_t)idTxIn.prevout.n));
4898 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Identity not found");
4902 UniValue IdentityPairToUni(const std::pair<CIdentityMapKey, CIdentityMapValue *> &identity)
4904 UniValue oneID(UniValue::VOBJ);
4906 if (identity.first.IsValid() && identity.second->IsValid())
4908 oneID.push_back(Pair("identity", identity.second->ToUniValue()));
4909 oneID.push_back(Pair("blockheight", (int64_t)identity.first.blockHeight));
4910 oneID.push_back(Pair("txid", identity.second->txid.GetHex()));
4911 if (identity.second->IsRevoked())
4913 oneID.push_back(Pair("status", "revoked"));
4914 oneID.push_back(Pair("canspendfor", 0));
4915 oneID.push_back(Pair("cansignfor", 0));
4919 oneID.push_back(Pair("status", "active"));
4920 oneID.push_back(Pair("canspendfor", bool(identity.first.flags & identity.first.CAN_SPEND)));
4921 oneID.push_back(Pair("cansignfor", bool(identity.first.flags & identity.first.CAN_SIGN)));
4927 UniValue listidentities(const UniValue& params, bool fHelp)
4929 if (fHelp || params.size() > 3)
4931 throw runtime_error(
4932 "listidentities (includecansign) (includewatchonly)\n"
4936 " \"includecanspend\" (bool, optional, default=true) Include identities for which we can spend/authorize\n"
4937 " \"includecansign\" (bool, optional, default=true) Include identities that we can only sign for but not spend\n"
4938 " \"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"
4943 + HelpExampleCli("listidentities", "\'{\"name\" : \"myname\"}\'")
4944 + HelpExampleRpc("listidentities", "\'{\"name\" : \"myname\"}\'")
4948 CheckIdentityAPIsValid();
4950 std::vector<std::pair<CIdentityMapKey, CIdentityMapValue *>> mine, imsigner, notmine;
4952 bool includeCanSpend = params.size() > 0 ? uni_get_bool(params[0], true) : true;
4953 bool includeCanSign = params.size() > 1 ? uni_get_bool(params[1], true) : true;
4954 bool includeWatchOnly = params.size() > 2 ? uni_get_bool(params[2], false) : false;
4956 if (pwalletMain->GetIdentities(mine, imsigner, notmine))
4958 UniValue ret(UniValue::VARR);
4959 if (includeCanSpend)
4961 for (auto identity : mine)
4964 if (identity.second->IsValid() && identity.second->name == CleanName(identity.second->name, parent, true))
4966 ret.push_back(IdentityPairToUni(identity));
4972 for (auto identity : imsigner)
4975 if (identity.second->IsValid() && identity.second->name == CleanName(identity.second->name, parent, true))
4977 ret.push_back(IdentityPairToUni(identity));
4981 if (includeWatchOnly)
4983 for (auto identity : notmine)
4986 if (identity.second->IsValid() && identity.second->name == CleanName(identity.second->name, parent, true))
4988 ret.push_back(IdentityPairToUni(identity));
4996 return NullUniValue;
5000 UniValue addmergedblock(const UniValue& params, bool fHelp)
5002 if (fHelp || params.size() != 5)
5004 throw runtime_error(
5005 "addmergedblock \"hexdata\" ( \"jsonparametersobject\" )\n"
5006 "\nAdds a fully prepared block and its header to the current merge mining queue of this daemon.\n"
5007 "Parameters determine the action to take if adding this block would exceed the available merge mining slots.\n"
5008 "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"
5011 "1. \"hexdata\" (string, required) the hex-encoded, complete, unsolved block data to add. nTime, and nSolution are replaced.\n"
5012 "2. \"name\" (string, required) chain name symbol\n"
5013 "3. \"rpchost\" (string, required) host address for RPC connection\n"
5014 "4. \"rpcport\" (int, required) port address for RPC connection\n"
5015 "5. \"userpass\" (string, required) credentials for login to RPC\n"
5018 "\"deserialize-invalid\" - block could not be deserialized and was rejected as invalid\n"
5019 "\"blocksfull\" - block did not exceed others in estimated ROI, and there was no room for an additional merge mined block\n"
5022 + HelpExampleCli("addmergedblock", "\"hexdata\" \'{\"chainid\" : \"hexstring\", \"rpchost\" : \"127.0.0.1\", \"rpcport\" : portnum}\'")
5023 + HelpExampleRpc("addmergedblock", "\"hexdata\" \'{\"chainid\" : \"hexstring\", \"rpchost\" : \"127.0.0.1\", \"rpcport\" : portnum, \"estimatedroi\" : (verusreward/hashrate)}\'")
5027 CheckPBaaSAPIsValid();
5029 // check to see if we should replace any existing block or add a new one. if so, add this to the merge mine vector
5030 string name = params[1].get_str();
5033 throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide chain name to merge mine");
5036 string rpchost = params[2].get_str();
5037 int32_t rpcport = params[3].get_int();
5038 string rpcuserpass = params[4].get_str();
5040 if (rpchost == "" || rpcport == 0 || rpcuserpass == "")
5042 throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide valid RPC connection parameters to merge mine");
5045 uint160 chainID = CCrossChainRPCData::GetChainID(name);
5047 // confirm data from blockchain
5048 CRPCChainData chainData;
5049 CPBaaSChainDefinition chainDef;
5050 if (ConnectedChains.GetChainInfo(chainID, chainData))
5052 chainDef = chainData.chainDefinition;
5055 if (!chainDef.IsValid() && !GetChainDefinition(name, chainDef))
5057 throw JSONRPCError(RPC_INVALID_PARAMETER, "chain not found");
5062 if (!DecodeHexBlk(blk, params[0].get_str()))
5063 return "deserialize-invalid";
5065 CPBaaSMergeMinedChainData blkData = CPBaaSMergeMinedChainData(chainDef, rpchost, rpcport, rpcuserpass, blk);
5067 return ConnectedChains.AddMergedBlock(blkData) ? NullUniValue : "blocksfull";
5070 UniValue submitmergedblock(const UniValue& params, bool fHelp)
5072 if (fHelp || params.size() < 1 || params.size() > 2)
5073 throw runtime_error(
5074 "submitmergedblock \"hexdata\" ( \"jsonparametersobject\" )\n"
5075 "\nAttempts to submit one more more new blocks to one or more networks.\n"
5076 "Each merged block submission may be valid for Verus and/or up to 8 merge mined chains.\n"
5077 "The submitted block consists of a valid block for this chain, along with embedded headers of up to 8 other chains.\n"
5078 "If the hash for this header meets targets of other chains that have been added with 'addmergedblock', this API will\n"
5079 "submit those blocks to the specified URL endpoints with an RPC 'submitblock' request."
5080 "\nAttempts to submit one more more new blocks to one or more networks.\n"
5081 "The 'jsonparametersobject' parameter is currently ignored.\n"
5082 "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
5085 "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n"
5086 "2. \"jsonparametersobject\" (string, optional) object of optional parameters\n"
5088 " \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n"
5091 "\"duplicate\" - node already has valid copy of block\n"
5092 "\"duplicate-invalid\" - node already has block, but it is invalid\n"
5093 "\"duplicate-inconclusive\" - node already has block but has not validated it\n"
5094 "\"inconclusive\" - node has not validated the block, it may not be on the node's current best chain\n"
5095 "\"rejected\" - block was rejected as invalid\n"
5096 "For more information on submitblock parameters and results, see: https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki#block-submission\n"
5098 + HelpExampleCli("submitblock", "\"mydata\"")
5099 + HelpExampleRpc("submitblock", "\"mydata\"")
5102 CheckPBaaSAPIsValid();
5105 //LogPrintStr("Hex block submission: " + params[0].get_str());
5106 if (!DecodeHexBlk(block, params[0].get_str()))
5107 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
5109 uint256 hash = block.GetHash();
5110 bool fBlockPresent = false;
5113 BlockMap::iterator mi = mapBlockIndex.find(hash);
5114 if (mi != mapBlockIndex.end()) {
5115 CBlockIndex *pindex = mi->second;
5118 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
5120 if (pindex->nStatus & BLOCK_FAILED_MASK)
5121 return "duplicate-invalid";
5122 // Otherwise, we might only have the header - process the block before returning
5123 fBlockPresent = true;
5128 CValidationState state;
5129 submitblock_StateCatcher sc(block.GetHash());
5130 RegisterValidationInterface(&sc);
5131 //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());
5132 bool fAccepted = ProcessNewBlock(1, chainActive.LastTip()->GetHeight()+1, state, Params(), NULL, &block, true, NULL);
5133 UnregisterValidationInterface(&sc);
5136 if (fAccepted && !sc.found)
5137 return "duplicate-inconclusive";
5143 return "inconclusive";
5146 return BIP22ValidationResult(state);
5149 UniValue getmergedblocktemplate(const UniValue& params, bool fHelp)
5151 if (fHelp || params.size() > 1)
5152 throw runtime_error(
5153 "getblocktemplate ( \"jsonrequestobject\" )\n"
5154 "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
5155 "It returns data needed to construct a block to work on.\n"
5156 "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
5159 "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n"
5161 " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n"
5162 " \"capabilities\":[ (array, optional) A list of strings\n"
5163 " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
5171 " \"version\" : n, (numeric) The block version\n"
5172 " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
5173 " \"finalsaplingroothash\" : \"xxxx\", (string) The hash of the final sapling root\n"
5174 " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
5176 " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
5177 " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n"
5178 " \"depends\" : [ (array) array of numbers \n"
5179 " 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"
5182 " \"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"
5183 " \"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"
5184 " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n"
5188 // " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
5189 // " \"flags\" : \"flags\" (string) \n"
5191 // " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
5192 " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
5193 " \"target\" : \"xxxx\", (string) The hash target\n"
5194 " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
5195 " \"mutable\" : [ (array of string) list of ways the block template may be changed \n"
5196 " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
5199 " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n"
5200 " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n"
5201 " \"sizelimit\" : n, (numeric) limit of block size\n"
5202 " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
5203 " \"bits\" : \"xxx\", (string) compressed target of next block\n"
5204 " \"height\" : n (numeric) The height of the next block\n"
5208 + HelpExampleCli("getblocktemplate", "")
5209 + HelpExampleRpc("getblocktemplate", "")
5212 CheckPBaaSAPIsValid();
5216 // Wallet or miner address is required because we support coinbasetxn
5217 if (GetArg("-mineraddress", "").empty()) {
5218 #ifdef ENABLE_WALLET
5220 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
5223 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "verusd compiled without wallet and -mineraddress not set");
5227 std::string strMode = "template";
5228 UniValue lpval = NullUniValue;
5229 // TODO: Re-enable coinbasevalue once a specification has been written
5230 bool coinbasetxn = true;
5231 if (params.size() > 0)
5233 const UniValue& oparam = params[0].get_obj();
5234 const UniValue& modeval = find_value(oparam, "mode");
5235 if (modeval.isStr())
5236 strMode = modeval.get_str();
5237 else if (modeval.isNull())
5242 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
5243 lpval = find_value(oparam, "longpollid");
5245 if (strMode == "proposal")
5247 const UniValue& dataval = find_value(oparam, "data");
5248 if (!dataval.isStr())
5249 throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
5252 if (!DecodeHexBlk(block, dataval.get_str()))
5253 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
5255 uint256 hash = block.GetHash();
5256 BlockMap::iterator mi = mapBlockIndex.find(hash);
5257 if (mi != mapBlockIndex.end()) {
5258 CBlockIndex *pindex = mi->second;
5261 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
5263 if (pindex->nStatus & BLOCK_FAILED_MASK)
5264 return "duplicate-invalid";
5266 return "duplicate-inconclusive";
5269 CBlockIndex* const pindexPrev = chainActive.LastTip();
5270 // TestBlockValidity only supports blocks built on the current Tip
5271 if (block.hashPrevBlock != pindexPrev->GetBlockHash())
5272 return "inconclusive-not-best-prevblk";
5273 CValidationState state;
5274 TestBlockValidity(state, Params(), block, pindexPrev, false, true);
5275 return BIP22ValidationResult(state);
5279 if (strMode != "template")
5280 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
5285 fvNodesEmpty = vNodes.empty();
5287 if (Params().MiningRequiresPeers() && (IsNotInSync() || fvNodesEmpty))
5289 throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Cannot get a block template while no peers are connected or chain not in sync!");
5292 //if (IsInitialBlockDownload())
5293 // throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks...");
5295 static unsigned int nTransactionsUpdatedLast;
5297 if (!lpval.isNull())
5299 // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
5300 uint256 hashWatchedChain;
5301 boost::system_time checktxtime;
5302 unsigned int nTransactionsUpdatedLastLP;
5306 // Format: <hashBestChain><nTransactionsUpdatedLast>
5307 std::string lpstr = lpval.get_str();
5309 hashWatchedChain.SetHex(lpstr.substr(0, 64));
5310 nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
5314 // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
5315 hashWatchedChain = chainActive.LastTip()->GetBlockHash();
5316 nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
5319 // Release the wallet and main lock while waiting
5320 LEAVE_CRITICAL_SECTION(cs_main);
5322 checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
5324 boost::unique_lock<boost::mutex> lock(csBestBlock);
5325 while (chainActive.LastTip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
5327 if (!cvBlockChange.timed_wait(lock, checktxtime))
5329 // Timeout: Check transactions for update
5330 if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
5332 checktxtime += boost::posix_time::seconds(10);
5336 ENTER_CRITICAL_SECTION(cs_main);
5338 if (!IsRPCRunning())
5339 throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
5340 // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
5344 static CBlockIndex* pindexPrev;
5345 static int64_t nStart;
5346 static CBlockTemplate* pblocktemplate;
5347 if (pindexPrev != chainActive.LastTip() ||
5348 (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
5350 // Clear pindexPrev so future calls make a new block, despite any failures from here on
5353 // Store the pindexBest used before CreateNewBlockWithKey, to avoid races
5354 nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
5355 CBlockIndex* pindexPrevNew = chainActive.LastTip();
5361 delete pblocktemplate;
5362 pblocktemplate = NULL;
5364 #ifdef ENABLE_WALLET
5365 CReserveKey reservekey(pwalletMain);
5366 pblocktemplate = CreateNewBlockWithKey(reservekey,chainActive.LastTip()->GetHeight()+1,KOMODO_MAXGPUCOUNT);
5368 pblocktemplate = CreateNewBlockWithKey();
5370 if (!pblocktemplate)
5371 throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory or no available utxo for staking");
5373 // Need to update only after we know CreateNewBlockWithKey succeeded
5374 pindexPrev = pindexPrevNew;
5376 CBlock* pblock = &pblocktemplate->block; // pointer for convenience
5379 UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
5380 pblock->nNonce = uint256();
5382 UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
5384 UniValue txCoinbase = NullUniValue;
5385 UniValue transactions(UniValue::VARR);
5386 map<uint256, int64_t> setTxIndex;
5388 BOOST_FOREACH (const CTransaction& tx, pblock->vtx) {
5389 uint256 txHash = tx.GetHash();
5390 setTxIndex[txHash] = i++;
5392 if (tx.IsCoinBase() && !coinbasetxn)
5395 UniValue entry(UniValue::VOBJ);
5397 entry.push_back(Pair("data", EncodeHexTx(tx)));
5399 entry.push_back(Pair("hash", txHash.GetHex()));
5401 UniValue deps(UniValue::VARR);
5402 BOOST_FOREACH (const CTxIn &in, tx.vin)
5404 if (setTxIndex.count(in.prevout.hash))
5405 deps.push_back(setTxIndex[in.prevout.hash]);
5407 entry.push_back(Pair("depends", deps));
5409 int index_in_template = i - 1;
5410 entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
5411 entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
5413 if (tx.IsCoinBase()) {
5414 // Show founders' reward if it is required
5415 //if (pblock->vtx[0].vout.size() > 1) {
5416 // Correct this if GetBlockTemplate changes the order
5417 // entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue));
5419 CAmount nReward = GetBlockSubsidy(chainActive.LastTip()->GetHeight()+1, Params().GetConsensus());
5420 entry.push_back(Pair("coinbasevalue", nReward));
5421 entry.push_back(Pair("required", true));
5424 transactions.push_back(entry);
5427 UniValue aux(UniValue::VOBJ);
5428 aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
5430 arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
5432 static UniValue aMutable(UniValue::VARR);
5433 if (aMutable.empty())
5435 aMutable.push_back("time");
5436 aMutable.push_back("transactions");
5437 aMutable.push_back("prevblock");
5440 UniValue result(UniValue::VOBJ);
5441 result.push_back(Pair("capabilities", aCaps));
5442 result.push_back(Pair("version", pblock->nVersion));
5443 result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
5444 result.push_back(Pair("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex()));
5445 result.push_back(Pair("transactions", transactions));
5447 assert(txCoinbase.isObject());
5448 result.push_back(Pair("coinbasetxn", txCoinbase));
5450 result.push_back(Pair("coinbaseaux", aux));
5451 result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
5453 result.push_back(Pair("longpollid", chainActive.LastTip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
5454 if ( ASSETCHAINS_STAKED != 0 )
5456 arith_uint256 POWtarget; int32_t PoSperc;
5457 POWtarget = komodo_PoWtarget(&PoSperc,hashTarget,(int32_t)(pindexPrev->GetHeight()+1),ASSETCHAINS_STAKED);
5458 result.push_back(Pair("target", POWtarget.GetHex()));
5459 result.push_back(Pair("PoSperc", (int64_t)PoSperc));
5460 result.push_back(Pair("ac_staked", (int64_t)ASSETCHAINS_STAKED));
5461 result.push_back(Pair("origtarget", hashTarget.GetHex()));
5462 } else result.push_back(Pair("target", hashTarget.GetHex()));
5463 result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
5464 result.push_back(Pair("mutable", aMutable));
5465 result.push_back(Pair("noncerange", "00000000ffffffff"));
5466 result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
5467 result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
5468 result.push_back(Pair("curtime", pblock->GetBlockTime()));
5469 result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
5470 result.push_back(Pair("height", (int64_t)(pindexPrev->GetHeight()+1)));
5472 //fprintf(stderr,"return complete template\n");
5476 static const CRPCCommand commands[] =
5477 { // category name actor (function) okSafeMode
5478 // --------------------- ------------------------ ----------------------- ----------
5479 { "identity", "registernamecommitment", ®isternamecommitment, true },
5480 { "identity", "registeridentity", ®isteridentity, true },
5481 { "identity", "updateidentity", &updateidentity, true },
5482 { "identity", "revokeidentity", &revokeidentity, true },
5483 { "identity", "recoveridentity", &recoveridentity, true },
5484 { "identity", "getidentity", &getidentity, true },
5485 { "identity", "listidentities", &listidentities, true },
5486 { "multichain", "definechain", &definechain, true },
5487 { "multichain", "getdefinedchains", &getdefinedchains, true },
5488 { "multichain", "getchaindefinition", &getchaindefinition, true },
5489 { "multichain", "getnotarizationdata", &getnotarizationdata, true },
5490 { "multichain", "getcrossnotarization", &getcrossnotarization, true },
5491 { "multichain", "submitacceptednotarization", &submitacceptednotarization, true },
5492 { "multichain", "paynotarizationrewards", &paynotarizationrewards, true },
5493 { "multichain", "getinitialcurrencystate", &getinitialcurrencystate, true },
5494 { "multichain", "getcurrencystate", &getcurrencystate, true },
5495 { "multichain", "sendreserve", &sendreserve, true },
5496 { "multichain", "getpendingchaintransfers", &getpendingchaintransfers, true },
5497 { "multichain", "getchainexports", &getchainexports, true },
5498 { "multichain", "getchainimports", &getchainimports, true },
5499 { "multichain", "reserveexchange", &reserveexchange, true },
5500 { "multichain", "getlatestimportsout", &getlatestimportsout, true },
5501 { "multichain", "getlastimportin", &getlastimportin, true },
5502 { "multichain", "refundfailedlaunch", &refundfailedlaunch, true },
5503 { "multichain", "refundfailedlaunch", &refundfailedlaunch, true },
5504 { "multichain", "getmergedblocktemplate", &getmergedblocktemplate, true },
5505 { "multichain", "addmergedblock", &addmergedblock, true }
5508 void RegisterPBaaSRPCCommands(CRPCTable &tableRPC)
5510 for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
5511 tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);