]> Git Repo - VerusCoin.git/blob - src/rpc/pbaasrpc.cpp
Merge branch 'dev' of https://github.com/miketout/VerusCoin into dev
[VerusCoin.git] / src / rpc / pbaasrpc.cpp
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.
6
7 #include "amount.h"
8 #include "chainparams.h"
9 #include "consensus/consensus.h"
10 #include "consensus/validation.h"
11 #include "core_io.h"
12 #ifdef ENABLE_MINING
13 #include "crypto/equihash.h"
14 #endif
15 #include "init.h"
16 #include "main.h"
17 #include "metrics.h"
18 #include "miner.h"
19 #include "net.h"
20 #include "pow.h"
21 #include "rpc/server.h"
22 #include "txmempool.h"
23 #include "util.h"
24 #include "validationinterface.h"
25 #ifdef ENABLE_WALLET
26 #include "wallet/wallet.h"
27 #endif
28 #include "timedata.h"
29
30 #include <stdint.h>
31
32 #include <boost/assign/list_of.hpp>
33
34 #include <univalue.h>
35
36 #include "rpc/pbaasrpc.h"
37 #include "pbaas/crosschainrpc.h"
38 #include "pbaas/identity.h"
39 #include "transaction_builder.h"
40
41 using namespace std;
42
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;
54
55 arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
56
57 std::set<uint160> ClosedPBaaSChains({CCrossChainRPCData::GetChainID("RESERVEWITHPREMINE"), CCrossChainRPCData::GetChainID("FAILCHAIN"), CCrossChainRPCData::GetChainID("SLC"), CCrossChainRPCData::GetChainID("RNPM"), CCrossChainRPCData::GetChainID("RNPNMR"), CCrossChainRPCData::GetChainID("MONKINS")});
58
59 // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
60 static UniValue BIP22ValidationResult(const CValidationState& state)
61 {
62     if (state.IsValid())
63         return NullUniValue;
64
65     std::string strRejectReason = state.GetRejectReason();
66     if (state.IsError())
67         throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
68     if (state.IsInvalid())
69     {
70         if (strRejectReason.empty())
71             return "rejected";
72         return strRejectReason;
73     }
74     // Should be impossible
75     return "valid?";
76 }
77
78 class submitblock_StateCatcher : public CValidationInterface
79 {
80 public:
81     uint256 hash;
82     bool found;
83     CValidationState state;
84
85     submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {};
86
87 protected:
88     virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
89         if (block.GetHash() != hash)
90             return;
91         found = true;
92         state = stateIn;
93     };
94 };
95
96 bool GetChainDefinition(uint160 chainID, CPBaaSChainDefinition &chainDef, int32_t *pDefHeight)
97 {
98     LOCK(cs_main);
99     if (chainID == ConnectedChains.ThisChain().GetChainID())
100     {
101         chainDef = ConnectedChains.ThisChain();
102         if (pDefHeight)
103         {
104             *pDefHeight = 0;
105         }
106         return true;
107     }
108
109     if (!IsVerusActive())
110     {
111         if (ConnectedChains.NotaryChain().IsValid() && (chainID == ConnectedChains.NotaryChain().chainDefinition.GetChainID()))
112         {
113             chainDef = ConnectedChains.NotaryChain().chainDefinition;
114             if (pDefHeight)
115             {
116                 *pDefHeight = 0;
117             }
118             return true;
119         }
120     }
121
122     std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
123     bool found = false;
124
125     if (!ClosedPBaaSChains.count(chainID)  && GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, addressIndex))
126     {
127         for (auto txidx : addressIndex)
128         {
129             CTransaction tx;
130             uint256 blkHash;
131             if (GetTransaction(txidx.first.txhash, tx, blkHash))
132             {
133                 chainDef = CPBaaSChainDefinition(tx);
134                 if (found = chainDef.IsValid() && chainDef.GetChainID() == chainID)
135                 {
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
138                     if (pDefHeight)
139                     {
140                         auto it = mapBlockIndex.find(blkHash);
141                         *pDefHeight = it->second->GetHeight();
142                     }
143                     break;
144                 }
145             }
146         }
147     }
148     return found;
149 }
150
151 bool GetChainDefinition(string &name, CPBaaSChainDefinition &chainDef)
152 {
153     return GetChainDefinition(CCrossChainRPCData::GetChainID(name), chainDef);
154 }
155
156 void GetDefinedChains(vector<CPBaaSChainDefinition> &chains, bool includeExpired)
157 {
158     CCcontract_info CC;
159     CCcontract_info *cp;
160
161     std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
162
163     if (GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_PBAASDEFINITION)), 1, addressIndex))
164     {
165         for (auto txidx : addressIndex)
166         {
167             CTransaction tx;
168             uint256 blkHash;
169             if (GetTransaction(txidx.first.txhash, tx, blkHash))
170             {
171                 chains.push_back(CPBaaSChainDefinition(tx));
172
173                 UniValue valStr(UniValue::VSTR);
174
175                 // TODO: remove/comment this if statement, as it is redundant with the one below
176                 if (!valStr.read(chains.back().ToUniValue().write()))
177                 {
178                     printf("Invalid characters in blockchain definition: %s\n", chains.back().ToUniValue().write().c_str());
179                 }
180
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()))
183                 {
184                     chains.pop_back();
185                 }
186             }
187         }
188     }
189 }
190
191 bool CConnectedChains::GetLastImport(const uint160 &chainID, 
192                                      CTransaction &lastImport, 
193                                      CTransaction &crossChainExport, 
194                                      CCrossChainImport &ccImport, 
195                                      CCrossChainExport &ccCrossExport)
196 {
197     CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
198
199     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
200
201     LOCK2(cs_main, mempool.cs);
202
203     // get last import from the specified chain
204     if (!GetAddressUnspent(keyID, 1, unspentOutputs))
205     {
206         return false;
207     }
208     
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;
211     COptCCParams p;
212     for (const auto &output : unspentOutputs)
213     {
214         if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
215         {
216             pOutput = &output;
217             break;
218         }
219     }
220     if (!pOutput)
221     {
222         return false;
223     }
224     uint256 hashBlk;
225     if (!myGetTransaction(pOutput->first.txhash, lastImport, hashBlk) || !(lastImport.vout.size() && lastImport.vout.back().scriptPubKey.IsOpReturn()))
226     {
227         return false;
228     }
229     ccImport = CCrossChainImport(p.vData[0]);
230     auto opRetArr = RetrieveOpRetArray(lastImport.vout.back().scriptPubKey);
231     if (!opRetArr.size() || opRetArr[0]->objectType != CHAINOBJ_TRANSACTION)
232     {
233         DeleteOpRetObjects(opRetArr);
234     }
235     else
236     {
237         crossChainExport = ((CChainObject<CTransaction> *)opRetArr[0])->object;
238         DeleteOpRetObjects(opRetArr);
239         if (!(ccCrossExport = CCrossChainExport(crossChainExport)).IsValid())
240         {
241             return false;
242         }
243     }
244     return true;
245 }
246
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)
257 {
258     uint160 chainID = chainDef.GetChainID();
259
260     CPBaaSNotarization lastConfirmed(lastConfirmedNotarization);
261     if (!lastConfirmed.IsValid() || (chainActive.LastTip() == NULL) || lastConfirmed.notarizationHeight > chainActive.LastTip()->GetHeight())
262     {
263         LogPrintf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
264         printf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
265         return false;
266     }
267
268     CCrossChainImport lastCCI(lastCrossChainImport);
269     if (!lastCCI.IsValid())
270     {
271         LogPrintf("%s: Invalid lastCrossChainImport transaction\n", __func__);
272         printf("%s: Invalid lastCrossChainImport transaction\n", __func__);
273         return false;
274     }
275
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()))
284     {
285         DeleteOpRetObjects(chainObjs);
286         LogPrintf("%s: Invalid last import tx\n", __func__);
287         printf("%s: Invalid last import tx\n", __func__);
288         return false;
289     }
290
291     bool isVerusActive = IsVerusActive();
292
293     LOCK2(cs_main, mempool.cs);
294
295     uint256 lastExportHash;
296     CTransaction lastExportTx;
297     CTransaction tx;
298     uint256 blkHash;
299     uint32_t blkHeight = 0;
300     bool found = false;
301     if (chainObjs.size())
302     {
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() && 
309             blkMapIt->second)
310         {
311             found = true;
312             blkHeight = blkMapIt->second->GetHeight();
313         }
314     }
315     else
316     {
317         std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
318
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())
322         {
323             CPBaaSChainDefinition localChainDef;
324
325             for (auto txidx : unspentOutputs)
326             {
327                 COptCCParams p;
328                 if (txidx.second.script.IsPayToCryptoCondition(p) && 
329                     p.IsValid() && 
330                     p.evalCode == EVAL_PBAASDEFINITION && 
331                     p.vData[0].size() && 
332                     (localChainDef = CPBaaSChainDefinition(p.vData[0])).IsValid() &&
333                     ((isVerusActive && localChainDef.GetChainID() == chainID) || 
334                          (!isVerusActive && localChainDef.GetChainID() == thisChain.GetChainID() && chainID == notaryChain.GetChainID())))
335                 {
336                     if (myGetTransaction(txidx.first.txhash, tx, blkHash))
337                     {
338                         CCrossChainExport ccx(tx);
339                         if (ccx.IsValid() && (isVerusActive || tx.IsCoinBase()))
340                         {
341                             found = true;
342                             lastExportTx = tx;
343                             lastExportHash = txidx.first.txhash;
344                             blkHeight = txidx.second.blockHeight;
345                         }
346                     }
347                 }
348             }
349         }
350     }
351     if (!found)
352     {
353         LogPrintf("%s: No export thread found\n", __func__);
354         printf("%s: No export thread found\n", __func__);
355         return false;
356     }
357
358     bool importToReserveChain = !isVerusActive && chainID != thisChain.GetChainID() && chainID == notaryChain.GetChainID();
359
360     // which transaction are we in this block?
361     std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
362
363     // look for new exports
364     CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
365
366     CBlockIndex *pIndex;
367
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))
370     {
371         // find this export, then check the next one that spends it and use it if still valid
372         bool found = false;
373         uint256 lastHash = lastExportHash;
374
375         // indexed by input hash
376         std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
377
378         // validate, order, and relate them with their inputs
379         for (auto utxo : addressIndex)
380         {
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;
386             COptCCParams p;
387             if (!utxo.first.spending &&
388                 !(utxo.first.txhash == lastExportHash) &&
389                 myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
390                 (ccx = CCrossChainExport(tx)).IsValid() &&
391                 ccx.numInputs &&
392                 (!tx.IsCoinBase() && 
393                 tx.vin.size() && 
394                 myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
395                 inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && 
396                 p.IsValid() && 
397                 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
398             {
399                 validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
400             }
401         }
402
403         CTransaction lastImport(lastCrossChainImport);
404         CMutableTransaction newImportTx(importTxTemplate);
405
406         for (auto aixIt = validExports.find(lastExportHash); 
407              aixIt != validExports.end(); 
408              aixIt = validExports.find(lastExportHash))
409         {
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
418
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());
421
422             CCrossChainExport ccx(aixIt->second.second);
423             lastCCI = CCrossChainImport(lastImport);
424             if (!lastCCI.IsValid() || !ccx.IsValid())
425             {
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());
428                 return false;
429             }
430
431             // if no prepared input, make one
432             if (!newImportTx.vin.size())
433             {
434                 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), 0));          // must spend output 0
435             }
436             newImportTx.vout.clear();
437             newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
438
439             // emit a reserve exchange output
440             // we will send using a reserve output, fee will be paid by converting from reserve
441             CCcontract_info CC;
442             CCcontract_info *cp;
443             CPubKey pk;
444
445             CAmount availableReserveFees = ccx.totalFees;
446             CAmount exportFees = ccx.CalculateExportFee();
447             CAmount importFees = ccx.CalculateImportFee();
448             CAmount feesOut = 0;
449
450             CReserveTransactionDescriptor rtxd;
451
452             std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
453
454             if (!rtxd.AddReserveTransferImportOutputs(chainDef, exportOutputs, newImportTx.vout))
455             {
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());
458                 // free the memory
459                 DeleteOpRetObjects(exportOutputs);
460                 return false;
461             }
462
463             // free the memory
464             DeleteOpRetObjects(exportOutputs);
465
466             // emit a crosschain import output as summary
467             cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
468
469             pk = CPubKey(ParseHex(CC.CChexstr));
470
471             if (rtxd.ReserveFees() != (ccx.totalFees - exportFees))
472             {
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);
475             }
476
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);
479
480             totalAvailableInput -= (importToReserveChain ? rtxd.reserveIn : rtxd.nativeIn);
481             newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, totalAvailableInput, pk, dests, cci);
482
483             if (totalAvailableInput < 0)
484             {
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());
487                 return false;
488             }
489
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);
494
495             // add a proof of the export transaction at the notarization height
496             CBlock block;
497             if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
498             {
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());
501                 return false;
502             }
503
504             CMerkleBranch exportProof(aixIt->second.first.index, block.GetMerkleBranch(aixIt->second.first.index));
505             auto mmrView = chainActive.GetMMV();
506             mmrView.resize(lastConfirmed.notarizationHeight);
507
508             if (mmrView.GetRoot() != lastConfirmed.mmrRoot)
509             {
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());
512                 return false;
513             }
514             else
515             {
516                 printf("%s: created import %s for export %s\n", __func__, cci.ToUniValue().write().c_str(), aixIt->second.second.GetHash().GetHex().c_str());
517             }
518
519
520             chainActive[aixIt->second.first.blockHeight]->AddMerkleProofBridge(exportProof);
521             mmrView.GetProof(exportProof, aixIt->second.first.blockHeight);
522
523             CChainObject<CCrossChainProof> exportXProof(CHAINOBJ_CROSSCHAINPROOF, CCrossChainProof(lastConfirmedNotarization.GetHash(), exportProof));
524             chainObjects.push_back(&exportXProof);
525
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)));
528
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;
536         }
537     }
538     return true;
539 }
540
541 void CheckPBaaSAPIsValid()
542 {
543     if (!chainActive.LastTip() ||
544         CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_PBAAS)
545     {
546         throw JSONRPCError(RPC_INVALID_REQUEST, "PBaaS not activated on blockchain.");
547     }
548 }
549
550 void CheckIdentityAPIsValid()
551 {
552     if (!chainActive.LastTip() ||
553         CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
554     {
555         throw JSONRPCError(RPC_INVALID_REQUEST, "Identity APIs not activated on blockchain.");
556     }
557 }
558
559 uint160 GetChainIDFromParam(const UniValue &param)
560 {
561     uint160 chainID;
562     std::string name;
563
564     if (param.isStr())
565     {
566         try
567         {
568             name = param.get_str();
569             if (name.size() > KOMODO_ASSETCHAIN_MAXLEN - 1)
570             {
571                 return chainID;
572             }
573
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)
576             {
577                 chainID.SetHex(param.get_str());
578             }
579         }
580         catch(const std::exception& e)
581         {
582         }
583
584         if (chainID.IsNull())
585         {
586             chainID = CCrossChainRPCData::GetChainID(param.get_str());
587         }
588     }
589     return chainID;
590 }
591
592 UniValue getchaindefinition(const UniValue& params, bool fHelp)
593 {
594     if (fHelp || params.size() != 1)
595     {
596         throw runtime_error(
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"
600
601             "\nArguments\n"
602             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
603
604             "\nResult:\n"
605             "  {\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"
617             "        {\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"
623             "        }\n"
624             "        \"nodes\"      : \"[obj, ..]\",  (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
625             "          [{\n"
626             "             \"nodeaddress\" : \"txid\", (string,  optional) internet, TOR, or other supported address for node\n"
627             "             \"paymentaddress\" : \"n\", (int,     optional) rewards payment address\n"
628             "           }, .. ]\n"
629             "      }\n"
630             "    \"bestnotarization\" : {\n"
631             "     }\n"
632             "    \"besttxid\" : \"txid\"\n"
633             "     }\n"
634             "    \"confirmednotarization\" : {\n"
635             "     }\n"
636             "    \"confirmedtxid\" : \"txid\"\n"
637             "  }\n"
638
639             "\nExamples:\n"
640             + HelpExampleCli("getchaindefinition", "\"chainname\"")
641             + HelpExampleRpc("getchaindefinition", "\"chainname\"")
642         );
643     }
644
645     CheckPBaaSAPIsValid();
646
647     UniValue ret(UniValue::VOBJ);
648
649     uint160 chainID = GetChainIDFromParam(params[0]);
650
651     if (chainID.IsNull())
652     {
653         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
654     }
655
656     CPBaaSChainDefinition chainDef;
657
658     LOCK(cs_main);
659
660     if (GetChainDefinition(chainID, chainDef))
661     {
662         CChainNotarizationData cnd;
663         GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
664         ret = chainDef.ToUniValue();
665
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;
669
670         ret.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
671         if (confirmedHeight != -1)
672         {
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()));
675         }
676         ret.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
677         if (bestHeight != -1)
678         {
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()));
681         }
682         return ret;
683     }
684     else
685     {
686         return NullUniValue;
687     }
688 }
689
690 UniValue getpendingchaintransfers(const UniValue& params, bool fHelp)
691 {
692     if (fHelp || params.size() > 1)
693     {
694         throw runtime_error(
695             "getpendingchaintransfers \"chainname\"\n"
696             "\nReturns all pending transfers for a particular chain that have not yet been aggregated into an export\n"
697
698             "\nArguments\n"
699             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
700
701             "\nResult:\n"
702             "  {\n"
703             "  }\n"
704
705             "\nExamples:\n"
706             + HelpExampleCli("getpendingchaintransfers", "\"chainname\"")
707             + HelpExampleRpc("getpendingchaintransfers", "\"chainname\"")
708         );
709     }
710
711     CheckPBaaSAPIsValid();
712
713     uint160 chainID = GetChainIDFromParam(params[0]);
714
715     if (chainID.IsNull())
716     {
717         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
718     }
719
720     CPBaaSChainDefinition chainDef;
721     int32_t defHeight;
722
723     LOCK(cs_main);
724
725     if ((IsVerusActive() && GetChainDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetChainID() == chainID)
726     {
727         // look for new exports
728         multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> inputDescriptors;
729
730         if (GetUnspentChainTransfers(inputDescriptors, chainID))
731         {
732             UniValue ret(UniValue::VARR);
733
734             for (auto &desc : inputDescriptors)
735             {
736                 COptCCParams p;
737                 if (desc.second.first.scriptPubKey.IsPayToCryptoCondition(p))
738                 {
739                     UniValue oneExport(UniValue::VOBJ);
740
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);
747                 }
748             }
749             if (ret.size())
750             {
751                 return ret;
752             }
753         }
754     }
755     else
756     {
757         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
758     }
759     
760     return NullUniValue;
761 }
762
763 UniValue getchainexports(const UniValue& params, bool fHelp)
764 {
765     if (fHelp || params.size() != 1)
766     {
767         throw runtime_error(
768             "getchainexports \"chainname\"\n"
769             "\nReturns all pending export transfers that are not yet provable with confirmed notarizations.\n"
770
771             "\nArguments\n"
772             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
773
774             "\nResult:\n"
775             "  {\n"
776             "  }\n"
777
778             "\nExamples:\n"
779             + HelpExampleCli("getchainexports", "\"chainname\"")
780             + HelpExampleRpc("getchainexports", "\"chainname\"")
781         );
782     }
783
784     CheckPBaaSAPIsValid();
785
786     uint160 chainID = GetChainIDFromParam(params[0]);
787
788     if (chainID.IsNull())
789     {
790         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
791     }
792
793     CPBaaSChainDefinition chainDef;
794     int32_t defHeight;
795
796     LOCK(cs_main);
797
798     if ((IsVerusActive() && GetChainDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetChainID() == chainID)
799     {
800         // which transaction are we in this block?
801         std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
802
803         // look for new exports
804         CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
805
806         CChainNotarizationData cnd;
807         if (GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd))
808         {
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))
811             {
812                 UniValue ret(UniValue::VARR);
813
814                 for (auto &idx : addressIndex)
815                 {
816                     uint256 blkHash;
817                     CTransaction exportTx;
818                     if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
819                     {
820                         std::vector<CBaseChainObject *> opretTransfers;
821                         CCrossChainExport ccx;
822                         if ((ccx = CCrossChainExport(exportTx)).IsValid() && exportTx.vout.back().scriptPubKey.IsOpReturn())
823                         {
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)
831                             {
832                                 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
833                                 {
834                                     transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
835                                 }
836                             }
837                             DeleteOpRetObjects(opretTransfers);
838                             oneExport.push_back(Pair("transfers", transferArray));
839                             ret.push_back(oneExport);
840                         }
841                     }
842                 }
843                 if (ret.size())
844                 {
845                     return ret;
846                 }
847             }
848         }
849     }
850     else
851     {
852         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
853     }
854     
855     return NullUniValue;
856 }
857
858 UniValue getchainimports(const UniValue& params, bool fHelp)
859 {
860     if (fHelp || params.size() != 1)
861     {
862         throw runtime_error(
863             "getchainimports \"chainname\"\n"
864             "\nReturns all imports from a specific chain.\n"
865
866             "\nArguments\n"
867             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
868
869             "\nResult:\n"
870             "  {\n"
871             "  }\n"
872
873             "\nExamples:\n"
874             + HelpExampleCli("getchainimports", "\"chainname\"")
875             + HelpExampleRpc("getchainimports", "\"chainname\"")
876         );
877     }
878
879     CheckPBaaSAPIsValid();
880
881     uint160 chainID = GetChainIDFromParam(params[0]);
882
883     if (chainID.IsNull())
884     {
885         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
886     }
887
888     CPBaaSChainDefinition chainDef;
889     int32_t defHeight;
890
891     LOCK(cs_main);
892
893     if ((IsVerusActive() && GetChainDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetChainID() == chainID)
894     {
895         // which transaction are we in this block?
896         std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
897
898         // look for new exports
899         CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
900
901         CBlockIndex *pIndex;
902
903         CChainNotarizationData cnd;
904         // get all export transactions including and since this one up to the confirmed height
905         if (GetAddressIndex(keyID, 1, addressIndex))
906         {
907             UniValue ret(UniValue::VARR);
908
909             for (auto &idx : addressIndex)
910             {
911                 uint256 blkHash;
912                 CTransaction importTx;
913                 if (!idx.first.spending && myGetTransaction(idx.first.txhash, importTx, blkHash))
914                 {
915                     CCrossChainImport cci;
916                     if ((cci = CCrossChainImport(importTx)).IsValid() && importTx.vout.back().scriptPubKey.IsOpReturn())
917                     {
918                         std::vector<CBaseChainObject *> opretImports;
919                         CTransaction exportTx;
920                         CCrossChainExport ccx;
921
922                         opretImports = RetrieveOpRetArray(importTx.vout.back().scriptPubKey);
923
924                         if (opretImports.size() >= 2 && 
925                             opretImports[0]->objectType == CHAINOBJ_TRANSACTION && 
926                             (ccx = CCrossChainExport(exportTx = ((CChainObject<CTransaction> *)opretImports[0])->object)).IsValid() && 
927                             ccx.numInputs &&
928                             exportTx.vout.size() && 
929                             exportTx.vout.back().scriptPubKey.IsOpReturn())
930                         {
931                             std::vector<CBaseChainObject *> opretTransfers;
932
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)
940                             {
941                                 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
942                                 {
943                                     transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
944                                 }
945                             }
946                             DeleteOpRetObjects(opretTransfers);
947                             oneImport.push_back(Pair("transfers", transferArray));
948                             ret.push_back(oneImport);
949                         }
950                         DeleteOpRetObjects(opretImports);
951                     }
952                 }
953             }
954             if (ret.size())
955             {
956                 return ret;
957             }
958         }
959     }
960     else
961     {
962         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
963     }
964     return NullUniValue;
965 }
966
967 UniValue getdefinedchains(const UniValue& params, bool fHelp)
968 {
969     if (fHelp || params.size() > 1)
970     {
971         throw runtime_error(
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"
975
976             "\nArguments\n"
977             "1. \"includeexpired\"                (bool, optional) if true, include chains that are no longer active\n"
978
979             "\nResult:\n"
980             "[\n"
981             "  {\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"
992             "    {\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"
998             "    }\n"
999             "    \"nodes\"      : \"[obj, ..]\",  (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
1000             "      [{\n"
1001             "         \"nodeaddress\" : \"txid\", (string,  optional) internet, TOR, or other supported address for node\n"
1002             "         \"paymentaddress\" : \"n\", (int,     optional) rewards payment address\n"
1003             "       }, .. ]\n"
1004             "  }, ...\n"
1005             "]\n"
1006
1007             "\nExamples:\n"
1008             + HelpExampleCli("getdefinedchains", "true")
1009             + HelpExampleRpc("getdefinedchains", "true")
1010         );
1011     }
1012
1013     CheckPBaaSAPIsValid();
1014
1015     UniValue ret(UniValue::VARR);
1016
1017     bool includeExpired = params[0].isBool() ? params[0].get_bool() : false;
1018
1019     vector<CPBaaSChainDefinition> chains;
1020     GetDefinedChains(chains, includeExpired);
1021
1022     for (auto def : chains)
1023     {
1024         UniValue oneChain(UniValue::VOBJ);
1025         oneChain.push_back(Pair("chaindefinition", def.ToUniValue()));
1026
1027         CChainNotarizationData cnd;
1028         GetNotarizationData(def.GetChainID(), IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
1029
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;
1033
1034         oneChain.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
1035         if (confirmedHeight != -1)
1036         {
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()));
1039         }
1040         oneChain.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
1041         if (bestHeight != -1)
1042         {
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()));
1045         }
1046         ret.push_back(oneChain);
1047     }
1048     return ret;
1049 }
1050
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)
1054 {
1055     if (!flags)
1056     {
1057         flags = CReserveTransfer::VALID;
1058     }
1059     bool nofilter = chainFilter.IsNull();
1060     CKeyID keyID;
1061
1062     // look for unspent chain transfer outputs for all chains
1063     keyID = CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_RESERVE_TRANSFER);
1064
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
1068
1069     LOCK2(cs_main, mempool.cs);
1070
1071     if (!GetAddressIndex(keyID, 1, addressIndex, start, end))
1072     {
1073         return false;
1074     }
1075     else
1076     {
1077         for (auto it = addressIndex.begin(); it != addressIndex.end(); it++)
1078         {
1079             CTransaction ntx;
1080             uint256 blkHash;
1081
1082             // each tx gets counted once
1083             if (countedTxes.count(it->first.txhash))
1084             {
1085                 continue;
1086             }
1087             countedTxes.insert(it->first.txhash);
1088
1089             if (myGetTransaction(it->first.txhash, ntx, blkHash))
1090             {
1091                 for (int i = 0; i < ntx.vout.size(); i++)
1092                 {
1093                     // if this is a transfer output, optionally to this chain, add it to the input vector
1094                     COptCCParams p;
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)
1102                     {
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)));
1105                     }
1106                 }
1107             }
1108             else
1109             {
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());
1112                 return false;
1113             }
1114         }
1115         return true;
1116     }
1117 }
1118
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)
1122 {
1123     bool nofilter = chainFilter.IsNull();
1124     CKeyID keyID;
1125
1126     // look for unspent chain transfer outputs for all chains
1127     keyID = CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_RESERVE_TRANSFER);
1128
1129     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1130
1131     LOCK2(cs_main, mempool.cs);
1132
1133     if (!GetAddressUnspent(keyID, 1, unspentOutputs))
1134     {
1135         return false;
1136     }
1137     else
1138     {
1139         CCoinsViewCache view(pcoinsTip);
1140
1141         for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1142         {
1143             CCoins coins;
1144
1145             if (view.GetCoins(it->first.txhash, coins))
1146             {
1147                 for (int i = 0; i < coins.vout.size(); i++)
1148                 {
1149                     if (coins.IsAvailable(i))
1150                     {
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
1153                         COptCCParams p;
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))
1159                         {
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)));
1162                         }
1163                     }
1164                 }
1165             }
1166             else
1167             {
1168                 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1169             }
1170         }
1171         return true;
1172     }
1173 }
1174
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)
1178 {
1179     // look for unspent notarization finalization outputs for the requested chain
1180     CKeyID keyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT));
1181
1182     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1183
1184     LOCK2(cs_main, mempool.cs);
1185
1186     if (!GetAddressUnspent(keyID, 1, unspentOutputs))
1187     {
1188         return false;
1189     }
1190     else
1191     {
1192         CCoinsViewCache view(pcoinsTip);
1193
1194         for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1195         {
1196             CCoins coins;
1197
1198             if (view.GetCoins(it->first.txhash, coins))
1199             {
1200                 for (int i = 0; i < coins.vout.size(); i++)
1201                 {
1202                     if (coins.IsAvailable(i))
1203                     {
1204                         // if this is an export output, optionally to this chain, add it to the input vector
1205                         COptCCParams p;
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())
1209                         {
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))))));
1212                         }
1213                     }
1214                 }
1215             }
1216             else
1217             {
1218                 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1219                 return false;
1220             }
1221         }
1222         return true;
1223     }
1224 }
1225
1226 bool GetNotarizationData(uint160 chainID, uint32_t ecode, CChainNotarizationData &notarizationData, vector<pair<CTransaction, uint256>> *optionalTxOut)
1227 {
1228     notarizationData.version = PBAAS_VERSION;
1229
1230     // look for unspent notarization finalization outputs for the requested chain
1231     CKeyID keyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZENOTARIZATION));
1232
1233     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1234     CPBaaSChainDefinition chainDef;
1235
1236     bool isEarned = ecode == EVAL_EARNEDNOTARIZATION;
1237
1238     LOCK2(cs_main, mempool.cs);
1239
1240     if (!GetAddressUnspent(keyID, 1, unspentOutputs))
1241     {
1242         return false;
1243     }
1244     else
1245     {
1246         multimap<int32_t, pair<uint256, CPBaaSNotarization>> sorted;
1247         multimap<int32_t, pair<CTransaction, uint256>> sortedTxs;
1248
1249         notarizationData.lastConfirmed = 0;
1250
1251         // filter out all transactions that do not spend from the notarization thread, or originate as the
1252         // chain definition
1253         for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1254         {
1255             // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1256             CTransaction ntx;
1257             uint256 blkHash;
1258
1259             if (myGetTransaction(it->first.txhash, ntx, blkHash))
1260             {
1261                 if (!chainDef.IsValid())
1262                 {
1263                     // try to make a chain definition out of each transaction, and keep the first one that is valid
1264                     chainDef = CPBaaSChainDefinition(ntx);
1265                 }
1266                 CPBaaSNotarization notarization = CPBaaSNotarization(ntx);
1267                 if (notarization.IsValid())
1268                 {
1269                     auto blkit = mapBlockIndex.find(blkHash);
1270                     if (blkit != mapBlockIndex.end())
1271                     {
1272                         // sort by block height, index by transaction id
1273                         sorted.insert(make_pair(blkit->second->GetHeight(), make_pair(it->first.txhash, notarization)));
1274                         if (optionalTxOut)
1275                         {
1276                             sortedTxs.insert(make_pair(blkit->second->GetHeight(), make_pair(ntx, blkHash)));
1277                         }
1278                     }
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)
1281                     {
1282                         notarizationData.lastConfirmed = -1;
1283                     }
1284                 }
1285             }
1286             else
1287             {
1288                 printf("cannot retrieve transaction %s, may need to reindex\n", it->first.txhash.GetHex().c_str());
1289                 //return false;
1290             }
1291         }
1292
1293         if (!sorted.size())
1294         {
1295             //printf("no notarizations found\n");
1296             return false;
1297         }
1298
1299         if (!chainDef.IsValid())
1300         {
1301             // the first entry of all forks must reference a confirmed transaction if there is one
1302             CTransaction rootTx;
1303             uint256 blkHash;
1304             auto prevHash = sorted.begin()->second.second.prevNotarization;
1305             if (!prevHash.IsNull())
1306             {
1307                 if (!myGetTransaction(prevHash, rootTx, blkHash))
1308                 {
1309                     return false;
1310                 }
1311
1312                 // ensure that we have a finalization output
1313                 COptCCParams p;
1314                 CPBaaSNotarization notarization;
1315                 CNotarizationFinalization finalization;
1316                 uint32_t notarizeIdx, finalizeIdx;
1317
1318                 if (GetNotarizationAndFinalization(ecode, CMutableTransaction(rootTx), notarization, &notarizeIdx, &finalizeIdx))
1319                 {
1320                     notarizationData.vtx.insert(notarizationData.vtx.begin(), make_pair(prevHash, notarization));
1321                     notarizationData.lastConfirmed = 0;
1322                     if (optionalTxOut)
1323                     {
1324                         optionalTxOut->insert(optionalTxOut->begin(), make_pair(rootTx, blkHash));
1325                     }
1326                 }
1327                 // debugging, this else is not needed
1328                 else
1329                 {
1330                     printf("previous transaction does not have both notarization and finalizaton outputs\n");
1331                 }
1332             }
1333             else
1334             {
1335                 if (!isEarned)
1336                 {
1337                     notarizationData.lastConfirmed = -1;
1338                 }
1339             }
1340         }
1341         else
1342         {
1343             if (!isEarned)
1344             {
1345                 // we still have the chain definition in our forks, so no notarization has been confirmed yet
1346                 notarizationData.lastConfirmed = -1;
1347             }
1348         }
1349
1350         multimap<uint256, pair<int32_t, int32_t>> references;       // associates the txid, the fork index, and the index in the fork
1351
1352         for (auto p : sorted)
1353         {
1354             notarizationData.vtx.push_back(make_pair(p.second.first, p.second.second));
1355         }
1356
1357         if (optionalTxOut)
1358         {
1359             for (auto p : sortedTxs)
1360             {
1361                 optionalTxOut->push_back(p.second);
1362             }
1363         }
1364
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.
1367
1368         // find roots and create a chain from each
1369         for (int32_t i = 0; i < notarizationData.vtx.size(); i++)
1370         {
1371             auto &nzp = notarizationData.vtx[i];
1372             auto it = nzp.second.prevNotarization.IsNull() ? references.end() : references.find(nzp.second.prevNotarization);
1373
1374             int32_t chainIdx = 0;
1375             int32_t posIdx = 0;
1376
1377             // do we refer to a notarization that is already in a fork?
1378             if (it != references.end())
1379             {
1380                 std::vector<int32_t> &fork = notarizationData.forks[it->second.first];
1381
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))
1384                 {
1385                     fork.push_back(i);
1386                     chainIdx = it->second.first;
1387                     posIdx = fork.size() - 1;
1388                 }
1389                 else
1390                 {
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;
1395                 }
1396             }
1397             else
1398             {
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;
1404             }
1405             references.insert(make_pair(nzp.first, make_pair(chainIdx, posIdx)));
1406         }
1407
1408         CChainPower best;
1409
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++)
1414         {
1415             CChainPower curPower = ExpandCompactPower(notarizationData.vtx[notarizationData.forks[i].back()].second.compactPower, i);
1416             if (curPower > best)
1417             {
1418                 best = curPower;
1419             }
1420         }
1421         notarizationData.bestChain = best.nHeight;
1422         return true;
1423     }
1424 }
1425
1426 UniValue getnotarizationdata(const UniValue& params, bool fHelp)
1427 {
1428     if (fHelp || params.size() < 1 || params.size() > 2)
1429     {
1430         throw runtime_error(
1431             "getnotarizationdata \"chainid\" accepted\n"
1432             "\nReturns the latest PBaaS notarization data for the specifed chainid.\n"
1433
1434             "\nArguments\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"
1438
1439             "\nResult:\n"
1440             "{\n"
1441             "  \"version\" : n,                 (numeric) The notarization protocol version\n"
1442             "}\n"
1443
1444             "\nExamples:\n"
1445             + HelpExampleCli("getnotarizationdata", "\"chainid\" true")
1446             + HelpExampleRpc("getnotarizationdata", "\"chainid\"")
1447         );
1448     }
1449
1450     CheckPBaaSAPIsValid();
1451
1452     uint160 chainID;
1453     CChainNotarizationData nData;
1454     uint32_t ecode;
1455     
1456     if (IsVerusActive())
1457     {
1458         ecode = EVAL_ACCEPTEDNOTARIZATION;
1459     }
1460     else
1461     {
1462         ecode = EVAL_EARNEDNOTARIZATION;
1463     }
1464
1465     chainID = GetChainIDFromParam(params[0]);
1466
1467     if (chainID.IsNull())
1468     {
1469         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chainid");
1470     }
1471
1472     if (params.size() > 1)
1473     {
1474         if (!params[1].get_bool())
1475         {
1476             ecode = EVAL_EARNEDNOTARIZATION;
1477         }
1478     }
1479
1480     if (GetNotarizationData(chainID, ecode, nData))
1481     {
1482         return nData.ToUniValue();
1483     }
1484     else
1485     {
1486         return NullUniValue;
1487     }
1488 }
1489
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)
1493 {
1494     CAmount retval = 0;
1495     uint160 chainID = chainDef.GetChainID();
1496
1497     // look for unspent outputs that match the addressout hashed with service code
1498     CKeyID keyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, serviceCode), EVAL_SERVICEREWARD));
1499
1500     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1501
1502     if (GetAddressUnspent(keyID, 1, unspentOutputs))
1503     {
1504         // spend all billing periods prior or equal to this one
1505         int billingPeriod = height / chainDef.billingPeriod;
1506
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
1510
1511         LOCK(cs_main);
1512         for (auto output : unspentOutputs)
1513         {
1514             // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1515             CServiceReward sr;
1516             CCoins coins;
1517             if (pcoinsTip->GetCoins(output.first.txhash, coins))
1518             {
1519                 for (auto txout : coins.vout)
1520                 {
1521                     COptCCParams p;
1522                     if (!txout.IsNull() && IsPayToCryptoCondition(txout.scriptPubKey, p) && p.evalCode == EVAL_SERVICEREWARD)
1523                     {
1524                         FromVector(p.vData[0], sr);
1525                         if (sr.IsValid())
1526                         {
1527                             inputs.push_back(CInputDescriptor(txout.scriptPubKey, txout.nValue, CTxIn(output.first.txhash, output.first.index)));
1528                             retval += txout.nValue;
1529                         }
1530                     }
1531                     else
1532                     {
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());
1535                     }
1536                 }
1537             }
1538         }
1539     }
1540     return retval;
1541 }
1542
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)
1546 {
1547     // get current chain info
1548     CAmount newIn = 0;
1549     newIn = GetUnspentRewardInputs(chainDef, inputs, SERVICE_NOTARIZATION, height);
1550     for (auto input : inputs)
1551     {
1552         mnewTx.vin.push_back(input.txIn);
1553     }
1554     return newIn;
1555 }
1556
1557 UniValue submitacceptednotarization(const UniValue& params, bool fHelp)
1558 {
1559     if (fHelp || params.size() != 1)
1560     {
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"
1565
1566             "\nArguments\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"
1569
1570             "\nResult:\n"
1571             "txid                               (hexstring) transaction ID of submitted transaction\n"
1572
1573             "\nExamples:\n"
1574             + HelpExampleCli("submitacceptednotarization", "\"hextx\"")
1575             + HelpExampleRpc("submitacceptednotarization", "\"hextx\"")
1576         );
1577     }
1578
1579     CheckPBaaSAPIsValid();
1580
1581     // decode the transaction and ensure that it is formatted as expected
1582     CTransaction notarization;
1583     CPBaaSNotarization pbn;
1584
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())
1590     {
1591         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid notarization transaction");
1592     }
1593
1594     CPBaaSChainDefinition chainDef;
1595     int32_t chainDefHeight;
1596     if (!GetChainDefinition(pbn.chainID, chainDef, &chainDefHeight))
1597     {
1598         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain notarization");
1599     }
1600
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
1604
1605     CChainNotarizationData nData;
1606     vector<pair<CTransaction, uint256>> txesBlkHashes;
1607
1608     bool success;
1609     
1610     {
1611         LOCK(cs_main);
1612         success = GetNotarizationData(pbn.chainID, EVAL_ACCEPTEDNOTARIZATION, nData, &txesBlkHashes);
1613     }
1614
1615     // get notarization data and check all transactions
1616     if (success)
1617     {
1618
1619         //LogPrintf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
1620         //printf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
1621
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;
1626
1627         // printf("opRet: %s\n", notarization.vout[notarization.vout.size() - 1].scriptPubKey.ToString().c_str());
1628
1629         auto chainObjects = RetrieveOpRetArray(notarization.vout[notarization.vout.size() - 1].scriptPubKey);
1630
1631         bool stillValid = false;
1632         if (chainObjects.size() && chainObjects.back()->objectType == CHAINOBJ_PRIORBLOCKS)
1633         {
1634             // once here, default to true
1635             stillValid = true;
1636
1637             CPriorBlocksCommitment &pbc = ((CChainObject<CPriorBlocksCommitment> *)chainObjects.back())->object;
1638             for (auto prior : pbc.priorBlocks)
1639             {
1640                 priorBlocks.insert(prior);
1641             }
1642
1643             for (auto it = nData.vtx.rbegin(); it != nData.vtx.rend(); it++)
1644             {
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))
1647                 {
1648                     stillValid = false;
1649                     break;
1650                 }
1651                 else
1652                 {
1653                     notarizationData.insert(make_pair(it->first, &it->second));
1654                 }
1655             }
1656         }
1657
1658         DeleteOpRetObjects(chainObjects);
1659
1660         auto lastIt = notarizationData.find(pbn.prevNotarization);
1661
1662         if (!stillValid || (lastIt == notarizationData.end()))
1663         {
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");
1666         }
1667
1668         if (pbn.prevHeight != lastIt->second->notarizationHeight)
1669         {
1670             printf("Notarization heights not matched with previous notarization\n");
1671             throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization heights not matched with previous notarization");
1672         }
1673
1674         if (pbn.prevHeight != 0 && (pbn.prevHeight + CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED > pbn.notarizationHeight))
1675         {
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");
1678         }
1679
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)
1686         {
1687             mnewTx.vin.push_back(input);
1688         }
1689         for (auto output : notarization.vout)
1690         {
1691             mnewTx.vout.push_back(output);
1692         }
1693
1694         CTransaction lastTx = txesBlkHashes.back().first;
1695
1696         int32_t confirmedInput = -1;
1697         int32_t confirmedIndex;
1698         CTxDestination payee;
1699
1700         uint32_t notarizationIdx = -1, finalizationIdx = -1;
1701         CPBaaSNotarization dummy;
1702
1703         notarizationInputs = AddSpendsAndFinalizations(nData, pbn.prevNotarization, mnewTx, &confirmedInput, &confirmedIndex, &payee);
1704
1705         // if we got our inputs, add finalization
1706         if (notarizationInputs.size())
1707         {
1708             CCcontract_info CC;
1709             CCcontract_info *cp;
1710             cp = CCinit(&CC, EVAL_FINALIZENOTARIZATION);
1711
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});
1716
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))});
1721
1722             CNotarizationFinalization nf(confirmedInput);
1723
1724             mnewTx.vout.insert(mnewTx.vout.begin() + (mnewTx.vout.size() - 1), MakeCC1of1Vout(EVAL_FINALIZENOTARIZATION, CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE, pk, dests, nf));
1725         }
1726
1727         if (notarizationInputs.size() && GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, mnewTx, dummy, &notarizationIdx, &finalizationIdx))
1728         {
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;
1736             uint256 hashBlock;
1737             CBlock confirmedBlock;
1738
1739             CAmount valueIn;
1740
1741             BlockMap::iterator it;
1742             {
1743                 LOCK2(cs_main, mempool.cs);
1744                 // get value in
1745                 CCoinsViewCache view(pcoinsTip);
1746                 int64_t dummyInterest;
1747                 valueIn = view.GetValueIn(chainActive.LastTip()->GetHeight(), &dummyInterest, newTx, chainActive.LastTip()->nTime);
1748
1749                 if (!valueIn)
1750                 {
1751                     throw JSONRPCError(RPC_TRANSACTION_REJECTED, "unable to spend necessary transaction outputs");
1752                 }
1753
1754                 if (confirmedInput != -1)
1755                 {
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())
1760                     {
1761                         pindex = mapBlockIndex.find(hashBlock)->second;
1762                     }
1763
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
1766                     if (pindex)
1767                     {
1768                         valueIn += AddNewNotarizationRewards(chainDef, notarizationInputs, mnewTx, pindex->GetHeight());
1769                     }
1770                     else
1771                     {
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());
1774                     }
1775                 }
1776             }
1777
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;
1782
1783             // get recipients of any reward output
1784             if (confirmedInput != -1)
1785             {
1786                 LOCK(cs_main);
1787                 if (pindex && ReadBlockFromDisk(confirmedBlock, pindex, Params().GetConsensus()) && 
1788                     (confirmedPBN = CPBaaSNotarization(confirmedTx)).IsValid() &&
1789                     ExtractDestination(confirmedBlock.vtx[0].vout[0].scriptPubKey, minerRecipient, false))
1790                 {
1791                     notaryRecipient = CTxDestination(CKeyID(confirmedPBN.notaryKeyID));
1792                 }
1793                 else
1794                 {
1795                     throw JSONRPCError(RPC_DATABASE_ERROR, "unable to retrieve confirmed notarization data");
1796                 }
1797             }
1798
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;
1803
1804             if (valueIn > (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1))
1805             {
1806                 if (confirmedInput != -1)
1807                 {
1808                     if (valueIn < (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1)))
1809                     {
1810                         valueOut = valueIn - (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1);
1811                     }
1812                     else
1813                     {
1814                         valueOut = (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (valueIn - (CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1))) / blocksLeft;
1815                     }
1816                 }
1817                 notaryValueOut = valueIn - ((CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE << 1) + valueOut);
1818             }
1819             else
1820             {
1821                 notaryValueOut = 0;
1822             }
1823
1824             if (notaryValueOut >= (PBAAS_MINNOTARIZATIONOUTPUT << 1))
1825             {
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
1832                 // 3) op_ret
1833                 //
1834                 // send:
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);
1838                 if (valueOut)
1839                 {
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)));
1844                 }
1845             }
1846             else
1847             {
1848                 notaryValueOut += valueOut;
1849                 valueOut = 0;
1850             }
1851             
1852             if ((notaryValueOut + valueOut + CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE) > valueIn)
1853             {
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());
1857             }
1858
1859             CCcontract_info CC;
1860             CCcontract_info *cp;
1861
1862             // make the output for the other chain's notarization
1863             cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1864
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});
1869
1870             mnewTx.vout[notarizationIdx] = MakeCC1of1Vout(EVAL_ACCEPTEDNOTARIZATION, notaryValueOut, pk, dests, pbn);
1871
1872             CTransaction ntx(mnewTx);
1873
1874             uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
1875
1876             // sign the transaction and submit
1877             for (int i = 0; i < ntx.vin.size(); i++)
1878             {
1879                 bool signSuccess;
1880                 SignatureData sigdata;
1881                 CAmount value;
1882                 const CScript *pScriptPubKey;
1883
1884                 // if this is our coinbase input, we won't find it elsewhere
1885                 if (i < notarizationInputs.size())
1886                 {
1887                     pScriptPubKey = &notarizationInputs[i].scriptPubKey;
1888                     value = notarizationInputs[i].nValue;
1889
1890                     signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, value, SIGHASH_ALL), *pScriptPubKey, sigdata, consensusBranchId);
1891
1892                     if (!signSuccess)
1893                     {
1894                         fprintf(stderr,"submitacceptednotarization: failure to sign accepted notarization\n");
1895                         throw JSONRPCError(RPC_VERIFY_ERROR, "Failed to sign notarizaton for " + chainDef.name);
1896                     } else {
1897                         UpdateTransaction(mnewTx, i, sigdata);
1898                     }
1899                 }
1900             }
1901
1902             // add to mempool and submit transaction
1903             CTransaction tx(mnewTx);
1904
1905             CValidationState state;
1906             bool fMissingInputs;
1907             bool accepted;
1908             {
1909                 LOCK2(cs_main, mempool.cs);
1910                 accepted = AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs);
1911             }
1912             if (!accepted) {
1913                 if (state.GetRejectReason() != "")
1914                 {
1915                     printf("Cannot enter notarization into mempool for chain %s, %s\n", chainDef.name.c_str(), state.GetRejectReason().c_str());
1916                 }
1917                 if (state.IsInvalid()) {
1918                     throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
1919                 } else {
1920                     if (fMissingInputs) {
1921                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
1922                     }
1923                     throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
1924                 }
1925             }
1926             else
1927             {
1928                 RelayTransaction(tx);
1929             }
1930
1931             return newTx.GetHash().GetHex();
1932         }
1933     }
1934     throw JSONRPCError(RPC_VERIFY_REJECTED, "Failed to get notarizaton data for chainID: " + pbn.chainID.GetHex());
1935 }
1936
1937 UniValue getcrossnotarization(const UniValue& params, bool fHelp)
1938 {
1939     if (fHelp || params.size() < 2 || params.size() > 3)
1940     {
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"
1944
1945             "\nArguments\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"
1950
1951             "\nResult:\n"
1952             "{\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"
1957             "}\n"
1958
1959             "\nExamples:\n"
1960             + HelpExampleCli("getcrossnotarization", "\"chainid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
1961             + HelpExampleRpc("getcrossnotarization", "\"chainid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
1962         );
1963     }
1964
1965     uint160 chainID;
1966     uint32_t ecode;
1967     UniValue ret(UniValue::VOBJ);
1968
1969     if (IsVerusActive())
1970     {
1971         ecode = EVAL_ACCEPTEDNOTARIZATION;
1972     }
1973     else
1974     {
1975         ecode = EVAL_EARNEDNOTARIZATION;
1976     }
1977
1978     if (params[0].type() == UniValue::VSTR)
1979     {
1980         try
1981         {
1982             chainID.SetHex(params[0].get_str());
1983         }
1984         catch(const std::exception& e)
1985         {
1986         }
1987     }
1988
1989     if (chainID.IsNull())
1990     {
1991         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chainid");
1992     }
1993
1994     if (params.size() > 2)
1995     {
1996         if (!params[2].get_bool())
1997         {
1998             ecode = EVAL_EARNEDNOTARIZATION;
1999         }
2000     }
2001
2002     uint32_t crosscode;
2003     if (ecode == EVAL_ACCEPTEDNOTARIZATION)
2004     {
2005         crosscode = EVAL_EARNEDNOTARIZATION;
2006     }
2007     else
2008     {
2009         crosscode = EVAL_ACCEPTEDNOTARIZATION;
2010     }
2011
2012     if (params[1].type() != UniValue::VARR)
2013     {
2014         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid second parameter object type: " + itostr(params[1].type()));
2015     }
2016
2017     vector<UniValue> values = params[1].getValues();
2018     set<uint256> txids;
2019     for (int32_t i = 0; i < values.size(); i++)
2020     {
2021         auto txid = uint256S(values[i].get_str());
2022         if (txid.IsNull())
2023         {
2024             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter for notarization ID: " + values[i].get_str());
2025         }
2026         txids.insert(txid);
2027     }
2028
2029     CChainNotarizationData nData;
2030
2031     LOCK(cs_main);
2032
2033     vector<pair<CTransaction, uint256>> nTxes;
2034
2035     // get notarization data and check all transactions
2036     if (GetNotarizationData(chainID, ecode, nData, &nTxes))
2037     {
2038         CTransaction tx;
2039         CPBaaSNotarization ourLast;
2040         uint256 blkHash;
2041         bool found = false;
2042
2043         // loop in reverse through list, as most recent is at end
2044         for (int32_t i = nData.vtx.size() - 1; i >= 0; i--)
2045         {
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()))
2051             {
2052                 found = true;
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)));
2057                 break;
2058             }
2059         }
2060
2061         // now make the basic notarization for this chain that the other chain daemon can complete
2062         // after it is returned
2063         if (found)
2064         {
2065             // make sure our MMR matches our tip height, etc.
2066             LOCK(cs_main);
2067
2068             CPBaaSNotarization prevNotarization(tx);
2069
2070             if(!prevNotarization.IsValid())
2071             {
2072                 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Invalid prior notarization");
2073             }
2074
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;
2079
2080             CMerkleBranch blockProof;
2081             chainActive.GetBlockProof(mmv, blockProof, proofheight);
2082
2083             // prove the last notarization txid with new MMR, which also provides its blockhash and power as part of proof
2084             CBlock block;
2085             CBlockIndex *pnindex = mapBlockIndex.find(blkHash)->second;
2086
2087             if(!pnindex || !ReadBlockFromDisk(block, pnindex, Params().GetConsensus(), 0))
2088             {
2089                 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
2090             }
2091
2092             int32_t prevHeight = pnindex->GetHeight();
2093
2094             // which transaction are we in this block?
2095             CKeyID keyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZENOTARIZATION));
2096             std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2097
2098             if (!GetAddressIndex(keyID, 1, addressIndex, prevHeight, prevHeight))
2099             {
2100                 throw JSONRPCError(RPC_INTERNAL_ERROR, "Address index read error - possible corruption in address index");
2101             }
2102
2103             uint256 txHash = tx.GetHash();
2104             unsigned int txIndex;
2105             for (txIndex = 0; txIndex < addressIndex.size(); txIndex++)
2106             {
2107                 if (addressIndex[txIndex].first.txhash == txHash)
2108                 {
2109                     break;
2110                 }
2111             }
2112
2113             if (txIndex == addressIndex.size())
2114             {
2115                 throw JSONRPCError(RPC_INTERNAL_ERROR, "Notarization not found in address index - possible corruption");
2116             }
2117             else
2118             {
2119                 // get index in the block as our transaction index for proofs
2120                 txIndex = addressIndex[txIndex].first.index;
2121             }
2122
2123             // if bock headers are merge mined, keep header refs, not headers
2124
2125             // create and store the notarization proof of chain
2126             vector<CBaseChainObject *> chainObjects;
2127             COpRetProof orp;
2128
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());
2134
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());
2139
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())
2143             {
2144                 mtx.vout.pop_back();
2145             }
2146             CTransaction strippedTx(mtx);
2147
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);
2151
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());
2156
2157             // add proof of the transaction
2158             CChainObject<CMerkleBranch> txProofObj(CHAINOBJ_PROOF, txProof);
2159             chainObjects.push_back(&txProofObj);
2160             orp.AddObject(CHAINOBJ_PROOF, txHash);
2161
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;
2165
2166             if (numPriorBlocks > PBAAS_MAXPRIORBLOCKS || numPriorBlocks > (proofheight - 1))
2167                 numPriorBlocks = PBAAS_MAXPRIORBLOCKS > (proofheight - 1) ? ((proofheight - 1) < 1 ? 0 : (proofheight - 1)) : PBAAS_MAXPRIORBLOCKS;
2168
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--)
2172             {
2173                 priorBlocks.priorBlocks.push_back(mmv.mmr.GetNode(proofheight - i).hash);
2174             }
2175
2176             CChainObject<CPriorBlocksCommitment> priorBlocksObj(CHAINOBJ_PRIORBLOCKS, priorBlocks);
2177             chainObjects.push_back(&priorBlocksObj);
2178             orp.AddObject(CHAINOBJ_PRIORBLOCKS, ::GetHash(priorBlocks));
2179
2180             // get node keys and addresses
2181             vector<CNodeData> nodes;
2182             const static int MAX_NODES = 2;
2183
2184             {
2185                 LOCK(cs_vNodes);
2186                 if (!vNodes.empty())
2187                 {
2188                     for (int i = 0; i < vNodes.size(); i++)
2189                     {
2190                         CNodeStats stats;
2191                         vNodes[i]->copyStats(stats);
2192                         if (vNodes[i]->fSuccessfullyConnected && !vNodes[i]->fInbound)
2193                         {
2194                             CBitcoinAddress bca(CKeyID(vNodes[i]->hashPaymentAddress));
2195                             nodes.push_back(CNodeData(vNodes[i]->addr.ToString(), bca.ToString()));
2196                         }
2197                     }
2198                 }
2199             }
2200
2201             // reduce number to max by removing randomly
2202             while (nodes.size() > MAX_NODES)
2203             {
2204                 int toErase = GetRandInt(nodes.size() - 1);
2205                 nodes.erase(nodes.begin() + toErase);
2206             }
2207
2208             CKeyID pkID;
2209             if (USE_EXTERNAL_PUBKEY)
2210             {
2211                 CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
2212                 if (pubKey.IsFullyValid())
2213                 {
2214                     pkID = pubKey.GetID();
2215                 }
2216             }
2217             else
2218             {
2219                 static bool printMsg = true;
2220                 if (printMsg)
2221                 {
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");
2224                     printMsg = false;
2225                 }
2226             }
2227
2228             CBlockIndex *nzIndex = chainActive[proofheight];
2229             CCurrencyState currencyState = ConnectedChains.GetCurrencyState(proofheight);
2230
2231             // TODO: get currency state as of proofHeight
2232             if (ConnectedChains.ThisChain().ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE)
2233             {
2234                 CBlock block;
2235                 if (!ReadBlockFromDisk(block, nzIndex, Params().GetConsensus(), false))
2236                 {
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());
2239                 }
2240             }
2241             else
2242             {
2243                 if (proofheight != 1)
2244                 {
2245                     // get 
2246                 }
2247             }
2248
2249             // get the current block's MMR root and proof height
2250             CPBaaSNotarization notarization = CPBaaSNotarization(CPBaaSNotarization::CURRENT_VERSION, 
2251                                                                  ASSETCHAINS_CHAINID,
2252                                                                  pkID,
2253                                                                  proofheight,
2254                                                                  mmrRoot,
2255                                                                  preHash,
2256                                                                  ArithToUint256(GetCompactPower(nzIndex->nNonce, nzIndex->nBits, nzIndex->nVersion)),
2257                                                                  currencyState,
2258                                                                  uint256(), 0,
2259                                                                  tx.GetHash(), prevNotarization.notarizationHeight,
2260                                                                  orp,
2261                                                                  nodes);
2262
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);
2266
2267             CCcontract_info CC;
2268             CCcontract_info *cp;
2269
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});
2276
2277             newNotarization.vout.push_back(MakeCC1of1Vout(crosscode, CPBaaSChainDefinition::DEFAULT_OUTPUT_VALUE, pk, dests, notarization));
2278
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))});
2283
2284             CNotarizationFinalization nf;
2285             newNotarization.vout.push_back(MakeCC1of1Vout(EVAL_FINALIZENOTARIZATION, DEFAULT_TRANSACTION_FEE, pk, dests, nf));
2286
2287             newNotarization.vout.push_back(CTxOut(0, StoreOpRetArray(chainObjects)));
2288
2289             CTransaction newTx(newNotarization);
2290             ret.push_back(Pair("newtx", EncodeHexTx(newTx)));
2291         }
2292     }
2293     return ret;
2294 }
2295
2296 UniValue paynotarizationrewards(const UniValue& params, bool fHelp)
2297 {
2298     if (fHelp || (params.size() != 2 && params.size() != 3))
2299     {
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"
2304
2305             "\nArguments\n"
2306
2307             "\nResult:\n"
2308
2309             "\nExamples:\n"
2310             + HelpExampleCli("paynotarizationrewards", "\"hextx\"")
2311             + HelpExampleRpc("paynotarizationrewards", "\"hextx\"")
2312         );
2313     }
2314     CheckPBaaSAPIsValid();
2315
2316     uint160 chainID;
2317
2318     if (!pwalletMain)
2319     {
2320         throw JSONRPCError(RPC_INVALID_PARAMETER, "Paying notarization rewards requires an active wallet");
2321     }
2322
2323     chainID = GetChainIDFromParam(params[0]);
2324     if (chainID.IsNull())
2325     {
2326         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or chainid");
2327     }
2328
2329     CAmount amount = AmountFromValue(params[1]);
2330     uint32_t billingPeriod = 0;
2331
2332     if (params.size() == 3)
2333     {
2334         uint32_t billingPeriod = uni_get_int(params[2]);
2335     }
2336
2337     CServiceReward sr = CServiceReward(SERVICE_NOTARIZATION, billingPeriod);
2338     
2339     CCcontract_info CC;
2340     CCcontract_info *cp;
2341     cp = CCinit(&CC, EVAL_SERVICEREWARD);
2342
2343     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2344
2345     std::vector<CTxDestination> dests({CKeyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, SERVICE_NOTARIZATION), EVAL_SERVICEREWARD))});
2346
2347     std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeCC1of1Vout(EVAL_SERVICEREWARD, amount, pk, dests, sr).scriptPubKey, amount}});
2348     CWalletTx wtx;
2349
2350     // create the transaction with native coin as input
2351     LOCK2(cs_main, pwalletMain->cs_wallet);
2352
2353     CReserveKey reserveKey(pwalletMain);
2354     CAmount fee;
2355     int nChangePos;
2356     string failReason;
2357
2358     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2359     {
2360         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainID.GetHex() + ": " + failReason);
2361     }
2362     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2363     {
2364         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2365     }
2366     return UniValue(wtx.GetHash().GetHex());
2367 }
2368
2369 UniValue listreservetransactions(const UniValue& params, bool fHelp)
2370 {
2371     if (fHelp || params.size() != 1)
2372     {
2373         throw runtime_error(
2374             "listreservetransactions (maxnumber) (minconfirmations)\n"
2375             "\nLists all reserve coin transactions sent to/from the current wallet.\n"
2376
2377             "\nArguments\n"
2378
2379             "\nResult:\n"
2380
2381             "\nExamples:\n"
2382             + HelpExampleCli("listreservetransactions", "100 0")
2383             + HelpExampleRpc("listreservetransactions", "100 0")
2384         );
2385     }
2386     // lists all transactions in a wallet that are 
2387     return NullUniValue;
2388 }
2389
2390 CCoinbaseCurrencyState GetInitialCurrencyState(CPBaaSChainDefinition &chainDef, int32_t definitionHeight)
2391 {
2392     std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
2393     CAmount preconvertedAmount = 0, fees = 0;
2394     bool isReserve = chainDef.ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE;
2395
2396     if (GetChainTransfers(transferInputs, chainDef.GetChainID(), definitionHeight, chainDef.startBlock, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
2397     {
2398         for (auto transfer : transferInputs)
2399         {
2400             // total amount will be transferred to the chain, with fee split between aggregator and miner in
2401             // Verus reserve
2402             preconvertedAmount += transfer.second.second.nValue;
2403             fees += transfer.second.second.nFees;
2404         }
2405     }
2406
2407     uint32_t Flags = isReserve ? CCurrencyState::VALID + CCurrencyState::ISRESERVE : CCurrencyState::VALID;
2408     CCurrencyState currencyState(chainDef.conversion, 0, 0, 0, preconvertedAmount, Flags);
2409
2410     CAmount preconvertedNative = currencyState.ReserveToNative(preconvertedAmount, chainDef.conversion);
2411     currencyState.InitialSupply = preconvertedNative;
2412     currencyState.Supply += preconvertedNative;
2413
2414     return CCoinbaseCurrencyState(currencyState, preconvertedAmount, 0, CReserveOutput(CReserveOutput::VALID, fees), chainDef.conversion, fees, fees);
2415 }
2416
2417 UniValue reserveexchange(const UniValue& params, bool fHelp)
2418 {
2419     if (fHelp || params.size() != 1)
2420     {
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"
2425
2426             "\nArguments\n"
2427             "       {\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"
2434             "       }\n"
2435
2436             "\nResult:\n"
2437             "       \"txid\" : \"transactionid\" (string) The transaction id.\n"
2438
2439             "\nExamples:\n"
2440             + HelpExampleCli("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
2441             + HelpExampleRpc("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
2442         );
2443     }
2444
2445     CheckPBaaSAPIsValid();
2446     throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Not yet implemented. Use sendreserve in this release.");
2447 }
2448
2449 UniValue sendreserve(const UniValue& params, bool fHelp)
2450 {
2451     if (fHelp || params.size() != 1)
2452     {
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"
2457
2458             "\nArguments\n"
2459             "       {\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"
2468             "       }\n"
2469             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
2470
2471             "\nResult:\n"
2472             "       \"txid\" : \"transactionid\" (string) The transaction id.\n"
2473
2474             "\nExamples:\n"
2475             + HelpExampleCli("sendreserve", "'{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}'")
2476             + HelpExampleRpc("sendreserve", "'{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}'")
2477         );
2478     }
2479
2480     CheckPBaaSAPIsValid();
2481
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.
2484     //
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.
2488     //
2489     vector<CRecipient> outputs;
2490     vector<bool> vConvert;
2491
2492     if ((!params[0].isObject()))
2493     {
2494         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. Must provide a single object that represents valid outputs. see help.");
2495     }
2496
2497     bool isVerusActive = IsVerusActive();
2498     uint160 thisChainID = ConnectedChains.ThisChain().GetChainID();
2499
2500     CWalletTx wtx;
2501
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"));
2508
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);
2512
2513     bool subtractFee = uni_get_int(find_value(params[0], "subtractfee"), false);
2514     uint32_t flags = CReserveOutput::VALID;
2515
2516     bool sameChain = false;
2517     if (name == "")
2518     {
2519         name = ASSETCHAINS_SYMBOL;
2520         sameChain = true;
2521     }
2522
2523     if (name == "" || paymentAddr == "" || amount < 0)
2524     {
2525         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters");
2526     }
2527
2528     CBitcoinAddress ba(DecodeDestination(paymentAddr));
2529     CKeyID kID;
2530
2531     if (!ba.IsValid() || !ba.GetKeyID(kID))
2532     {
2533         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid payment address");
2534     }
2535
2536     CTxDestination refundDest = DecodeDestination(refundAddr);
2537     CBitcoinAddress refunda(refundDest);
2538     CKeyID refundID;
2539
2540     if (!refunda.IsValid() || !refunda.GetKeyID(refundID))
2541     {
2542         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid refund address");
2543     }
2544
2545     uint160 chainID = CCrossChainRPCData::GetChainID(name);
2546
2547     CPBaaSChainDefinition chainDef;
2548     int32_t definitionHeight;
2549
2550     // validate that the target chain is still running
2551     if (!GetChainDefinition(chainID, chainDef, &definitionHeight) || !chainDef.IsValid())
2552     {
2553         throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified is not a valid chain");
2554     }
2555
2556     bool isReserve = chainDef.ChainOptions() & CPBaaSChainDefinition::OPTION_RESERVE;
2557
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
2561     // the start block
2562     int32_t height = chainActive.Height();
2563     bool beforeStart = chainDef.startBlock > height;
2564     CCoinbaseCurrencyState currencyState;
2565
2566     UniValue ret = NullUniValue;
2567
2568     if (isVerusActive)
2569     {
2570         if (chainID == thisChainID)
2571         {
2572             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send reserve or convert, except on a PBaaS fractional reserve chain. Use sendtoaddress or z_sendmany.");
2573         }
2574         else // ensure the PBaaS chain is a fractional reserve or that it's convertible and this is a conversion
2575         {
2576             if (tonative || preconvert)
2577             {
2578                 // if chain hasn't started yet, we use the conversion as a ratio over satoshis to participate in the pre-mine
2579                 // up to a maximum
2580                 if (beforeStart)
2581                 {
2582                     if (chainDef.conversion <= 0)
2583                     {
2584                         throw JSONRPCError(RPC_INVALID_PARAMETER, std::string(ASSETCHAINS_SYMBOL) + " is not convertible to " + chainDef.name + ".");
2585                     }
2586
2587                     currencyState = GetInitialCurrencyState(chainDef, definitionHeight);
2588
2589                     if (tonative)
2590                     {
2591                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Until " + chainDef.name + " launches, you must use \"preconvert\" rather than \"tonative\" for conversion.");
2592                     }
2593
2594                     flags |= CReserveTransfer::PRECONVERT;
2595                 } else if (!isReserve || preconvert)
2596                 {
2597                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot preconvert " + std::string(ASSETCHAINS_SYMBOL) + " after chain launch");
2598                 }
2599                 else
2600                 {
2601                     flags |= CReserveTransfer::CONVERT;
2602                 }
2603             }
2604             else if (!isReserve)
2605             {
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.");
2608             }
2609         }
2610
2611         CCcontract_info CC;
2612         CCcontract_info *cp;
2613         cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2614
2615         CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2616
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)});
2619
2620         // start with default fee for this send
2621         CAmount transferFee = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
2622
2623         if (flags & CReserveTransfer::PRECONVERT)
2624         {
2625             arith_uint256 bigAmount(amount);
2626             static const arith_uint256 bigsatoshi(CReserveExchange::SATOSHIDEN);
2627             arith_uint256 feeRate(chainDef.launchFee);
2628
2629             if (chainDef.launchFee)
2630             {
2631                 CAmount launchFee;
2632
2633                 if (!subtractFee)
2634                 {
2635                     amount = (bigAmount = ((bigAmount * bigsatoshi) / (bigsatoshi - feeRate))).GetLow64();
2636                 }
2637                 launchFee = ((bigAmount * feeRate) / bigsatoshi).GetLow64();
2638
2639                 amount -= launchFee;
2640
2641                 // add the output to this transaction
2642                 CTxDestination feeOutAddr(chainDef.address);
2643                 outputs.push_back(CRecipient({GetScriptForDestination(feeOutAddr), launchFee, false}));
2644             }
2645
2646             if (!subtractFee)
2647             {
2648                 // add the fee amount that will make the conversion correct after subtracting it again
2649                 amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2650             }
2651
2652             if (currencyState.ReserveIn + amount > chainDef.maxpreconvert)
2653             {
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());
2655             }
2656         }
2657         else if (flags & CReserveTransfer::CONVERT && !subtractFee)
2658         {
2659             // add the fee amount that will make the conversion correct after subtracting it again
2660             amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2661         }
2662
2663         if (!subtractFee)
2664         {
2665             amount += transferFee;
2666         }
2667
2668         if (amount <= (transferFee << 1))
2669         {
2670             throw JSONRPCError(RPC_INVALID_PARAMETER, "Must send more than twice the cost of the fee. Fee for this transaction is " + ValueFromAmount(transferFee).write());
2671         }
2672
2673         transferFee = CReserveTransfer::CalculateFee(flags, amount, chainDef);
2674         amount -= transferFee;
2675
2676         // create the transfer object
2677         CReserveTransfer rt(flags, amount, transferFee, kID);
2678
2679         CTxOut ccOut;
2680
2681         // if preconversion and we have a minpreconvert, prepare for refund output
2682         if (flags & CReserveTransfer::PRECONVERT && chainDef.minpreconvert)
2683         {
2684             LOCK2(cs_main, pwalletMain->cs_wallet);
2685             CPubKey pk2(GetDestinationBytes(refundDest));
2686             if (!pk2.IsFullyValid() && !pwalletMain->GetPubKey(refundID, pk2))
2687             {
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.");
2689             }
2690             dests.push_back(pk2);
2691             ccOut = MakeCC1of2Vout(EVAL_RESERVE_TRANSFER, amount + transferFee, pk, pk2, dests, (CReserveTransfer)rt);
2692         }
2693         else
2694         {
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);
2697         }
2698
2699         outputs.push_back(CRecipient({ccOut.scriptPubKey, amount + transferFee, false}));
2700
2701         // create the transaction with native coin as input
2702         LOCK2(cs_main, pwalletMain->cs_wallet);
2703
2704         CReserveKey reserveKey(pwalletMain);
2705         CAmount fee;
2706         int nChangePos;
2707         string failReason;
2708
2709         if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2710         {
2711             throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2712         }
2713         if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2714         {
2715             throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2716         }
2717         ret = UniValue(wtx.GetHash().GetHex());
2718     }
2719     else
2720     {
2721         if (chainID == thisChainID)
2722         {
2723             CCcontract_info CC;
2724             CCcontract_info *cp;
2725
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
2728             if (toreserve)
2729             {
2730                 if (!isReserve)
2731                 {
2732                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + ConnectedChains.ThisChain().name + " into " + ConnectedChains.NotaryChain().chainDefinition.name + ".");
2733                 }
2734
2735                 CCoinsViewCache view(pcoinsTip);
2736
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));
2740
2741                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({kID, CTxDestination(pk)});
2742
2743                 if (!subtractFee)
2744                 {
2745                     amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2746                 }
2747
2748                 CReserveExchange rex(flags + CReserveExchange::TO_RESERVE, amount);
2749
2750                 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_EXCHANGE, amount, dests, rex, pk);
2751                 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount, false}));
2752
2753                 // create a transaction with native coin as input
2754                 {
2755                     LOCK2(cs_main, pwalletMain->cs_wallet);
2756
2757                     CReserveKey reserveKey(pwalletMain);
2758                     CAmount fee;
2759                     int nChangePos;
2760                     string failReason;
2761
2762                     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2763                     {
2764                         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2765                     }
2766                     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2767                     {
2768                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2769                     }
2770                     ret = UniValue(wtx.GetHash().GetHex());
2771                 }
2772             }
2773             else if (tonative)
2774             {
2775                 // convert from reserve to native (buy)
2776                 cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
2777                 CPubKey pk(ParseHex(CC.CChexstr));
2778
2779                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(kID), CTxDestination(pk)});
2780
2781                 if (!subtractFee)
2782                 {
2783                     amount += CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2784                 }
2785
2786                 CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, amount);
2787
2788                 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_EXCHANGE, 0, dests, rex, pk);
2789                 outputs.push_back(CRecipient({ccOut.scriptPubKey, 0, false}));
2790
2791                 // create a transaction with reserve coin as input
2792                 {
2793                     LOCK2(cs_main, pwalletMain->cs_wallet);
2794
2795                     CReserveKey reserveKey(pwalletMain);
2796                     CAmount fee;
2797                     int nChangePos;
2798                     string failReason;
2799
2800                     if (!pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2801                     {
2802                         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2803                     }
2804                     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2805                     {
2806                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit reserve transaction " + wtx.GetHash().GetHex());
2807                     }
2808                     ret = UniValue(wtx.GetHash().GetHex());
2809                 }
2810             }
2811             else
2812             {
2813                 // we will send using a reserve output, fee will be paid by converting from reserve
2814                 cp = CCinit(&CC, EVAL_RESERVE_OUTPUT);
2815
2816                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({kID});
2817
2818                 // create the transfer object
2819                 CReserveOutput ro(flags, amount);
2820
2821                 // native amount in the output is 0
2822                 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_OUTPUT, 0, dests, ro);
2823
2824                 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount, subtractFee}));
2825
2826                 // create a transaction with reserve coin as input
2827                 {
2828                     LOCK2(cs_main, pwalletMain->cs_wallet);
2829
2830                     CReserveKey reserveKey(pwalletMain);
2831                     CAmount fee;
2832                     int nChangePos;
2833                     string failReason;
2834
2835                     if (!pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2836                     {
2837                         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2838                     }
2839                     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2840                     {
2841                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2842                     }
2843                     ret = UniValue(wtx.GetHash().GetHex());
2844                 }
2845             }
2846         }
2847         else if (chainID == ConnectedChains.NotaryChain().GetChainID())
2848         {
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
2851             if (tonative)
2852             {
2853                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert to native and send. Can only send " + chainDef.name + " reserve to the chain.");
2854             }
2855
2856             if (toreserve)
2857             {
2858                 if (!isReserve)
2859                 {
2860                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + ConnectedChains.ThisChain().name + " into " + ConnectedChains.NotaryChain().chainDefinition.name + ".");
2861                 }
2862
2863                 CAmount conversionFee;
2864                 if (subtractFee)
2865                 {
2866                     conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(amount);
2867                     amount -= conversionFee;
2868                 }
2869                 else
2870                 {
2871                     conversionFee = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(amount);
2872                 }
2873
2874                 CCoinsViewCache view(pcoinsTip);
2875                 CReserveExchange rex(flags + CReserveExchange::TO_RESERVE + CReserveExchange::SEND_OUTPUT, amount + conversionFee);
2876
2877                 // we will send using a reserve output, fee will be paid by converting from reserve
2878                 CCcontract_info CC;
2879                 CCcontract_info *cp;
2880                 cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
2881                 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2882
2883                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({kID});
2884
2885                 // native amount in output is 0
2886                 CTxOut ccOut = MakeCC1ofAnyVout(EVAL_RESERVE_EXCHANGE, amount + conversionFee, dests, rex, pk);
2887
2888                 outputs.push_back(CRecipient({ccOut.scriptPubKey, amount + conversionFee, false}));
2889
2890                 // create a transaction with native coin as input
2891                 {
2892                     LOCK2(cs_main, pwalletMain->cs_wallet);
2893
2894                     CReserveKey reserveKey(pwalletMain);
2895                     CAmount fee;
2896                     int nChangePos;
2897                     string failReason;
2898
2899                     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2900                     {
2901                         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2902                     }
2903                     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2904                     {
2905                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2906                     }
2907                     ret = UniValue(wtx.GetHash().GetHex());
2908                 }
2909             }
2910             else
2911             {
2912                 // we will send using from reserve input without conversion
2913                 // output will be a reserve transfer
2914                 CCcontract_info CC;
2915                 CCcontract_info *cp;
2916                 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2917
2918                 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2919
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)});
2922
2923                 CAmount transferFee = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
2924                 if (subtractFee)
2925                 {
2926                     amount -= transferFee;
2927                 }
2928
2929                 // create the transfer object
2930                 CReserveTransfer rt(flags, amount, CReserveTransfer::DEFAULT_PER_STEP_FEE << 1, kID);
2931
2932                 CTxOut ccOut = MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, 0, pk, dests, (CReserveTransfer)rt);
2933                 outputs.push_back(CRecipient({ccOut.scriptPubKey, 0, subtractFee}));
2934
2935                 // create a transaction with reserve coin as input
2936                 {
2937                     LOCK2(cs_main, pwalletMain->cs_wallet);
2938
2939                     CReserveKey reserveKey(pwalletMain);
2940                     CAmount fee;
2941                     int nChangePos;
2942                     string failReason;
2943
2944                     if (!pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2945                     {
2946                         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainDef.name + ": " + failReason);
2947                     }
2948                     /*
2949                     printf("newTx outputs:\n");
2950                     for (auto outp : wtx.vout)
2951                     {
2952                         UniValue scrOut(UniValue::VOBJ);
2953                         ScriptPubKeyToJSON(outp.scriptPubKey, scrOut, false);
2954                         printf("%s\n", scrOut.write(1, 2).c_str());
2955                     }
2956                     */
2957                     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2958                     {
2959                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2960                     }
2961                     ret = UniValue(wtx.GetHash().GetHex());
2962                 }
2963             }
2964         }
2965         else
2966         {
2967             throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified is not the current chain or this chain's reserve");
2968         }
2969     }
2970     return ret;
2971 }
2972
2973 bool GetLastImportIn(uint160 chainID, CTransaction &lastImportTx)
2974 {
2975     // look for unspent chain transfer outputs for all chains
2976     CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
2977
2978     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
2979
2980     LOCK(cs_main);
2981
2982     if (!GetAddressUnspent(keyID, 1, unspentOutputs))
2983     {
2984         return false;
2985     }
2986
2987     for (auto output : unspentOutputs)
2988     {
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.
2992         COptCCParams p;
2993         if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
2994         {
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;
2999             uint256 blkHash;
3000             if (cci.IsValid() && myGetTransaction(output.first.txhash, lastTx, blkHash))
3001             {
3002                 if (output.second.blockHeight == 1 && lastTx.IsCoinBase())
3003                 {
3004                     lastImportTx = lastTx;
3005                     return true;
3006                 }
3007                 else
3008                 {
3009                     if (IsVerusActive())
3010                     {
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())
3014                         {
3015                             lastImportTx = lastTx;
3016                             return true;
3017                         }
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())
3022                         {
3023                             lastImportTx = lastTx;
3024                             return true;
3025                         }
3026                     }
3027                 }
3028             }
3029         }
3030     }
3031     return false;
3032 }
3033
3034 UniValue getlastimportin(const UniValue& params, bool fHelp)
3035 {
3036     if (fHelp || params.size() != 1)
3037     {
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"
3043
3044             "\nArguments\n"
3045             "   \"fromname\"                (string, required) name of the chain to get the last import transaction in from\n"
3046
3047             "\nResult:\n"
3048             "   {\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"
3053             "   }\n"
3054
3055             "\nExamples:\n"
3056             + HelpExampleCli("getlastimportin", "jsondefinition")
3057             + HelpExampleRpc("getlastimportin", "jsondefinition")
3058         );
3059     }
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();
3063
3064     uint160 chainID = GetChainIDFromParam(params[0]);
3065
3066     if (chainID.IsNull())
3067     {
3068         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3069     }
3070
3071     CTransaction lastImportTx, lastConfirmedTx;
3072
3073     CChainNotarizationData cnd;
3074     std::vector<std::pair<CTransaction, uint256>> txesOut;
3075
3076     LOCK(cs_main);
3077
3078     if (!GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd, &txesOut))
3079     {
3080         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No chain notarizations for " + uni_get_str(params[0]) + " found");
3081     }
3082
3083     UniValue ret(UniValue::VNULL);
3084
3085     if (cnd.IsConfirmed())
3086     {
3087         lastConfirmedTx = txesOut[cnd.lastConfirmed].first;
3088         if (GetLastImportIn(chainID, lastImportTx))
3089         {
3090             ret = UniValue(UniValue::VOBJ);
3091             CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
3092             // find the import output
3093             COptCCParams p;
3094             int importIdx;
3095             for (importIdx = 0; importIdx < lastImportTx.vout.size(); importIdx++)
3096             {
3097                 if (lastImportTx.vout[importIdx].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
3098                 {
3099                     break;
3100                 }
3101             }
3102             if (importIdx >= lastImportTx.vout.size())
3103             {
3104                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid lastImport transaction");
3105             }
3106
3107             txTemplate.vin.push_back(CTxIn(lastImportTx.GetHash(), importIdx, CScript()));
3108
3109             // if Verus is active, our template import will gather all RESERVE_DEPOSITS that it can to spend into the
3110             // import thread
3111             if (IsVerusActive())
3112             {
3113                 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > reserveDeposits;
3114
3115                 LOCK2(cs_main, mempool.cs);
3116
3117                 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_DEPOSIT)), 1, reserveDeposits))
3118                 {
3119                     for (auto deposit : reserveDeposits)
3120                     {
3121                         COptCCParams p;
3122                         if (deposit.second.script.IsPayToCryptoCondition(p) && p.evalCode == EVAL_RESERVE_DEPOSIT)
3123                         {
3124                             txTemplate.vin.push_back(CTxIn(deposit.first.txhash, deposit.first.index, CScript()));
3125                         }
3126                     }
3127                 }
3128             }
3129
3130             CCoinsViewCache view(pcoinsTip);
3131             int64_t dummyInterest;
3132             CAmount totalImportAvailable = view.GetValueIn(cnd.vtx[cnd.lastConfirmed].second.notarizationHeight, &dummyInterest, txTemplate);
3133
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));
3138         }
3139     }
3140     return ret;
3141 }
3142
3143 UniValue getlatestimportsout(const UniValue& params, bool fHelp)
3144 {
3145     if (fHelp || params.size() != 1)
3146     {
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"
3150
3151             "\nArguments\n"
3152             "{\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"
3158             "}\n"
3159
3160             "\nResult:\n"
3161             "UniValue array of hex transactions"
3162
3163             "\nExamples:\n"
3164             + HelpExampleCli("getlatestimportsout", "jsonargs")
3165             + HelpExampleRpc("getlatestimportsout", "jsonargs")
3166         );
3167     }
3168
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();
3172
3173     bool isVerusActive = IsVerusActive();
3174
3175     uint160 chainID = GetChainIDFromParam(find_value(params[0], "name"));
3176
3177     if (chainID.IsNull())
3178     {
3179         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3180     }
3181
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;
3185
3186     if (!GetChainDefinition(chainID, chainDef))
3187     {
3188         throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain definition not found");
3189     }
3190
3191     std::string lastImportHex = uni_get_str(find_value(params[0], "lastimporttx"));
3192     CTransaction lastImportTx;
3193
3194     std::string templateTxHex = uni_get_str(find_value(params[0], "importtxtemplate"));
3195     CTransaction templateTx;
3196
3197     std::string lastConfirmedTxHex = uni_get_str(find_value(params[0], "lastconfirmednotarization"));
3198     CTransaction lastConfirmedNotarization;
3199
3200     CAmount totalImportAvailable = uni_get_int64(find_value(params[0], "totalimportavailable"));
3201
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))))
3212     {
3213         DeleteOpRetObjects(chainObjs);
3214         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid last import tx");
3215     }
3216
3217     DeleteOpRetObjects(chainObjs);
3218
3219     std::vector<CTransaction> newImports;
3220     if (!ConnectedChains.CreateLatestImports(chainDef, lastImportTx, templateTx, lastConfirmedNotarization, totalImportAvailable, newImports))
3221     {
3222         throw JSONRPCError(RPC_INVALID_PARAMETER, "Failure creating new imports");
3223     }
3224     UniValue ret(UniValue::VARR);
3225
3226     for (auto import : newImports)
3227     {
3228         ret.push_back(EncodeHexTx(import));
3229     }
3230     return ret;
3231 }
3232
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)
3236 {
3237     int32_t defHeight = 0;
3238     CPBaaSChainDefinition chainDef;
3239     CTransaction chainDefTx;
3240     uint160 thisChainID = ConnectedChains.ThisChain().GetChainID();
3241
3242     if (!GetChainDefinition(chainID, chainDef, &defHeight) || chainID == thisChainID)
3243     {
3244         errorReason = "chain-not-found-or-ineligible";
3245         return false;
3246     }
3247
3248     bool valid = false;
3249
3250     LOCK(cs_main);
3251
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())
3257     {
3258         uint256 blkHash;
3259
3260         for (auto txidx : addressIndex)
3261         {
3262             COptCCParams p;
3263             if (!myGetTransaction(txidx.first.txhash, chainDefTx, blkHash))
3264             {
3265                 errorReason = "chain-transaction-not-found";
3266                 return false;
3267             }
3268             if (chainDefTx.vout[txidx.first.index].scriptPubKey.IsPayToCryptoCondition(p) && 
3269                 p.IsValid() && 
3270                 p.evalCode == EVAL_PBAASDEFINITION && 
3271                 p.vData[0].size() && 
3272                 (chainDef = CPBaaSChainDefinition(p.vData[0])).IsValid() &&
3273                 chainDef.GetChainID() == chainID)
3274             {
3275                 valid = true;
3276                 break;
3277             }
3278         }
3279     }
3280
3281     if (!valid)
3282     {
3283         errorReason = "valid-chain-not-found";
3284         return false;
3285     }
3286
3287     if (!GetLastImportIn(chainID, lastImportTx))
3288     {
3289         errorReason = "no-import-thread-found";
3290         return false;
3291     }
3292
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()))
3301     {
3302         DeleteOpRetObjects(chainObjs);
3303         errorReason = "invalid-import-thread-found";
3304         return false;
3305     }
3306
3307     bool isVerusActive = IsVerusActive();
3308
3309     uint256 lastExportHash;
3310     CTransaction lastExportTx;
3311
3312     uint32_t blkHeight = defHeight;
3313     bool found = false;
3314     if (chainObjs.size())
3315     {
3316         CTransaction tx;
3317         uint256 blkHash;
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() && 
3324             blkMapIt->second)
3325         {
3326             found = true;
3327             blkHeight = blkMapIt->second->GetHeight();
3328         }
3329     }
3330     else
3331     {
3332         // if we haven't processed any refunds yet, setup to payout from the first export and continue
3333         CCrossChainExport ccx(lastImportTx);
3334         if (ccx.IsValid())
3335         {
3336             found = true;
3337             lastExportTx = lastImportTx;
3338             lastExportHash = lastImportTx.GetHash();
3339         }
3340     }
3341
3342     if (!found)
3343     {
3344         LogPrintf("%s: No export thread found\n", __func__);
3345         printf("%s: No export thread found\n", __func__);
3346         return false;
3347     }
3348
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();
3352
3353     // make sure the chain is qualified for a refund
3354     if (!(chainDef.minpreconvert && chainDef.startBlock < nHeight && GetInitialCurrencyState(chainDef, defHeight).Reserve < chainDef.minpreconvert))
3355     {
3356         errorReason = "chain-ineligible";
3357         return false;
3358     }
3359     else
3360     {
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();
3364
3365         std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
3366
3367         // get all exports for the chain
3368         CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
3369
3370         CBlockIndex *pIndex;
3371
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))
3376         {
3377             // get all exports from first to last, and make sure they spend from the export thread consecutively
3378             bool found = false;
3379             uint256 lastHash = lastExportHash;
3380
3381             // indexed by input hash
3382             std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
3383
3384             // validate, order, and relate them with their inputs
3385             for (auto utxo : addressIndex)
3386             {
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;
3392                 COptCCParams p;
3393                 if (!utxo.first.spending &&
3394                     !(utxo.first.txhash == lastExportHash) &&
3395                     myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
3396                     (ccx = CCrossChainExport(tx)).IsValid() &&
3397                     ccx.numInputs &&
3398                     (!tx.IsCoinBase() && 
3399                     tx.vin.size() && 
3400                     myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
3401                     inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && 
3402                     p.IsValid() && 
3403                     p.evalCode == EVAL_CROSSCHAIN_EXPORT)
3404                 {
3405                     validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
3406                 }
3407             }
3408
3409             CTransaction lastImport(lastImportTx);
3410             CCrossChainImport lastCCI;
3411
3412             for (auto aixIt = validExports.find(lastExportHash); 
3413                 aixIt != validExports.end(); 
3414                 aixIt = validExports.find(lastExportHash))
3415             {
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
3424
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());
3427
3428                 int32_t lastImportPrevoutN = 0, lastExportPrevoutN = 0;
3429                 lastCCI = CCrossChainImport();
3430                 COptCCParams p;
3431
3432                 for (int i = 0; i < lastImport.vout.size(); i++)
3433                 {
3434                     if (lastImport.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size() && (lastCCI = CCrossChainImport(p.vData[0])).IsValid())
3435                     {
3436                         lastImportPrevoutN = i;
3437                         break;
3438                     }
3439                 }
3440
3441                 CCrossChainExport ccx(aixIt->second.second);
3442                 if (!lastCCI.IsValid() || !ccx.IsValid())
3443                 {
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());
3446                     return false;
3447                 }
3448
3449                 CMutableTransaction newImportTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
3450                 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), lastImportPrevoutN));
3451
3452                 // if no prepared input, make one
3453                 newImportTx.vout.clear();
3454                 newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
3455
3456                 // we need to recalculate fees, as there will be no conversions
3457
3458                 CReserveTransactionDescriptor rtxd;
3459
3460                 std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
3461
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
3464
3465                 ccx.totalFees = (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * ccx.numInputs;
3466                 for (auto objPtr : exportOutputs)
3467                 {
3468                     if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
3469                     {
3470                         CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
3471
3472                         // turn it into a normal transfer, which will create a regular native output
3473                         rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
3474
3475                         if (rt.flags & CReserveTransfer::FEE_OUTPUT)
3476                         {
3477                             // will be recalculated
3478                             rt.nFees = 0;
3479                             rt.nValue = ccx.CalculateExportFee();
3480                             continue;
3481                         }
3482                         else
3483                         {
3484                             rt.nValue = (rt.nValue + rt.nFees) - (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1);
3485                             rt.nFees = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
3486                         }
3487                     }
3488                 }
3489
3490                 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain(), exportOutputs, newImportTx.vout))
3491                 {
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());
3494                     // free the memory
3495                     DeleteOpRetObjects(exportOutputs);
3496                     return false;
3497                 }
3498
3499                 // free the memory
3500                 DeleteOpRetObjects(exportOutputs);
3501
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))
3506                 {
3507                     for (auto deposit : reserveDeposits)
3508                     {
3509                         COptCCParams p;
3510                         if (deposit.second.script.IsPayToCryptoCondition(p) && p.evalCode == EVAL_RESERVE_DEPOSIT)
3511                         {
3512                             newImportTx.vin.push_back(CTxIn(deposit.first.txhash, deposit.first.index, CScript()));
3513                             totalInput += deposit.second.satoshis;
3514                         }
3515                     }
3516                 }
3517
3518                 CAmount totalAvailableInput = totalInput;
3519                 CAmount availableReserveFees = ccx.totalFees;
3520                 CAmount exportFees = ccx.CalculateExportFee();
3521                 CAmount importFees = ccx.CalculateImportFee();
3522                 CAmount feesOut = 0;
3523
3524                 CCcontract_info CC;
3525                 CCcontract_info *cp;
3526                 CPubKey pk;
3527
3528                 // emit a crosschain import output as summary
3529                 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
3530
3531                 pk = CPubKey(ParseHex(CC.CChexstr));
3532
3533                 if (rtxd.ReserveFees() != (ccx.totalFees - exportFees))
3534                 {
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);
3537                 }
3538
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);
3541
3542                 totalAvailableInput -= rtxd.reserveIn;
3543                 newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, totalAvailableInput, pk, dests, cci);
3544
3545                 if (totalAvailableInput < 0)
3546                 {
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__);
3549                     return false;
3550                 }
3551
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);
3556
3557                 // add a proof of the export transaction at the notarization height
3558                 CBlock block;
3559                 if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
3560                 {
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());
3563                     return false;
3564                 }
3565
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);
3569
3570                 printf("%s: created refund %s for export %s\n", __func__, cci.ToUniValue().write().c_str(), aixIt->second.second.GetHash().GetHex().c_str());
3571
3572                 chainActive[aixIt->second.first.blockHeight]->AddMerkleProofBridge(exportProof);
3573                 mmrView.GetProof(exportProof, aixIt->second.first.blockHeight);
3574
3575                 CChainObject<CCrossChainProof> exportXProof(CHAINOBJ_CROSSCHAINPROOF, CCrossChainProof(uint256(), exportProof));
3576                 chainObjects.push_back(&exportXProof);
3577
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)));
3580
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;
3586             }
3587         }
3588         return true;
3589     }
3590 }
3591
3592 UniValue refundfailedlaunch(const UniValue& params, bool fHelp)
3593 {
3594     if (fHelp || params.size() != 1)
3595     {
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"
3600
3601             "\nArguments\n"
3602             "\"chainid\"            (hex or chain name, required)   the chain to refund contributions to\n"
3603
3604             "\nResult:\n"
3605
3606             "\nExamples:\n"
3607             + HelpExampleCli("refundfailedlaunch", "\"chainid\"")
3608             + HelpExampleRpc("refundfailedlaunch", "\"chainid\"")
3609         );
3610     }
3611     CheckPBaaSAPIsValid();
3612
3613     uint160 chainID;
3614
3615     chainID = GetChainIDFromParam(params[0]);
3616     if (chainID.IsNull())
3617     {
3618         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or chainid");
3619     }
3620
3621     if (chainID == ConnectedChains.ThisChain().GetChainID() || chainID == ConnectedChains.NotaryChain().chainDefinition.GetChainID())
3622     {
3623         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot refund the specified chain");
3624     }
3625
3626     CTransaction lastImportTx;
3627     std::vector<CTransaction> refundTxes;
3628     std::string failReason;
3629
3630     if (!RefundFailedLaunch(chainID, lastImportTx, refundTxes, failReason))
3631     {
3632         throw JSONRPCError(RPC_INVALID_REQUEST, failReason);
3633     }
3634
3635     uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
3636
3637     UniValue ret(UniValue::VARR);
3638
3639     CCoinsViewCache view(pcoinsTip);
3640
3641     // sign and commit the transactions
3642     for (auto tx : refundTxes)
3643     {
3644         LOCK2(cs_main, mempool.cs);
3645
3646         CMutableTransaction newTx(tx);
3647
3648         // sign the transaction and submit
3649         bool signSuccess;
3650         for (int i = 0; i < tx.vin.size(); i++)
3651         {
3652             SignatureData sigdata;
3653             CAmount value;
3654             CScript outputScript;
3655
3656             if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
3657             {
3658                 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
3659                 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
3660             }
3661             else
3662             {
3663                 CCoinsViewCache view(pcoinsTip);
3664                 CCoins coins;
3665                 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
3666                 {
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);
3669                     break;
3670                 }
3671                 value = coins.vout[tx.vin[i].prevout.n].nValue;
3672                 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
3673             }
3674
3675             signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
3676
3677             if (!signSuccess)
3678             {
3679                 fprintf(stderr,"refundfailedlaunch: failure to sign refund transaction\n");
3680                 LogPrintf("refundfailedlaunch: failure to sign refund transaction\n");
3681                 break;
3682             } else {
3683                 UpdateTransaction(newTx, i, sigdata);
3684             }
3685         }
3686
3687         if (signSuccess)
3688         {
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());
3697                 } else {
3698                     if (fMissingInputs) {
3699                         fprintf(stderr,"refundfailedlaunch: missing inputs\n");
3700                         LogPrintf("refundfailedlaunch: missing inputs\n");
3701                     }
3702                     else
3703                     {
3704                         fprintf(stderr,"refundfailedlaunch: rejected by memory pool for\n");
3705                         LogPrintf("refundfailedlaunch: rejected by memory pool for\n");
3706                     }
3707                 }
3708                 break;
3709             }
3710             else
3711             {
3712                 RelayTransaction(signedTx);
3713                 ret.push_back(signedTx.GetHash().GetHex());
3714             }
3715         }
3716     }
3717     return ret;
3718 }
3719
3720 UniValue getinitialcurrencystate(const UniValue& params, bool fHelp)
3721 {
3722     if (fHelp || params.size() != 1)
3723     {
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"
3727
3728             "\nArguments\n"
3729             "   \"name\"                    (string, required) name or chain ID of the chain to get the export transactions for\n"
3730
3731             "\nResult:\n"
3732             "   [\n"
3733             "       {\n"
3734             "           \"flags\" : n,\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"
3741             "       },\n"
3742             "   ]\n"
3743
3744             "\nExamples:\n"
3745             + HelpExampleCli("getinitialcurrencystate", "name")
3746             + HelpExampleRpc("getinitialcurrencystate", "name")
3747         );
3748     }
3749     CheckPBaaSAPIsValid();
3750
3751     uint160 chainID = GetChainIDFromParam(params[0]);
3752
3753     if (chainID.IsNull())
3754     {
3755         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3756     }
3757
3758     CPBaaSChainDefinition chainDef;
3759     int32_t definitionHeight;
3760     if (!GetChainDefinition(chainID, chainDef, &definitionHeight))
3761     {
3762         throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain " + params[0].get_str() + " not found");
3763     }
3764
3765     return GetInitialCurrencyState(chainDef, definitionHeight).ToUniValue();
3766 }
3767
3768 UniValue getcurrencystate(const UniValue& params, bool fHelp)
3769 {
3770     if (fHelp || params.size() > 1)
3771     {
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"
3775
3776             "\nArguments\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"
3779
3780             "\nResult:\n"
3781             "   [\n"
3782             "       {\n"
3783             "           \"height\": n,\n"
3784             "           \"blocktime\": n,\n"
3785             "           \"currencystate\": {\n"
3786             "               \"flags\" : n,\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"
3793             "           \"}\n"
3794             "       },\n"
3795             "   ]\n"
3796
3797             "\nExamples:\n"
3798             + HelpExampleCli("getcurrencystate", "name")
3799             + HelpExampleRpc("getcurrencystate", "name")
3800         );
3801     }
3802     CheckPBaaSAPIsValid();
3803
3804     uint64_t lStart;
3805     uint64_t startEnd[3] = {0};
3806
3807     lStart = startEnd[1] = startEnd[0] = chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 1;
3808
3809     if (params.size() == 1)
3810     {
3811         if (uni_get_int(params[0], -1) == -1 && params[0].isStr())
3812         {
3813             Split(params[0].get_str(), startEnd, startEnd[0], 3);
3814         }
3815     }
3816
3817     if (startEnd[0] > startEnd[1])
3818     {
3819         startEnd[0] = startEnd[1];
3820     }
3821
3822     if (startEnd[1] > lStart)
3823     {
3824         startEnd[1] = lStart;
3825     }
3826
3827     if (startEnd[1] < startEnd[0])
3828     {
3829         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range for currency state");
3830     }
3831
3832     if (startEnd[2] == 0)
3833     {
3834         startEnd[2] = 1;
3835     }
3836
3837     if (startEnd[2] > INT_MAX)
3838     {
3839         startEnd[2] = INT_MAX;
3840     }
3841
3842     uint32_t start = startEnd[0], end = startEnd[1], step = startEnd[2];
3843
3844     UniValue ret(UniValue::VARR);
3845
3846     for (int i = start; i <= end; i += step)
3847     {
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));
3852         CAmount price;
3853         entry.push_back(Pair("currencystate", currencyState.ToUniValue()));
3854         ret.push_back(entry);
3855     }
3856     return ret;
3857 }
3858
3859 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
3860
3861 UniValue definechain(const UniValue& params, bool fHelp)
3862 {
3863     if (fHelp || params.size() != 1)
3864     {
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"
3868
3869             "\nArguments\n"
3870             "      {\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"
3879             "         {\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"
3885             "         }\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"
3889             "         [{\n"
3890             "            \"networkaddress\" : \"txid\", (string,  optional) internet, TOR, or other supported address for node\n"
3891             "            \"paymentaddress\" : \"n\", (int,     optional) rewards payment address\n"
3892             "          }, .. ]\n"
3893             "      }\n"
3894
3895             "\nResult:\n"
3896             "{\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"
3900             "}\n"
3901
3902             "\nExamples:\n"
3903             + HelpExampleCli("definechain", "jsondefinition")
3904             + HelpExampleRpc("definechain", "jsondefinition")
3905         );
3906     }
3907
3908     CheckPBaaSAPIsValid();
3909
3910     if (!params[0].isObject())
3911     {
3912         throw JSONRPCError(RPC_INVALID_PARAMETER, "JSON object required. see help.");
3913     }
3914     if (!pwalletMain)
3915     {
3916         throw JSONRPCError(RPC_WALLET_ERROR, "must have active wallet to define PBaaS chain");
3917     }
3918
3919     UniValue valStr(UniValue::VSTR);
3920     if (!valStr.read(params[0].write()))
3921     {
3922         throw JSONRPCError(RPC_INVALID_PARAMS, "Invalid characters in blockchain definition");
3923     }
3924
3925     CPBaaSChainDefinition newChain(params[0]);
3926
3927     if (!newChain.IsValid())
3928     {
3929         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain definition. see help.");
3930     }
3931
3932     CPBaaSChainDefinition checkDef;
3933     if (GetChainDefinition(newChain.name, checkDef))
3934     {
3935         throw JSONRPCError(RPC_INVALID_PARAMETER, newChain.name + " chain already defined. see help.");
3936     }
3937
3938     if (!newChain.startBlock || newChain.startBlock < (chainActive.Height() + PBAAS_MINSTARTBLOCKDELTA))
3939     {
3940         newChain.startBlock = chainActive.Height() + PBAAS_MINSTARTBLOCKDELTA;
3941     }
3942
3943     if (newChain.billingPeriod < CPBaaSChainDefinition::MIN_BILLING_PERIOD || (newChain.notarizationReward / newChain.billingPeriod) < CPBaaSChainDefinition::MIN_PER_BLOCK_NOTARIZATION)
3944     {
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");
3948     }
3949
3950     for (int i = 0; i < newChain.rewards.size(); i++)
3951     {
3952         arith_uint256 reward(newChain.rewards[i]), decay(newChain.rewardsDecay[i]), limit(0x7fffffffffffffff);
3953         if (reward * decay > limit)
3954         {
3955             throw JSONRPCError(RPC_INVALID_PARAMS, "reward * decay exceeds 64 bit integer limit of 9,223,372,036,854,775,807\n");
3956         }
3957     }
3958
3959     vector<CRecipient> outputs;
3960
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);
3964
3965     CCcontract_info CC;
3966     CCcontract_info *cp;
3967
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));
3972
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}));
3976
3977     // make the first chain notarization output
3978     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
3979
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;
3983     {
3984         LOCK(cs_main);
3985         auto mmr = chainActive.GetMMR();
3986         auto mmv = CMerkleMountainView<CMMRPowerNode, CChunkedLayer<CMMRPowerNode>, COverlayNodeLayer<CMMRPowerNode, CChain>>(mmr, mmr.size());
3987         mmv.resize(1);
3988         mmvRoot = mmv.GetRoot();
3989         nodePreHash = mmr.GetNode(0).hash;
3990     }
3991
3992     CKeyID pkID;
3993     extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY;
3994     if (USE_EXTERNAL_PUBKEY)
3995     {
3996         CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
3997         if (pubKey.IsFullyValid())
3998         {
3999             pkID = pubKey.GetID();
4000         }
4001     }
4002
4003     CAmount initialToConvert = 0;
4004     CAmount initialConversion = 0;
4005     CAmount initialReserve = 0;
4006     CAmount initialFee = 0;
4007     CAmount conversionFee = 0;
4008
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)
4012     {
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))
4021         {
4022             initialReserve = initialToConvert;
4023         }
4024     }
4025     else
4026     {
4027         newChain.initialcontribution = 0;
4028     }
4029     
4030     CCurrencyState currencyState(newChain.conversion, newChain.premine + initialConversion, initialConversion, 0, initialReserve);
4031
4032     CPBaaSNotarization pbn = CPBaaSNotarization(CPBaaSNotarization::CURRENT_VERSION,
4033                                                 newChain.GetChainID(),
4034                                                 pkID,
4035                                                 0, mmvRoot,
4036                                                 nodePreHash,
4037                                                 ArithToUint256(GetCompactPower(chainActive.Genesis()->nNonce, chainActive.Genesis()->nBits, chainActive.Genesis()->nVersion)),
4038                                                 currencyState,
4039                                                 uint256(), 0,
4040                                                 uint256(), 0,
4041                                                 COpRetProof(),
4042                                                 newChain.nodes);
4043
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}));
4048
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))});
4053
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}));
4057
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
4063     dests.clear();
4064     cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
4065
4066     pk = CPubKey(ParseHex(CC.CChexstr));
4067
4068     CKeyID newChainID(newChain.GetChainID());
4069
4070     // import thread is specific to the chain importing from
4071     dests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_IMPORT)));
4072
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}));
4075
4076     // export - currently only spendable for reserve currency, but added for future capabilities
4077     dests.clear();
4078     cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
4079
4080     pk = CPubKey(ParseHex(CC.CChexstr));
4081     dests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_EXPORT)));
4082
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}));
4085
4086     // make the reserve transfer and fee outputs if there is an initial contribution
4087     if (newChain.initialcontribution)
4088     {
4089         cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
4090         pk = CPubKey(ParseHex(CC.CChexstr));
4091         assert(pk.IsFullyValid());
4092
4093         dests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(newChainID)});
4094
4095         CAmount fee = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(initialToConvert);
4096         CAmount contribution = (initialToConvert + fee) - CReserveTransactionDescriptor::CalculateConversionFee(initialToConvert + fee);
4097         fee += CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
4098
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}));
4102
4103         // if there is a fee output, send it to the payment address
4104         if (initialFee)
4105         {
4106             CTxDestination feeOutAddr(newChain.address);
4107             outputs.push_back(CRecipient({GetScriptForDestination(feeOutAddr), initialFee, false}));
4108         }
4109     }
4110
4111     // create the transaction
4112     CWalletTx wtx;
4113     {
4114         LOCK2(cs_main, pwalletMain->cs_wallet);
4115
4116         CReserveKey reserveKey(pwalletMain);
4117         CAmount fee;
4118         int nChangePos;
4119         string failReason;
4120
4121         if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
4122         {
4123             throw JSONRPCError(RPC_TRANSACTION_ERROR, newChain.name + ": " + failReason);
4124         }
4125     }
4126
4127     UniValue uvret(UniValue::VOBJ);
4128     uvret.push_back(Pair("chaindefinition", CPBaaSChainDefinition(wtx).ToUniValue()));
4129
4130     uvret.push_back(Pair("basenotarization", CPBaaSNotarization(wtx).ToUniValue()));
4131
4132     uvret.push_back(Pair("txid", wtx.GetHash().GetHex()));
4133
4134     UniValue txJSon(UniValue::VOBJ);
4135     TxToJSON(wtx, uint256(), txJSon);
4136     uvret.push_back(Pair("tx",  txJSon));
4137
4138     string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
4139     uvret.push_back(Pair("hex", strHex));
4140
4141     return uvret;
4142 }
4143
4144 UniValue registernamecommitment(const UniValue& params, bool fHelp)
4145 {
4146     if (fHelp || (params.size() < 2 && params.size() > 3))
4147     {
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"
4153
4154             "\nArguments\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"
4159
4160             "\nResult: obj\n"
4161             "{\n"
4162             "    \"txid\" : \"hexid\"\n"
4163             "    \"namereservation\" :\n"
4164             "    {\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"
4170             "    }\n"
4171             "}\n"
4172
4173             "\nExamples:\n"
4174             + HelpExampleCli("registernamecommitment", "\"name\"")
4175             + HelpExampleRpc("registernamecommitment", "\"name\"")
4176         );
4177     }
4178
4179     CheckIdentityAPIsValid();
4180
4181     uint160 parent;
4182     std::string name = CleanName(uni_get_str(params[0]), parent, true);
4183
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]))
4186     {
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 (\\/:*?\"<>|@)");
4188     }
4189
4190     parent = ConnectedChains.ThisChain().GetChainID();
4191
4192     CTxDestination dest = DecodeDestination(uni_get_str(params[1]));
4193     if (dest.which() == COptCCParams::ADDRTYPE_INVALID)
4194     {
4195         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid control address for commitment");
4196     }
4197
4198     // create the transaction with native coin as input
4199     LOCK2(cs_main, pwalletMain->cs_wallet);
4200
4201     CIdentityID referrer;
4202     if (params.size() > 2)
4203     {
4204         CTxDestination referDest = DecodeDestination(uni_get_str(params[2]));
4205         if (referDest.which() != COptCCParams::ADDRTYPE_ID)
4206         {
4207             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid referral identity for commitment, must be a currently registered friendly name or i-address");
4208         }
4209         referrer = CIdentityID(GetDestinationID(referDest));
4210         if (!CIdentity::LookupIdentity(referrer).IsValidUnrevoked())
4211         {
4212             throw JSONRPCError(RPC_INVALID_PARAMETER, "Referral identity for commitment must be a currently valid, unrevoked friendly name or i-address");
4213         }
4214     }
4215
4216     CNameReservation nameRes(name, referrer, GetRandHash());
4217     CCommitmentHash commitment(nameRes.GetCommitment());
4218     
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}});
4221     CWalletTx wtx;
4222
4223     if (CIdentity::LookupIdentity(CIdentity::GetID(name, parent)).IsValid())
4224     {
4225         throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity already exists.");
4226     }
4227
4228     CReserveKey reserveKey(pwalletMain);
4229     CAmount fee;
4230     int nChangePos;
4231     string failReason;
4232
4233     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
4234     {
4235         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create commitment transaction: " + failReason);
4236     }
4237     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4238     {
4239         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4240     }
4241
4242     UniValue ret(UniValue::VOBJ);
4243     ret.push_back(Pair("txid", wtx.GetHash().GetHex()));
4244     ret.push_back(Pair("namereservation", nameRes.ToUniValue()));
4245     return ret;
4246 }
4247
4248 UniValue registeridentity(const UniValue& params, bool fHelp)
4249 {
4250     if (fHelp || params.size() < 1 || params.size() > 2)
4251     {
4252         throw runtime_error(
4253             "registeridentity \"jsonidregistration\" feeoffer\n"
4254             "\n\n"
4255
4256             "\nArguments\n"
4257             "{\n"
4258             "    \"txid\" : \"hexid\",          (hex)    the transaction ID of the name committment for this ID name\n"
4259             "    \"namereservation\" :\n"
4260             "    {\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"
4264             "    },\n"
4265             "    \"identity\" :\n"
4266             "    {\n"
4267             "        \"name\": \"namestr\",     (string) the unique name for this identity\n"
4268             "        ...\n"
4269             "    }\n"
4270             "}\n"
4271             "feeoffer                           (amount, optional) amount to offer miner/staker for the registration fee, if missing, uses standard price\n\n"
4272
4273             "\nResult:\n"
4274             "   transactionid                   (hexstr)\n"
4275
4276             "\nExamples:\n"
4277             + HelpExampleCli("registeridentity", "jsonidregistration")
4278             + HelpExampleRpc("registeridentity", "jsonidregistration")
4279         );
4280     }
4281
4282     CheckIdentityAPIsValid();
4283
4284     // all names have a parent of the current chain
4285     uint160 parent = ConnectedChains.ThisChain().GetChainID();
4286
4287     uint256 txid = uint256S(uni_get_str(find_value(params[0], "txid")));
4288     CNameReservation reservation(find_value(params[0], "namereservation"));
4289
4290     CIdentity newID(find_value(params[0], "identity"));
4291     if (!newID.IsValid())
4292     {
4293         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity");
4294     }
4295
4296     CAmount feeOffer;
4297     CAmount minFeeOffer = reservation.referral.IsNull() ? CIdentity::FullRegistrationAmount() : CIdentity::ReferredRegistrationAmount();
4298
4299     if (params.size() > 1)
4300     {
4301         feeOffer = AmountFromValue(params[1]);
4302     }
4303     else
4304     {
4305         feeOffer = minFeeOffer;
4306     }
4307
4308     if (feeOffer < minFeeOffer)
4309     {
4310         throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee offer must be at least " + ValueFromAmount(CIdentity::MinRegistrationAmount()).write());
4311     }
4312
4313     uint160 impliedParent;
4314     if (txid.IsNull() || CleanName(reservation.name, impliedParent) != CleanName(newID.name, impliedParent) || !impliedParent.IsNull())
4315     {
4316         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity description or mismatched reservation.");
4317     }
4318
4319     // lookup commitment to be sure that we can register this identity
4320     LOCK2(cs_main, pwalletMain->cs_wallet);
4321
4322     uint256 hashBlk;
4323     uint32_t commitmentHeight;
4324     CTransaction txOut;
4325     CCommitmentHash ch;
4326     int commitmentOutput;
4327
4328     // must be present and in a mined block
4329     {
4330         LOCK(mempool.cs);
4331         if (!myGetTransaction(txid, txOut, hashBlk) || hashBlk.IsNull())
4332         {
4333             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment transaction id");
4334         }
4335
4336         auto indexIt = mapBlockIndex.find(hashBlk);
4337         if (indexIt == mapBlockIndex.end() || indexIt->second->GetHeight() > chainActive.Height() || chainActive[indexIt->second->GetHeight()]->GetBlockHash() != indexIt->second->GetBlockHash())
4338         {
4339             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment");
4340         }
4341
4342         commitmentHeight = indexIt->second->GetHeight();
4343
4344         for (int i = 0; i < txOut.vout.size(); i++)
4345         {
4346             COptCCParams p;
4347             if (txOut.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_COMMITMENT && p.vData.size())
4348             {
4349                 commitmentOutput = i;
4350                 ::FromVector(p.vData[0], ch);
4351                 break;
4352             }
4353         }
4354         if (ch.hash.IsNull())
4355         {
4356             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment hash");
4357         }
4358     }
4359
4360     if (ch.hash != reservation.GetCommitment().hash)
4361     {
4362         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment salt or referral ID");
4363     }
4364
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())
4367     {
4368         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid to specify parent or qualified name when creating an identity. Parent is determined by the current blockchain.");
4369     }
4370
4371     newID.parent = parent;
4372
4373     CIdentity dupID = newID.LookupIdentity(newID.GetID());
4374     if (dupID.IsValid())
4375     {
4376         throw JSONRPCError(RPC_VERIFY_ALREADY_IN_CHAIN, "Identity already exists.");
4377     }
4378
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);
4382
4383     if (!recoveryAuth.IsValidUnrevoked() || !revocationAuth.IsValidUnrevoked())
4384     {
4385         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked recovery, or revocation identity.");
4386     }
4387
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();
4391
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}});
4396
4397     // add referrals, Verus supports referrals
4398     if ((ConnectedChains.ThisChain().IDReferrals() || IsVerusActive()) && !reservation.referral.IsNull())
4399     {
4400         uint32_t referralHeight;
4401         CTxIn referralTxIn;
4402         CTransaction referralIdTx;
4403         auto referralIdentity =  newID.LookupIdentity(reservation.referral, commitmentHeight - 1);
4404         if (referralIdentity.IsValidUnrevoked())
4405         {
4406             if (!newID.LookupFirstIdentity(reservation.referral, &referralHeight, &referralTxIn, &referralIdTx).IsValid())
4407             {
4408                 throw JSONRPCError(RPC_DATABASE_ERROR, "Database or blockchain data error, \"" + referralIdentity.name + "\" seems valid, but first instance is not found in index");
4409             }
4410
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++)
4416             {
4417                 CTxDestination nextID;
4418                 COptCCParams p, master;
4419
4420                 if (referralIdTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && 
4421                     p.IsValid() && 
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)))
4429                 {
4430                     outputs.push_back({newID.TransparentOutput(CIdentityID(GetDestinationID(p.vKeys[0]))), CIdentity::ReferralAmount(), false});
4431                     feeOffer -= CIdentity::ReferralAmount();
4432                 }
4433                 else
4434                 {
4435                     break;
4436                 }
4437             }
4438         }
4439         else
4440         {
4441             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked referral identity at time of commitment");
4442         }
4443     }
4444
4445     CScript reservationOutScript = MakeMofNCCScript(condObj, &resIndexDest);
4446     outputs.push_back({reservationOutScript, CNameReservation::DEFAULT_OUTPUT_AMOUNT, false});
4447
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});
4451
4452     CWalletTx wtx;
4453
4454     CReserveKey reserveKey(pwalletMain);
4455     CAmount fee;
4456     int nChangePos;
4457     string failReason;
4458
4459     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4460     {
4461         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create identity transaction: " + failReason);
4462     }
4463
4464     // add commitment output
4465     CMutableTransaction mtx(wtx);
4466     mtx.vin.push_back(CTxIn(txid, commitmentOutput));
4467
4468     // remove the fee offer output
4469     mtx.vout.pop_back();
4470
4471     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4472
4473     // now sign
4474     CCoinsViewCache view(pcoinsTip);
4475     for (int i = 0; i < wtx.vin.size(); i++)
4476     {
4477         bool signSuccess;
4478         SignatureData sigdata;
4479
4480         CCoins coins;
4481         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4482         {
4483             break;
4484         }
4485
4486         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4487
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()));
4489
4490         if (!signSuccess)
4491         {
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");
4495         } else {
4496             UpdateTransaction(mtx, i, sigdata);
4497         }
4498     }
4499     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4500
4501     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4502     {
4503         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4504     }
4505
4506     // including definitions and claims thread
4507     return UniValue(wtx.GetHash().GetHex());
4508 }
4509
4510 UniValue updateidentity(const UniValue& params, bool fHelp)
4511 {
4512     if (fHelp || params.size() < 1 || params.size() > 2)
4513     {
4514         throw runtime_error(
4515             "updateidentity \"jsonidentity\" (returntx)\n"
4516             "\n\n"
4517
4518             "\nArguments\n"
4519             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
4520
4521             "\nResult:\n"
4522
4523             "\nExamples:\n"
4524             + HelpExampleCli("updateidentity", "\'{\"name\" : \"myname\"}\'")
4525             + HelpExampleRpc("updateidentity", "\'{\"name\" : \"myname\"}\'")
4526         );
4527     }
4528
4529     CheckIdentityAPIsValid();
4530
4531     // get identity
4532     bool returnTx = false;
4533     CIdentity newID(params[0]);
4534
4535     if (!newID.IsValid())
4536     {
4537         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
4538     }
4539
4540     if (params.size() > 1)
4541     {
4542         returnTx = uni_get_bool(params[1], false);
4543     }
4544
4545     CTxIn idTxIn;
4546     CIdentity oldID;
4547     uint32_t idHeight;
4548
4549     LOCK2(cs_main, pwalletMain->cs_wallet);
4550
4551     if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
4552     {
4553         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
4554     }
4555
4556     // create the identity definition transaction
4557     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4558     CWalletTx wtx;
4559
4560     CReserveKey reserveKey(pwalletMain);
4561     CAmount fee;
4562     int nChangePos;
4563     string failReason;
4564
4565     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4566     {
4567         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
4568     }
4569
4570     CMutableTransaction mtx(wtx);
4571     mtx.vin.push_back(idTxIn);
4572     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4573
4574     // now sign
4575     CCoinsViewCache view(pcoinsTip);
4576     for (int i = 0; i < wtx.vin.size(); i++)
4577     {
4578         bool signSuccess;
4579         SignatureData sigdata;
4580
4581         CCoins coins;
4582         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4583         {
4584             break;
4585         }
4586
4587         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4588
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()));
4590
4591         if (!signSuccess)
4592         {
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");
4596         } else {
4597             UpdateTransaction(mtx, i, sigdata);
4598         }
4599     }
4600     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4601
4602     if (returnTx)
4603     {
4604         return EncodeHexTx(wtx);
4605     }
4606     else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4607     {
4608         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4609     }
4610     return wtx.GetHash().GetHex();
4611 }
4612
4613 UniValue revokeidentity(const UniValue& params, bool fHelp)
4614 {
4615     if (fHelp || params.size() < 1 || params.size() > 2)
4616     {
4617         throw runtime_error(
4618             "revokeidentity \"nameorID\" (returntx)\n"
4619             "\n\n"
4620
4621             "\nArguments\n"
4622             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
4623
4624             "\nResult:\n"
4625
4626             "\nExamples:\n"
4627             + HelpExampleCli("revokeidentity", "\"nameorID\"")
4628             + HelpExampleRpc("revokeidentity", "\"nameorID\"")
4629         );
4630     }
4631
4632     CheckIdentityAPIsValid();
4633
4634     // get identity
4635     bool returnTx = false;
4636     CTxDestination idDest = DecodeDestination(uni_get_str(params[0]));
4637
4638     if (idDest.which() != COptCCParams::ADDRTYPE_ID)
4639     {
4640         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
4641     }
4642
4643     CIdentityID idID(GetDestinationID(idDest));
4644
4645     if (params.size() > 1)
4646     {
4647         returnTx = uni_get_bool(params[1], false);
4648     }
4649
4650     CTxIn idTxIn;
4651     CIdentity oldID;
4652     uint32_t idHeight;
4653
4654     LOCK2(cs_main, pwalletMain->cs_wallet);
4655
4656     if (!(oldID = CIdentity::LookupIdentity(idID, 0, &idHeight, &idTxIn)).IsValid())
4657     {
4658         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + EncodeDestination(idID));
4659     }
4660
4661     CIdentity newID(oldID);
4662     newID.Revoke();
4663
4664     // create the identity definition transaction
4665     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4666     CWalletTx wtx;
4667
4668     CReserveKey reserveKey(pwalletMain);
4669     CAmount fee;
4670     int nChangePos;
4671     string failReason;
4672
4673     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4674     {
4675         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
4676     }
4677     CMutableTransaction mtx(wtx);
4678
4679     // add the spend of the last ID transaction output
4680     mtx.vin.push_back(idTxIn);
4681
4682     // all of the reservation output is actually the fee offer, so zero the output
4683     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4684
4685     // now sign
4686     CCoinsViewCache view(pcoinsTip);
4687     for (int i = 0; i < wtx.vin.size(); i++)
4688     {
4689         bool signSuccess;
4690         SignatureData sigdata;
4691
4692         CCoins coins;
4693         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4694         {
4695             break;
4696         }
4697
4698         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4699
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()));
4701
4702         if (!signSuccess)
4703         {
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");
4707         } else {
4708             UpdateTransaction(mtx, i, sigdata);
4709         }
4710     }
4711     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4712
4713     if (returnTx)
4714     {
4715         return EncodeHexTx(wtx);
4716     }
4717     else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4718     {
4719         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4720     }
4721     return wtx.GetHash().GetHex();
4722 }
4723
4724 UniValue recoveridentity(const UniValue& params, bool fHelp)
4725 {
4726     if (fHelp || params.size() < 1 || params.size() > 2)
4727     {
4728         throw runtime_error(
4729             "recoveridentity \"jsonidentity\" (returntx)\n"
4730             "\n\n"
4731
4732             "\nArguments\n"
4733             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
4734
4735             "\nResult:\n"
4736
4737             "\nExamples:\n"
4738             + HelpExampleCli("recoveridentity", "\'{\"name\" : \"myname\"}\'")
4739             + HelpExampleRpc("recoveridentity", "\'{\"name\" : \"myname\"}\'")
4740         );
4741     }
4742     CheckIdentityAPIsValid();
4743
4744     // get identity
4745     bool returnTx = false;
4746     CIdentity newID(params[0]);
4747
4748     if (!newID.IsValid())
4749     {
4750         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
4751     }
4752
4753     if (params.size() > 1)
4754     {
4755         returnTx = uni_get_bool(params[1], false);
4756     }
4757
4758     CTxIn idTxIn;
4759     CIdentity oldID;
4760     uint32_t idHeight;
4761
4762     LOCK2(cs_main, pwalletMain->cs_wallet);
4763
4764     if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
4765     {
4766         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
4767     }
4768
4769     if (!oldID.IsRevoked())
4770     {
4771         throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity must be revoked in order to recover : " + newID.name);
4772     }
4773
4774     // create the identity definition transaction
4775     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
4776     CWalletTx wtx;
4777
4778     CReserveKey reserveKey(pwalletMain);
4779     CAmount fee;
4780     int nChangePos;
4781     string failReason;
4782
4783     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
4784     {
4785         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
4786     }
4787     CMutableTransaction mtx(wtx);
4788
4789     // add the spend of the last ID transaction output
4790     mtx.vin.push_back(idTxIn);
4791
4792     // all of the reservation output is actually the fee offer, so zero the output
4793     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4794
4795     // now sign
4796     CCoinsViewCache view(pcoinsTip);
4797     for (int i = 0; i < wtx.vin.size(); i++)
4798     {
4799         bool signSuccess;
4800         SignatureData sigdata;
4801
4802         CCoins coins;
4803         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
4804         {
4805             break;
4806         }
4807
4808         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
4809
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()));
4811
4812         if (!signSuccess)
4813         {
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");
4817         } else {
4818             UpdateTransaction(mtx, i, sigdata);
4819         }
4820     }
4821     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
4822
4823     if (returnTx)
4824     {
4825         return EncodeHexTx(wtx);
4826     }
4827     else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
4828     {
4829         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
4830     }
4831     return wtx.GetHash().GetHex();
4832 }
4833
4834 UniValue getidentity(const UniValue& params, bool fHelp)
4835 {
4836     if (fHelp || params.size() != 1)
4837     {
4838         throw runtime_error(
4839             "getidentity \"name\"\n"
4840             "\n\n"
4841
4842             "\nArguments\n"
4843
4844             "\nResult:\n"
4845
4846             "\nExamples:\n"
4847             + HelpExampleCli("getidentity", "\"name@\"")
4848             + HelpExampleRpc("getidentity", "\"name@\"")
4849         );
4850     }
4851
4852     CheckIdentityAPIsValid();
4853
4854     CTxDestination idID = DecodeDestination(uni_get_str(params[0]));
4855     if (idID.which() != COptCCParams::ADDRTYPE_ID)
4856     {
4857         throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity parameter must be valid friendly name or identity address: \"" + uni_get_str(params[0]) + "\"");
4858     }
4859
4860     CTxIn idTxIn;
4861     uint32_t height;
4862
4863     CIdentity identity;
4864     bool canSign = false, canSpend = false, found = false;
4865
4866     if (pwalletMain)
4867     {
4868         LOCK(pwalletMain->cs_wallet);
4869         uint256 txID;
4870         std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
4871         if (pwalletMain->GetIdentity(GetDestinationID(idID), keyAndIdentity))
4872         {
4873             found = true;
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);
4877         }
4878     }
4879
4880     identity = CIdentity::LookupIdentity(CIdentityID(GetDestinationID(idID)), 0, &height, &idTxIn);
4881
4882     UniValue ret(UniValue::VOBJ);
4883
4884     uint160 parent;
4885     if (identity.IsValid() && identity.name == CleanName(identity.name, parent, true))
4886     {
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));
4894         return ret;
4895     }
4896     else
4897     {
4898         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Identity not found");
4899     }
4900 }
4901
4902 UniValue IdentityPairToUni(const std::pair<CIdentityMapKey, CIdentityMapValue *> &identity)
4903 {
4904     UniValue oneID(UniValue::VOBJ);
4905
4906     if (identity.first.IsValid() && identity.second->IsValid())
4907     {
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())
4912         {
4913             oneID.push_back(Pair("status", "revoked"));
4914             oneID.push_back(Pair("canspendfor", 0));
4915             oneID.push_back(Pair("cansignfor", 0));
4916         }
4917         else
4918         {
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)));
4922         }
4923     }
4924     return oneID;
4925 }
4926
4927 UniValue listidentities(const UniValue& params, bool fHelp)
4928 {
4929     if (fHelp || params.size() > 3)
4930     {
4931         throw runtime_error(
4932             "listidentities (includecansign) (includewatchonly)\n"
4933             "\n\n"
4934
4935             "\nArguments\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"
4939
4940             "\nResult:\n"
4941
4942             "\nExamples:\n"
4943             + HelpExampleCli("listidentities", "\'{\"name\" : \"myname\"}\'")
4944             + HelpExampleRpc("listidentities", "\'{\"name\" : \"myname\"}\'")
4945         );
4946     }
4947
4948     CheckIdentityAPIsValid();
4949
4950     std::vector<std::pair<CIdentityMapKey, CIdentityMapValue *>> mine, imsigner, notmine;
4951
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;
4955
4956     if (pwalletMain->GetIdentities(mine, imsigner, notmine))
4957     {
4958         UniValue ret(UniValue::VARR);
4959         if (includeCanSpend)
4960         {
4961             for (auto identity : mine)
4962             {
4963                 uint160 parent;
4964                 if (identity.second->IsValid() && identity.second->name == CleanName(identity.second->name, parent, true))
4965                 {
4966                     ret.push_back(IdentityPairToUni(identity));
4967                 }
4968             }
4969         }
4970         if (includeCanSign)
4971         {
4972             for (auto identity : imsigner)
4973             {
4974                 uint160 parent;
4975                 if (identity.second->IsValid() && identity.second->name == CleanName(identity.second->name, parent, true))
4976                 {
4977                     ret.push_back(IdentityPairToUni(identity));
4978                 }
4979             }
4980         }
4981         if (includeWatchOnly)
4982         {
4983             for (auto identity : notmine)
4984             {
4985                 uint160 parent;
4986                 if (identity.second->IsValid() && identity.second->name == CleanName(identity.second->name, parent, true))
4987                 {
4988                     ret.push_back(IdentityPairToUni(identity));
4989                 }
4990             }
4991         }
4992         return ret;
4993     }
4994     else
4995     {
4996         return NullUniValue;
4997     }
4998 }
4999
5000 UniValue addmergedblock(const UniValue& params, bool fHelp)
5001 {
5002     if (fHelp || params.size() != 5)
5003     {
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"
5009
5010             "\nArguments\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"
5016
5017             "\nResult:\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"
5020
5021             "\nExamples:\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)}\'")
5024         );
5025     }
5026
5027     CheckPBaaSAPIsValid();
5028
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();
5031     if (name == "")
5032     {
5033         throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide chain name to merge mine");
5034     }
5035
5036     string rpchost = params[2].get_str();
5037     int32_t rpcport = params[3].get_int();
5038     string rpcuserpass = params[4].get_str();
5039
5040     if (rpchost == "" || rpcport == 0 || rpcuserpass == "")
5041     {
5042         throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide valid RPC connection parameters to merge mine");
5043     }
5044
5045     uint160 chainID = CCrossChainRPCData::GetChainID(name);
5046
5047     // confirm data from blockchain
5048     CRPCChainData chainData;
5049     CPBaaSChainDefinition chainDef;
5050     if (ConnectedChains.GetChainInfo(chainID, chainData))
5051     {
5052         chainDef = chainData.chainDefinition;
5053     }
5054
5055     if (!chainDef.IsValid() && !GetChainDefinition(name, chainDef))
5056     {
5057         throw JSONRPCError(RPC_INVALID_PARAMETER, "chain not found");
5058     }
5059
5060     CBlock blk;
5061
5062     if (!DecodeHexBlk(blk, params[0].get_str()))
5063         return "deserialize-invalid";
5064
5065     CPBaaSMergeMinedChainData blkData = CPBaaSMergeMinedChainData(chainDef, rpchost, rpcport, rpcuserpass, blk);
5066
5067     return ConnectedChains.AddMergedBlock(blkData) ? NullUniValue : "blocksfull";
5068 }
5069
5070 UniValue submitmergedblock(const UniValue& params, bool fHelp)
5071 {
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"
5083
5084             "\nArguments\n"
5085             "1. \"hexdata\"    (string, required) the hex-encoded block data to submit\n"
5086             "2. \"jsonparametersobject\"     (string, optional) object of optional parameters\n"
5087             "    {\n"
5088             "      \"workid\" : \"id\"    (string, optional) if the server provided a workid, it MUST be included with submissions\n"
5089             "    }\n"
5090             "\nResult:\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"
5097             "\nExamples:\n"
5098             + HelpExampleCli("submitblock", "\"mydata\"")
5099             + HelpExampleRpc("submitblock", "\"mydata\"")
5100         );
5101
5102     CheckPBaaSAPIsValid();
5103
5104     CBlock block;
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");
5108
5109     uint256 hash = block.GetHash();
5110     bool fBlockPresent = false;
5111     {
5112         LOCK(cs_main);
5113         BlockMap::iterator mi = mapBlockIndex.find(hash);
5114         if (mi != mapBlockIndex.end()) {
5115             CBlockIndex *pindex = mi->second;
5116             if (pindex)
5117             {
5118                 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
5119                     return "duplicate";
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;
5124             }
5125         }
5126     }
5127
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);
5134     if (fBlockPresent)
5135     {
5136         if (fAccepted && !sc.found)
5137             return "duplicate-inconclusive";
5138         return "duplicate";
5139     }
5140     if (fAccepted)
5141     {
5142         if (!sc.found)
5143             return "inconclusive";
5144         state = sc.state;
5145     }
5146     return BIP22ValidationResult(state);
5147 }
5148
5149 UniValue getmergedblocktemplate(const UniValue& params, bool fHelp)
5150 {
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"
5157
5158             "\nArguments:\n"
5159             "1. \"jsonrequestobject\"       (string, optional) A json object in the following spec\n"
5160             "     {\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"
5164             "           ,...\n"
5165             "         ]\n"
5166             "     }\n"
5167             "\n"
5168
5169             "\nResult:\n"
5170             "{\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"
5175             "      {\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"
5180             "             ,...\n"
5181             "         ],\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"
5185             "      }\n"
5186             "      ,...\n"
5187             "  ],\n"
5188 //            "  \"coinbaseaux\" : {                  (json object) data that should be included in the coinbase's scriptSig content\n"
5189 //            "      \"flags\" : \"flags\"            (string) \n"
5190 //            "  },\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"
5197             "     ,...\n"
5198             "  ],\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"
5205             "}\n"
5206
5207             "\nExamples:\n"
5208             + HelpExampleCli("getblocktemplate", "")
5209             + HelpExampleRpc("getblocktemplate", "")
5210          );
5211
5212     CheckPBaaSAPIsValid();
5213
5214     LOCK(cs_main);
5215
5216     // Wallet or miner address is required because we support coinbasetxn
5217     if (GetArg("-mineraddress", "").empty()) {
5218 #ifdef ENABLE_WALLET
5219         if (!pwalletMain) {
5220             throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
5221         }
5222 #else
5223         throw JSONRPCError(RPC_METHOD_NOT_FOUND, "verusd compiled without wallet and -mineraddress not set");
5224 #endif
5225     }
5226
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)
5232     {
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())
5238         {
5239             /* Do nothing */
5240         }
5241         else
5242             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
5243         lpval = find_value(oparam, "longpollid");
5244
5245         if (strMode == "proposal")
5246         {
5247             const UniValue& dataval = find_value(oparam, "data");
5248             if (!dataval.isStr())
5249                 throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
5250
5251             CBlock block;
5252             if (!DecodeHexBlk(block, dataval.get_str()))
5253                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
5254
5255             uint256 hash = block.GetHash();
5256             BlockMap::iterator mi = mapBlockIndex.find(hash);
5257             if (mi != mapBlockIndex.end()) {
5258                 CBlockIndex *pindex = mi->second;
5259                 if (pindex)
5260                 {
5261                     if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
5262                         return "duplicate";
5263                     if (pindex->nStatus & BLOCK_FAILED_MASK)
5264                         return "duplicate-invalid";
5265                 }
5266                 return "duplicate-inconclusive";
5267             }
5268
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);
5276         }
5277     }
5278
5279     if (strMode != "template")
5280         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
5281
5282     bool fvNodesEmpty;
5283     {
5284         LOCK(cs_vNodes);
5285         fvNodesEmpty = vNodes.empty();
5286     }
5287     if (Params().MiningRequiresPeers() && (IsNotInSync() || fvNodesEmpty))
5288     {
5289         throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Cannot get a block template while no peers are connected or chain not in sync!");
5290     }
5291
5292     //if (IsInitialBlockDownload())
5293      //   throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks...");
5294
5295     static unsigned int nTransactionsUpdatedLast;
5296
5297     if (!lpval.isNull())
5298     {
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;
5303
5304         if (lpval.isStr())
5305         {
5306             // Format: <hashBestChain><nTransactionsUpdatedLast>
5307             std::string lpstr = lpval.get_str();
5308
5309             hashWatchedChain.SetHex(lpstr.substr(0, 64));
5310             nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
5311         }
5312         else
5313         {
5314             // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
5315             hashWatchedChain = chainActive.LastTip()->GetBlockHash();
5316             nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
5317         }
5318
5319         // Release the wallet and main lock while waiting
5320         LEAVE_CRITICAL_SECTION(cs_main);
5321         {
5322             checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
5323
5324             boost::unique_lock<boost::mutex> lock(csBestBlock);
5325             while (chainActive.LastTip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
5326             {
5327                 if (!cvBlockChange.timed_wait(lock, checktxtime))
5328                 {
5329                     // Timeout: Check transactions for update
5330                     if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
5331                         break;
5332                     checktxtime += boost::posix_time::seconds(10);
5333                 }
5334             }
5335         }
5336         ENTER_CRITICAL_SECTION(cs_main);
5337
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?
5341     }
5342
5343     // Update block
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))
5349     {
5350         // Clear pindexPrev so future calls make a new block, despite any failures from here on
5351         pindexPrev = NULL;
5352
5353         // Store the pindexBest used before CreateNewBlockWithKey, to avoid races
5354         nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
5355         CBlockIndex* pindexPrevNew = chainActive.LastTip();
5356         nStart = GetTime();
5357
5358         // Create new block
5359         if(pblocktemplate)
5360         {
5361             delete pblocktemplate;
5362             pblocktemplate = NULL;
5363         }
5364 #ifdef ENABLE_WALLET
5365         CReserveKey reservekey(pwalletMain);
5366         pblocktemplate = CreateNewBlockWithKey(reservekey,chainActive.LastTip()->GetHeight()+1,KOMODO_MAXGPUCOUNT);
5367 #else
5368         pblocktemplate = CreateNewBlockWithKey();
5369 #endif
5370         if (!pblocktemplate)
5371             throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory or no available utxo for staking");
5372
5373         // Need to update only after we know CreateNewBlockWithKey succeeded
5374         pindexPrev = pindexPrevNew;
5375     }
5376     CBlock* pblock = &pblocktemplate->block; // pointer for convenience
5377
5378     // Update nTime
5379     UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
5380     pblock->nNonce = uint256();
5381
5382     UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
5383
5384     UniValue txCoinbase = NullUniValue;
5385     UniValue transactions(UniValue::VARR);
5386     map<uint256, int64_t> setTxIndex;
5387     int i = 0;
5388     BOOST_FOREACH (const CTransaction& tx, pblock->vtx) {
5389         uint256 txHash = tx.GetHash();
5390         setTxIndex[txHash] = i++;
5391
5392         if (tx.IsCoinBase() && !coinbasetxn)
5393             continue;
5394
5395         UniValue entry(UniValue::VOBJ);
5396
5397         entry.push_back(Pair("data", EncodeHexTx(tx)));
5398
5399         entry.push_back(Pair("hash", txHash.GetHex()));
5400
5401         UniValue deps(UniValue::VARR);
5402         BOOST_FOREACH (const CTxIn &in, tx.vin)
5403         {
5404             if (setTxIndex.count(in.prevout.hash))
5405                 deps.push_back(setTxIndex[in.prevout.hash]);
5406         }
5407         entry.push_back(Pair("depends", deps));
5408
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]));
5412
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));
5418             //}
5419             CAmount nReward = GetBlockSubsidy(chainActive.LastTip()->GetHeight()+1, Params().GetConsensus());
5420             entry.push_back(Pair("coinbasevalue", nReward));
5421             entry.push_back(Pair("required", true));
5422             txCoinbase = entry;
5423         } else
5424             transactions.push_back(entry);
5425     }
5426
5427     UniValue aux(UniValue::VOBJ);
5428     aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
5429
5430     arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
5431
5432     static UniValue aMutable(UniValue::VARR);
5433     if (aMutable.empty())
5434     {
5435         aMutable.push_back("time");
5436         aMutable.push_back("transactions");
5437         aMutable.push_back("prevblock");
5438     }
5439
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));
5446     if (coinbasetxn) {
5447         assert(txCoinbase.isObject());
5448         result.push_back(Pair("coinbasetxn", txCoinbase));
5449     } else {
5450         result.push_back(Pair("coinbaseaux", aux));
5451         result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
5452     }
5453     result.push_back(Pair("longpollid", chainActive.LastTip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
5454     if ( ASSETCHAINS_STAKED != 0 )
5455     {
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)));
5471
5472     //fprintf(stderr,"return complete template\n");
5473     return result;
5474 }
5475
5476 static const CRPCCommand commands[] =
5477 { //  category              name                      actor (function)         okSafeMode
5478   //  --------------------- ------------------------  -----------------------  ----------
5479     { "identity",     "registernamecommitment",       &registernamecommitment, true  },
5480     { "identity",     "registeridentity",             &registeridentity,       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  }
5506 };
5507
5508 void RegisterPBaaSRPCCommands(CRPCTable &tableRPC)
5509 {
5510     for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
5511         tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
5512 }
This page took 0.345895 seconds and 4 git commands to generate.