Conversion fixes
[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 "transaction_builder.h"
38
39 using namespace std;
40
41 extern uint32_t ASSETCHAINS_ALGO;
42 extern int32_t ASSETCHAINS_EQUIHASH, ASSETCHAINS_LWMAPOS;
43 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
44 extern uint64_t ASSETCHAINS_STAKED;
45 extern int32_t KOMODO_MININGTHREADS;
46 extern bool VERUS_MINTBLOCKS;
47 extern uint8_t NOTARY_PUBKEY33[33];
48 extern uint160 ASSETCHAINS_CHAINID;
49 extern uint160 VERUS_CHAINID;
50 extern std::string VERUS_CHAINNAME;
51 extern int32_t USE_EXTERNAL_PUBKEY;
52 extern std::string NOTARY_PUBKEY;
53
54 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
55 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
56 extern int64_t ASSETCHAINS_SUPPLY;
57 extern uint64_t ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
58 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
59 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
60 extern std::string VERUS_CHAINNAME;
61
62
63 arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
64
65 std::set<uint160> ClosedPBaaSChains({});
66
67 // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
68 static UniValue BIP22ValidationResult(const CValidationState& state)
69 {
70     if (state.IsValid())
71         return NullUniValue;
72
73     std::string strRejectReason = state.GetRejectReason();
74     if (state.IsError())
75         throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
76     if (state.IsInvalid())
77     {
78         if (strRejectReason.empty())
79             return "rejected";
80         return strRejectReason;
81     }
82     // Should be impossible
83     return "valid?";
84 }
85
86 class submitblock_StateCatcher : public CValidationInterface
87 {
88 public:
89     uint256 hash;
90     bool found;
91     CValidationState state;
92
93     submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {};
94
95 protected:
96     virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) {
97         if (block.GetHash() != hash)
98             return;
99         found = true;
100         state = stateIn;
101     };
102 };
103
104 bool GetCurrencyDefinition(uint160 chainID, CCurrencyDefinition &chainDef, int32_t *pDefHeight)
105 {
106     LOCK(cs_main);
107
108     if (chainID == ConnectedChains.ThisChain().GetID())
109     {
110         chainDef = ConnectedChains.ThisChain();
111         if (pDefHeight)
112         {
113             *pDefHeight = 0;
114         }
115         return true;
116     }
117     else if (!IsVerusActive())
118     {
119         if (ConnectedChains.NotaryChain().IsValid() && (chainID == ConnectedChains.NotaryChain().chainDefinition.GetID()))
120         {
121             chainDef = ConnectedChains.NotaryChain().chainDefinition;
122             if (pDefHeight)
123             {
124                 *pDefHeight = 0;
125             }
126             return true;
127         }
128     }
129
130     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
131     CCurrencyDefinition foundDef;
132
133     if (!ClosedPBaaSChains.count(chainID) &&
134         GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_CURRENCY_DEFINITION)), 1, unspentOutputs))
135     {
136         for (auto &currencyDefOut : unspentOutputs)
137         {
138             if ((foundDef = CCurrencyDefinition(currencyDefOut.second.script)).IsValid())
139             {
140                 chainDef = foundDef;
141                 if (pDefHeight)
142                 {
143                     *pDefHeight = currencyDefOut.second.blockHeight;
144                 }
145                 break;
146             }
147         }
148     }
149     return foundDef.IsValid();
150 }
151
152 bool GetCurrencyDefinition(string &name, CCurrencyDefinition &chainDef)
153 {
154     return GetCurrencyDefinition(CCrossChainRPCData::GetID(name), chainDef);
155 }
156
157 CTxDestination ValidateDestination(const std::string &destStr)
158 {
159     CTxDestination destination = DecodeDestination(destStr);
160     CTransferDestination transferDestination;
161     if (destination.which() == COptCCParams::ADDRTYPE_ID)
162     {
163         AssertLockHeld(cs_main);
164         if (!CIdentity::LookupIdentity(GetDestinationID(destination)).IsValid())
165         {
166             return CTxDestination();
167         }
168     }
169     return destination;
170 }
171
172 // set default peer nodes in the current connected chains
173 bool SetPeerNodes(const UniValue &nodes)
174 {
175     if (!nodes.isArray() || nodes.size() == 0)
176     {
177         return false;
178     }
179
180     LOCK(ConnectedChains.cs_mergemining);
181     ConnectedChains.defaultPeerNodes.clear();
182
183     for (int i = 0; i < nodes.size(); i++)
184     {
185         CNodeData oneNode(nodes[i]);
186         if (oneNode.networkAddress != "")
187         {
188             ConnectedChains.defaultPeerNodes.push_back(oneNode);
189         }
190     }
191     // set all command line parameters into mapArgs from chain definition
192     vector<string> nodeStrs;
193     for (auto node : ConnectedChains.defaultPeerNodes)
194     {
195         nodeStrs.push_back(node.networkAddress);
196     }
197     if (nodeStrs.size())
198     {
199         mapMultiArgs["-seednode"] = nodeStrs;
200     }
201     if (int port = ConnectedChains.GetThisChainPort())
202     {
203         mapArgs["-port"] = to_string(port);
204     }
205     return true;
206 }
207
208 // adds the chain definition for this chain and nodes as well
209 // this also sets up the notarization chain, if there is one
210 uint256 CurrencyDefHash()
211 {
212     return ::GetHash(ConnectedChains.ThisChain());
213 }
214
215 // adds the chain definition for this chain and nodes as well
216 // this also sets up the notarization chain, if there is one
217 bool SetThisChain(const UniValue &chainDefinition)
218 {
219     ConnectedChains.ThisChain() = CCurrencyDefinition(chainDefinition);
220     if (!ConnectedChains.ThisChain().IsValid())
221     {
222         return false;
223     }
224     SetPeerNodes(find_value(chainDefinition, "nodes"));
225
226     if (!IsVerusActive())
227     {
228         CCurrencyDefinition &notaryChainDef = ConnectedChains.notaryChain.chainDefinition;
229         // we set the notary chain to either Verus or VerusTest
230         notaryChainDef.nVersion = PBAAS_VERSION;
231         if (PBAAS_TESTMODE)
232         {
233             // setup Verus test parameters
234             notaryChainDef.name = "VRSCTEST";
235             notaryChainDef.preAllocation = {std::make_pair(uint160(), 5000000000000000)};
236             notaryChainDef.rewards = std::vector<int64_t>({2400000000});
237             notaryChainDef.rewardsDecay = std::vector<int64_t>({0});
238             Split(GetArg("-ac_halving",""),  ASSETCHAINS_HALVING, 0);
239             notaryChainDef.halving = std::vector<int32_t>({(int32_t)(ASSETCHAINS_HALVING[0])});
240             notaryChainDef.eraEnd = std::vector<int32_t>({0});
241         }
242         else
243         {
244             // first setup Verus parameters
245             notaryChainDef.name = "VRSC";
246             notaryChainDef.rewards = std::vector<int64_t>({0,38400000000,2400000000});
247             notaryChainDef.rewardsDecay = std::vector<int64_t>({100000000,0,0});
248             notaryChainDef.halving = std::vector<int32_t>({1,43200,1051920});
249             notaryChainDef.eraEnd = std::vector<int32_t>({10080,226080,0});
250         }
251         notaryChainDef.options = (notaryChainDef.OPTION_CANBERESERVE | notaryChainDef.OPTION_ID_REFERRALS);
252         notaryChainDef.idRegistrationAmount = CCurrencyDefinition::DEFAULT_ID_REGISTRATION_AMOUNT;
253         notaryChainDef.idReferralLevels = CCurrencyDefinition::DEFAULT_ID_REFERRAL_LEVELS;
254         notaryChainDef.systemID = notaryChainDef.GetID();
255
256         ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
257         ASSETCHAINS_TIMEUNLOCKFROM = 0;
258         ASSETCHAINS_TIMEUNLOCKTO = 0;
259     }
260     else
261     {
262         ConnectedChains.ThisChain().options = (CCurrencyDefinition::OPTION_CANBERESERVE | CCurrencyDefinition::OPTION_ID_REFERRALS);
263         ConnectedChains.ThisChain().idRegistrationAmount = CCurrencyDefinition::DEFAULT_ID_REGISTRATION_AMOUNT;
264         ConnectedChains.ThisChain().idReferralLevels = CCurrencyDefinition::DEFAULT_ID_REFERRAL_LEVELS;
265         ConnectedChains.ThisChain().systemID = ConnectedChains.ThisChain().GetID();   
266     }
267
268     memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
269     assert(ConnectedChains.ThisChain().name.size() < sizeof(ASSETCHAINS_SYMBOL));
270     strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
271
272     auto numEras = ConnectedChains.ThisChain().rewards.size();
273     ASSETCHAINS_LASTERA = numEras - 1;
274     mapArgs["-ac_eras"] = to_string(numEras);
275
276     mapArgs["-ac_end"] = "";
277     mapArgs["-ac_reward"] = "";
278     mapArgs["-ac_halving"] = "";
279     mapArgs["-ac_decay"] = "";
280     mapArgs["-ac_options"] = "";
281
282     for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
283     {
284         if (j > ASSETCHAINS_LASTERA)
285         {
286             ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
287             ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
288             ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
289             ASSETCHAINS_ENDSUBSIDY[j] = 0;
290             ASSETCHAINS_ERAOPTIONS[j] = 0;
291         }
292         else
293         {
294             ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
295             ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
296             ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
297             ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
298             ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().options;
299             if (j == 0)
300             {
301                 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
302                 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
303                 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
304                 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
305                 mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
306             }
307             else
308             {
309                 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
310                 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
311                 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
312                 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
313                 mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
314             }
315         }
316     }
317
318     PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
319     mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
320     PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
321     mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
322
323     LOCK(cs_main);
324     CCurrencyState currencyState = ConnectedChains.GetCurrencyState(0);
325
326     ASSETCHAINS_SUPPLY = currencyState.supply;
327     mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
328     return true;
329 }
330
331 void GetCurrencyDefinitions(vector<CCurrencyDefinition> &chains, bool includeExpired)
332 {
333     CCcontract_info CC;
334     CCcontract_info *cp;
335
336     std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
337
338     if (GetAddressIndex(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetID(), EVAL_CURRENCY_DEFINITION)), 1, addressIndex))
339     {
340         for (auto txidx : addressIndex)
341         {
342             CTransaction tx;
343             uint256 blkHash;
344             if (GetTransaction(txidx.first.txhash, tx, blkHash))
345             {
346                 std::vector<CCurrencyDefinition> newChains = CCurrencyDefinition::GetCurrencyDefinitions(tx);
347                 chains.insert(chains.begin(), newChains.begin(), newChains.end());
348
349                 int downTo = chains.size() - newChains.size();
350                 for (int i = chains.size() - 1; i >= downTo; i--)
351                 {
352                     UniValue valStr(UniValue::VSTR);
353
354                     // TODO: remove/comment this if statement, as it is redundant with the one below
355                     if (!valStr.read(chains[i].ToUniValue().write()))
356                     {
357                         printf("Invalid characters in blockchain definition: %s\n", chains[i].ToUniValue().write().c_str());
358                     }
359
360                     // remove after to use less storage
361                     if (!valStr.read(chains[i].ToUniValue().write()) || ClosedPBaaSChains.count(chains[i].GetID()) || (!includeExpired && chains[i].endBlock != 0 && chains[i].endBlock < chainActive.Height()))
362                     {
363                         chains.erase(chains.begin() + i);
364                     }
365                 }
366             }
367         }
368     }
369 }
370
371 bool CConnectedChains::LoadReserveCurrencies()
372 {
373     // we need to get the currency definition for all convertible currencies in addition to Verus
374     // and store them in ConnectedChains. then, add currency definition outputs for each
375     // currency to the coinbase
376     for (auto &curID : thisChain.currencies)
377     {
378         // get the total amount pre-converted
379         CCurrencyDefinition oneDef;
380         UniValue params(UniValue::VARR);
381         params.push_back(EncodeDestination(CIdentityID(curID)));
382
383         UniValue result;
384         try
385         {
386             result = find_value(RPCCallRoot("getcurrency", params), "result");
387         } catch (exception e)
388         {
389             result = NullUniValue;
390         }
391
392         if (!result.isNull())
393         {
394             oneDef = CCurrencyDefinition(result);
395         }
396
397         if (result.isNull() || !oneDef.IsValid())
398         {
399             // no matter what happens, we should be able to get a valid currency state of some sort, if not, fail
400             LogPrintf("Unable to get currency definition for %s\n", EncodeDestination(CIdentityID(curID)).c_str());
401             printf("Unable to get currency definition for %s\n", EncodeDestination(CIdentityID(curID)).c_str());
402             return false;
403         }
404
405         {
406             LOCK(cs_mergemining);
407             reserveCurrencies[curID] = oneDef;
408         }
409     }
410     return true;
411 }
412
413 bool CConnectedChains::GetLastImport(const uint160 &systemID, 
414                                      CTransaction &lastImport, 
415                                      CPartialTransactionProof &crossChainExport, 
416                                      CCrossChainImport &ccImport, 
417                                      CCrossChainExport &ccCrossExport)
418 {
419     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
420
421     LOCK2(cs_main, mempool.cs);
422
423     // get last import from the specified chain
424     if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(systemID, EVAL_CROSSCHAIN_IMPORT), 1, unspentOutputs))
425     {
426         return false;
427     }
428
429     // make sure it isn't just a burned transaction to that address, drop out on first match
430     const std::pair<CAddressUnspentKey, CAddressUnspentValue> *pOutput = NULL;
431     COptCCParams p;
432     for (const auto &output : unspentOutputs)
433     {
434         if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && 
435             p.evalCode == EVAL_CROSSCHAIN_IMPORT &&
436             p.vData.size())
437         {
438             pOutput = &output;
439             break;
440         }
441     }
442     if (!pOutput)
443     {
444         return false;
445     }
446     uint256 hashBlk;
447     CCurrencyDefinition newCur;
448
449     if (!myGetTransaction(pOutput->first.txhash, lastImport, hashBlk) || 
450         !(lastImport.vout.size() &&
451          (lastImport.vout.back().scriptPubKey.IsOpReturn() || CCurrencyDefinition::GetCurrencyDefinitions(lastImport).size())))
452     {
453         return false;
454     }
455     else if (!lastImport.vout.back().scriptPubKey.IsOpReturn())
456     {
457         ccCrossExport = CCrossChainExport();
458         ccImport = CCrossChainImport(p.vData[0]);
459         crossChainExport = CPartialTransactionProof();
460     }
461     else
462     {
463         ccImport = CCrossChainImport(p.vData[0]);
464         CPartialTransactionProof *pTxProof;
465         auto opRetArr = RetrieveOpRetArray(lastImport.vout.back().scriptPubKey);
466         if (!opRetArr.size() || 
467             opRetArr[0]->objectType != CHAINOBJ_TRANSACTION_PROOF || 
468             (pTxProof = &(((CChainObject<CPartialTransactionProof> *)(opRetArr[0]))->object))->txProof.proofSequence.size() < 3)
469         {
470             DeleteOpRetObjects(opRetArr);
471             return false;
472         }
473         else
474         {
475             crossChainExport = ((CChainObject<CPartialTransactionProof> *)opRetArr[0])->object;
476             DeleteOpRetObjects(opRetArr);
477             CTransaction tx;
478             if (crossChainExport.GetPartialTransaction(tx).IsNull())
479             {
480                 return false;
481             }
482             else
483             {
484                 ccCrossExport = CCrossChainExport(tx);
485             }
486         }
487     }
488     return true;
489 }
490
491 // returns newly created import transactions to the specified chain/system from exports on this chain specified chain
492 // nHeight is the height for which we have an MMR that the chainID chain considers confirmed for this chain. it will
493 // accept proofs of any transactions with that MMR and height.
494 // Parameters should be validated before this call.
495 bool CConnectedChains::CreateLatestImports(const CCurrencyDefinition &currencyDef, 
496                                            const CTransaction &lastCrossChainImport, 
497                                            const CTransaction &importTxTemplate,
498                                            const CTransaction &lastConfirmedNotarization,
499                                            const CCurrencyValueMap &AvailableTokenInput,
500                                            CAmount TotalNativeInput,
501                                            std::vector<CTransaction> &newImports)
502 {
503     uint160 systemID = currencyDef.systemID;
504     uint160 thisChainID = thisChain.GetID();
505     uint160 currencyID = currencyDef.GetID();
506     bool isTokenImport = currencyDef.IsToken() && systemID == thisChainID;
507     bool isDefinition = false;
508
509     CCurrencyValueMap availableTokenInput(AvailableTokenInput);
510     CAmount totalNativeInput(TotalNativeInput);
511
512     // printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
513
514     CPBaaSNotarization lastConfirmed(lastConfirmedNotarization);
515     if ((isTokenImport && chainActive.LastTip() == NULL) ||
516         (!isTokenImport && (!lastConfirmed.IsValid() || (chainActive.LastTip() == NULL) || lastConfirmed.notarizationHeight > chainActive.LastTip()->GetHeight())))
517     {
518         LogPrintf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
519         printf("%s: Invalid lastConfirmedNotarization transaction\n", __func__);
520         return false;
521     }
522
523     CCrossChainImport lastCCI(lastCrossChainImport);
524     if (!lastCCI.IsValid())
525     {
526         LogPrintf("%s: Invalid lastCrossChainImport transaction\n", __func__);
527         printf("%s: Invalid lastCrossChainImport transaction\n", __func__);
528         return false;
529     }
530
531     std::vector<CBaseChainObject *> chainObjs;
532
533     // either a fully valid import with an export or the first import either in block 1 or the chain definition
534     // on the Verus chain
535     std::vector<CCurrencyDefinition> chainDefs;
536     if (!(lastCrossChainImport.vout.back().scriptPubKey.IsOpReturn() &&
537           (chainObjs = RetrieveOpRetArray(lastCrossChainImport.vout.back().scriptPubKey)).size() >= 1 &&
538           chainObjs[0]->objectType == CHAINOBJ_TRANSACTION_PROOF) &&
539         !(chainObjs.size() == 0 && 
540           (chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(lastCrossChainImport)).size()))
541     {
542         DeleteOpRetObjects(chainObjs);
543         LogPrintf("%s: Invalid last import tx\n", __func__);
544         printf("%s: Invalid last import tx\n", __func__);
545         return false;
546     }
547
548     bool isVerusActive = IsVerusActive();
549
550     LOCK2(cs_main, mempool.cs);
551
552     // confirmation of tokens are simultaneous to blocks
553     if (isTokenImport)
554     {
555         lastConfirmed.notarizationHeight = chainActive.Height();
556     }
557
558     uint256 lastExportHash;
559     CTransaction lastExportTx;
560     uint256 blkHash;
561     uint32_t blkHeight = 0;
562     uint32_t nHeight = chainActive.Height() + 1;
563
564     bool found = false;
565     if (chainObjs.size())
566     {
567         CPartialTransactionProof oneExportProof(((CChainObject<CPartialTransactionProof> *)chainObjs[0])->object);
568         if (oneExportProof.txProof.proofSequence.size() == 3)
569         {
570             lastExportHash = oneExportProof.TransactionHash();
571         }
572
573         BlockMap::iterator blkMapIt;
574         DeleteOpRetObjects(chainObjs);
575         if (!lastExportHash.IsNull() &&
576             myGetTransaction(lastExportHash, lastExportTx, blkHash) &&
577             (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() &&
578             blkMapIt->second &&
579             chainActive.Contains(blkMapIt->second))
580         {
581             found = true;
582             blkHeight = blkMapIt->second->GetHeight();
583         }
584     }
585     else
586     {
587         std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
588
589         // we cannot get export to a chain that has shut down
590         // if the chain definition is spent, a chain is inactive
591         // TODO:PBAAS this check needs to be lifted one level out to any loop that processes multiple 
592         // import/exports at a time to prevent complexity
593         if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_CURRENCY_DEFINITION)), 1, unspentOutputs) && unspentOutputs.size())
594         {
595             CCurrencyDefinition localChainDef;
596             CTransaction tx;
597
598             for (auto &txidx : unspentOutputs)
599             {
600                 COptCCParams p;
601                 uint160 localChainID;
602                 if (txidx.second.script.IsPayToCryptoCondition(p) && 
603                     p.IsValid() && 
604                     p.evalCode == EVAL_CURRENCY_DEFINITION && 
605                     p.vData[0].size() && 
606                     (localChainDef = CCurrencyDefinition(p.vData[0])).IsValid() &&
607                     (localChainDef.GetID()) == currencyID)
608                 {
609                     if (myGetTransaction(txidx.first.txhash, tx, blkHash))
610                     {
611                         CCrossChainExport ccx(tx);
612                         if (ccx.IsValid() && (isVerusActive || tx.IsCoinBase()))
613                         {
614                             found = true;
615                             isDefinition = true;
616                             lastExportHash = txidx.first.txhash;
617                             blkHeight = txidx.second.blockHeight;
618                             break;
619                         }
620                     }
621                 }
622             }
623         }
624     }
625
626     if (!found)
627     {
628         //LogPrintf("%s: No export thread found\n", __func__);
629         //printf("%s: No export thread found\n", __func__);
630         return false;
631     }
632
633     // which transaction are we in this block?
634     std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
635
636     // get all export transactions including and since this one up to the confirmed height
637     if (blkHeight <= lastConfirmed.notarizationHeight && 
638         GetAddressIndex(CCrossChainRPCData::GetConditionID(isTokenImport ? currencyID : systemID, EVAL_CROSSCHAIN_EXPORT), 1, addressIndex, blkHeight, lastConfirmed.notarizationHeight))
639     {
640         // find this export, then check the next one that spends it and use it if also valid
641         found = false;
642         uint256 lastHash = lastExportHash;
643
644         // indexed by input hash
645         std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
646
647         // validate, order, and relate them with their inputs
648         for (auto &utxo : addressIndex)
649         {
650             // if current tx spends lastHash, then we have our next valid transaction to create an import with
651             CTransaction tx, inputtx;
652             uint256 blkHash1, blkHash2;
653             BlockMap::iterator blkIt;
654             CCrossChainExport ccx;
655             COptCCParams p;
656             if (!utxo.first.spending &&
657                 (utxo.first.txhash != lastExportHash) &&
658                 myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
659                 (ccx = CCrossChainExport(tx)).IsValid() &&
660                 ccx.numInputs &&
661                 (!tx.IsCoinBase() && 
662                 tx.vin.size() && 
663                 myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
664                 inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && 
665                 p.IsValid() && 
666                 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
667             {
668                 validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
669             }
670             /*
671             else
672             {
673                 bool doNext = true;
674                 printf("!utxo.first.spending: %s\n", (doNext = !utxo.first.spending) ? "true" : "false");
675                 printf("!(utxo.first.txhash == lastExportHash): %s\n", (doNext = !(utxo.first.txhash == lastExportHash)) ? "true" : "false");
676                 printf("myGetTransaction(utxo.first.txhash, tx, blkHash1): %s\n", (doNext = myGetTransaction(utxo.first.txhash, tx, blkHash1)) ? "true" : "false");
677                 if (doNext)
678                     printf("(ccx = CCrossChainExport(tx)).IsValid(): %s\n", (doNext = (ccx = CCrossChainExport(tx)).IsValid()) ? "true" : "false");
679                 if (doNext)
680                     printf("ccx.numInputs: %d\n", (doNext = ccx.numInputs != 0) ? ccx.numInputs : 0);
681                 if (doNext)
682                     printf("!tx.IsCoinBase(): %s\n", (doNext = !tx.IsCoinBase()) ? "true" : "false");
683                 if (doNext)
684                     printf("tx.vin.size(): %lu\n", (doNext = tx.vin.size() != 0) ? tx.vin.size() : 0l);
685                 if (doNext)
686                     printf("myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)): %s\n", (doNext = myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) ? "true" : "false");
687                 if (doNext)
688                     printf("inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p): %s\n", (doNext = inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p)) ? "true" : "false");
689                 if (doNext)
690                     printf("p.IsValid(): %s\n", (doNext = p.IsValid()) ? "true" : "false");
691                 if (doNext)
692                     printf("p.evalCode == EVAL_CROSSCHAIN_EXPORT: %s\n", (doNext = p.evalCode == EVAL_CROSSCHAIN_EXPORT) ? "true" : "false");
693             }
694             */
695         }
696
697         CTransaction lastImport(lastCrossChainImport);
698
699         for (auto aixIt = validExports.find(lastExportHash); 
700              aixIt != validExports.end(); 
701              aixIt = validExports.find(lastExportHash))
702         {
703             // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
704             // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
705             // 2. Creates reserve outputs for unconverted imports
706             // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
707             // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
708             // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
709             // then signs the transaction considers it the latest import and the new export the latest export and
710             // loops until there are no more confirmed, consecutive, valid export transactions to export
711
712             // aixIt has an input from the export thread of last transaction, an optional deposit to the reserve, and an opret of all outputs + 1 fee
713             assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
714
715             CMutableTransaction newImportTx(importTxTemplate);
716
717             int ccxOutputNum = 0;
718             CCrossChainExport ccx(aixIt->second.second, &ccxOutputNum);
719
720             int32_t importOutNum = 0;
721             lastCCI = CCrossChainImport(lastImport, &importOutNum);
722             if (!lastCCI.IsValid() || !ccx.IsValid())
723             {
724                 LogPrintf("%s: POSSIBLE CORRUPTION bad import/export data in transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
725                 printf("%s: POSSIBLE CORRUPTION bad import/export data transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
726                 return false;
727             }
728
729             // if no prepared input, make one
730             if (!newImportTx.vin.size())
731             {
732                 //printf("adding input: %s, %d\n", lastImport.GetHash().GetHex().c_str(), importOutNum);
733                 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), importOutNum));
734             }
735
736             newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
737
738             CCcontract_info CC;
739             CCcontract_info *cp;
740             CPubKey pk;
741
742             CCurrencyValueMap availableReserveFees = ccx.totalFees;
743             CCurrencyValueMap exportFees = ccx.CalculateExportFee();
744             CCurrencyValueMap importFees = ccx.CalculateImportFee();
745             CAmount feesOut = 0;
746
747             CCoinbaseCurrencyState _currencyState, currencyState;
748             if (isTokenImport)
749             {
750                 // spend export finalization if there is one
751                 int32_t finalizeOutNum = -1;
752                 uint32_t eCode;
753                 CTransactionFinalization ccxFinalization = CTransactionFinalization(aixIt->second.second, &eCode, &finalizeOutNum);
754                 if (finalizeOutNum != -1)
755                 {
756                     newImportTx.vin.push_back(CTxIn(aixIt->second.second.GetHash(), finalizeOutNum));
757                 }
758
759                 if (!NewImportNotarization(currencyDef, nHeight, lastImport, aixIt->second.first.blockHeight, aixIt->second.second, newImportTx, _currencyState))
760                 {
761                     LogPrintf("%s: cannot create notarization for transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
762                     printf("%s:  cannot create notarization for transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
763                     return false;
764                 }
765             }
766             else
767             {
768                 // TODO: confirm that for non-tokens, we don't have to calculate initial state
769                 _currencyState = lastConfirmed.currencyState;
770             }
771
772             CReserveTransactionDescriptor rtxd;
773             std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
774
775             if (!_currencyState.IsValid() ||
776                 !rtxd.AddReserveTransferImportOutputs(thisChainID, currencyDef, _currencyState, exportOutputs, newImportTx.vout, &currencyState))
777             {
778                 LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
779                 printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
780                 // free the memory
781                 DeleteOpRetObjects(exportOutputs);
782                 return false;
783             }
784
785             // free the memory
786             DeleteOpRetObjects(exportOutputs);
787
788             // emit a crosschain import output as summary
789             cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
790             pk = CPubKey(ParseHex(CC.CChexstr));
791
792             CCurrencyValueMap leftoverCurrency = ((availableTokenInput - rtxd.ReserveInputMap()) +
793                                                   CCurrencyValueMap(std::vector<uint160>({systemID}), std::vector<CAmount>({totalNativeInput - rtxd.nativeIn})));
794
795             /*
796             printf("%s: leftoverCurrency:\n%s\nReserveInputMap():\n%s\nrtxd.nativeIn:\n%s\nconversionfees:\n%s\ntxreservefees:\n%s\ntxnativefees:\n%ld\nccx.totalfees:\n%s\nexportfees:\n%s\nccx.totalFees - exportFees:\n%s\n\n", 
797                         __func__, 
798                         leftoverCurrency.ToUniValue().write().c_str(),
799                         rtxd.ReserveInputMap().ToUniValue().write().c_str(),
800                         ValueFromAmount(rtxd.nativeIn).write().c_str(),
801                         (rtxd.ReserveConversionFeesMap() + CCurrencyValueMap(std::vector<uint160>({thisChainID}), std::vector<CAmount>({rtxd.nativeConversionFees}))).ToUniValue().write().c_str(),
802                         rtxd.ReserveFees().ToUniValue().write().c_str(),
803                         rtxd.NativeFees(),
804                         ccx.totalFees.ToUniValue().write().c_str(),
805                         exportFees.ToUniValue().write().c_str(),
806                         (ccx.totalFees - exportFees).ToUniValue().write().c_str());
807             */
808
809             if (leftoverCurrency.HasNegative())
810             {
811                 LogPrintf("%s: ERROR - fees do not match. leftoverCurrency:\n%s\nReserveInputMap():\n%s\nrtxd.nativeIn:\n%s\nconversionfees:\n%s\ntxreservefees:\n%s\ntxnativefees:\n%ld\nccx.totalfees:\n%s\nexportfees:\n%s\nccx.totalFees - exportFees:\n%s\n\n", 
812                         __func__, 
813                         leftoverCurrency.ToUniValue().write().c_str(),
814                         rtxd.ReserveInputMap().ToUniValue().write().c_str(),
815                         ValueFromAmount(rtxd.nativeIn).write().c_str(),
816                         (rtxd.ReserveConversionFeesMap() + CCurrencyValueMap(std::vector<uint160>({thisChainID}), std::vector<CAmount>({rtxd.nativeConversionFees}))).ToUniValue().write().c_str(),
817                         rtxd.ReserveFees().ToUniValue().write().c_str(),
818                         rtxd.NativeFees(),
819                         ccx.totalFees.ToUniValue().write().c_str(),
820                         exportFees.ToUniValue().write().c_str(),
821                         (ccx.totalFees - exportFees).ToUniValue().write().c_str());
822                 return false;
823             }
824
825             std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(ccx.systemID, EVAL_CROSSCHAIN_IMPORT)))});
826             std::vector<CTxDestination> dests;
827
828             if (currencyDef.proofProtocol == CCurrencyDefinition::PROOF_PBAASMMR ||
829                 currencyDef.proofProtocol == CCurrencyDefinition::PROOF_CHAINID)
830             {
831                 dests = std::vector<CTxDestination>({pk});
832             }
833             else
834             {
835                 LogPrintf("%s: ERROR - invalid proof protocol\n", __func__);
836                 printf("%s: ERROR - invalid proof protocol\n", __func__);
837                 return false;
838             }
839
840             CAmount nativeOutConverted = 0;
841             for (auto &oneNativeConversion : rtxd.NativeOutConvertedMap().valueMap)
842             {
843                 nativeOutConverted += oneNativeConversion.second;
844             }
845
846             /*
847             printf("DEBUGOUT: nativeOutConverted: %ld, rtxd.ReserveOutConvertedMap():%s\n", nativeOutConverted, rtxd.ReserveOutConvertedMap().ToUniValue().write().c_str());
848             printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
849             printf("rtxd.ReserveInputMap(): %s, ccx.totalAmounts: %s\n", rtxd.ReserveInputMap().ToUniValue().write().c_str(), ccx.totalAmounts.ToUniValue().write().c_str());
850             printf("leftoverCurrency: %s\n", leftoverCurrency.ToUniValue().write().c_str());
851             */
852
853             // breakout native from leftover currency
854             totalNativeInput = leftoverCurrency.valueMap[systemID];
855             leftoverCurrency.valueMap.erase(systemID);
856
857             /*
858             printf("DEBUGOUT: totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
859             printf("DEBUGOUT: rtxd.nativeIn: %ld, rtxd.ReserveInputMap():%s\n", rtxd.nativeIn, rtxd.ReserveInputMap().ToUniValue().write().c_str());
860             */
861
862             CCrossChainImport cci = CCrossChainImport(ccx.systemID, 
863                                                       rtxd.ReserveOutConvertedMap() + 
864                                                       CCurrencyValueMap(std::vector<uint160>({systemID}), std::vector<CAmount>({nativeOutConverted})),
865                                                       leftoverCurrency.CanonicalMap());
866
867             newImportTx.vout[0] = CTxOut(totalNativeInput, MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests));
868
869             //printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
870             if (totalNativeInput < 0 || (availableTokenInput - rtxd.ReserveInputMap()).HasNegative())
871             {
872                 LogPrintf("%s: ERROR - importing more currency than available for %s\n", __func__, currencyDef.name.c_str());
873                 LogPrintf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
874                 printf("%s: ERROR - importing more currency than available for %s\n", __func__, currencyDef.name.c_str());
875                 printf("totalNativeInput: %ld, availableTokenInput:%s\n", totalNativeInput, availableTokenInput.ToUniValue().write().c_str());
876                 return false;
877             }
878
879             availableTokenInput = leftoverCurrency;
880
881             // add a proof of the export transaction at the notarization height
882             CBlock block;
883             if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
884             {
885                 LogPrintf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
886                 printf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
887                 return false;
888             }
889
890             // prove ccx input 0, export output, and opret
891             std::vector<std::pair<int16_t, int16_t>> parts({{CTransactionHeader::TX_HEADER, (int16_t)0},
892                                                             {CTransactionHeader::TX_PREVOUTSEQ, (int16_t)0},
893                                                             {CTransactionHeader::TX_OUTPUT, ccxOutputNum},
894                                                             {CTransactionHeader::TX_OUTPUT, (int16_t)(aixIt->second.second.vout.size() - 1)}});
895
896             // prove our transaction up to the MMR root of the last transaction
897             CPartialTransactionProof exportProof = block.GetPartialTransactionProof(aixIt->second.second, aixIt->second.first.txindex, parts);
898             if (!exportProof.components.size())
899             {
900                 LogPrintf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
901                 printf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
902                 return false;
903             }
904             exportProof.txProof << block.MMRProofBridge();
905
906             // TODO: don't include chain MMR proof for exports from the same chain
907             ChainMerkleMountainView mmv(chainActive.GetMMR(), lastConfirmed.notarizationHeight);
908             mmv.GetProof(exportProof.txProof, aixIt->second.first.blockHeight);
909
910             CChainObject<CPartialTransactionProof> exportXProof(CHAINOBJ_TRANSACTION_PROOF, exportProof);
911
912             CTransaction checkTx;
913
914             uint256 checkTxID = exportProof.CheckPartialTransaction(checkTx);
915             CMMRProof blockProof;
916             chainActive.GetBlockProof(mmv, blockProof, aixIt->second.first.blockHeight);
917             if (checkTxID != mmv.GetRoot())
918             {
919                 /*
920                 printf("CTransactionHeaderCheck: %s\n\n", exportProof.components[0].CheckProof().GetHex().c_str());
921                 printf("CTransactionProof\n%s\n", exportProof.txProof.ToUniValue().write(1,2).c_str());
922                 printf("CTransactionProofCheck: %s\n\n", checkTxID.GetHex().c_str());
923                 printf("CBlockProof\n%s\n", blockProof.ToUniValue().write(1,2).c_str());
924                 printf("CBlockProofCheck: %s\n\n", blockProof.CheckProof(chainActive[aixIt->second.first.blockHeight]->GetBlockHash()).GetHex().c_str());
925                 printf("CBlockHash: %s\n\n", block.GetHash().GetHex().c_str());
926                 printf("CBlockMMRRoot: %s\n\n", block.GetBlockMMRRoot().GetHex().c_str());
927                 printf("CBlockMMViewRoot: %s\n\n", BlockMMView(block.GetBlockMMRTree()).GetRoot().GetHex().c_str());
928                 for (auto &oneNode : mmv.GetPeaks())
929                 {
930                     printf("oneMerklePeakNode: %s:%s\n", oneNode.hash.GetHex().c_str(), oneNode.power.GetHex().c_str());
931                 }
932                 printf("MMVRoot: %s\n\n", mmv.GetRoot().GetHex().c_str());
933                 for (auto &oneTx : block.vtx)
934                 {
935                     printf("oneTxRoot: %s\n", TransactionMMView(oneTx.GetTransactionMMR()).GetRoot().GetHex().c_str());
936                 }
937                 */
938
939                 printf("%s: invalid partial transaction proof, hash is\n%s, should be\n%s\n", __func__, checkTxID.GetHex().c_str(), mmv.GetRoot().GetHex().c_str());
940                 LogPrintf("%s: invalid partial transaction proof, hash is\n%s, should be\n%s\n", __func__, checkTxID.GetHex().c_str(), mmv.GetRoot().GetHex().c_str());
941                 return false;
942             }
943             uint256 lastProof = exportProof.components[0].CheckProof();
944             for (int i = 1; i < exportProof.components.size(); i++)
945             {
946                 if (lastProof != exportProof.components[i].CheckProof())
947                 {
948                     printf("%s: error in new proof for component %d\n", __func__, i);
949                 }
950             }
951
952             // add the opret with the transaction and proof
953             newImportTx.vout.push_back(CTxOut(0, StoreOpRetArray(std::vector<CBaseChainObject *>({&exportXProof}))));
954
955             // we now have an Import transaction for the chainID chain, it is the latest, and the export we used is now the latest as well
956             // work our way forward
957             lastImport = newImportTx;
958             newImports.push_back(lastImport);
959             lastExportHash = aixIt->second.first.txhash;
960             //printf("loop again with lastImport.GetHash(): %s, lastExportHash %s\n", lastImport.GetHash().GetHex().c_str(), lastExportHash.GetHex().c_str());
961         }
962     }
963     return true;
964 }
965
966 void CheckPBaaSAPIsValid()
967 {
968     //printf("Solution version running: %d\n\n", CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()));
969     if (!chainActive.LastTip() ||
970         CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_PBAAS)
971     {
972         throw JSONRPCError(RPC_INVALID_REQUEST, "PBaaS not activated on blockchain.");
973     }
974 }
975
976 void CheckIdentityAPIsValid()
977 {
978     if (!chainActive.LastTip() ||
979         CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
980     {
981         throw JSONRPCError(RPC_INVALID_REQUEST, "Identity APIs not activated on blockchain.");
982     }
983 }
984
985 uint160 ValidateCurrencyName(std::string currencyStr, CCurrencyDefinition *pCurrencyDef=NULL)
986 {
987     std::string extraName;
988     uint160 retVal;
989     currencyStr = TrimSpaces(currencyStr);
990     if (!currencyStr.size())
991     {
992         return retVal;
993     }
994     ParseSubNames(currencyStr, extraName, true);
995     if (currencyStr.back() == '@' || (extraName != "" && boost::to_lower_copy(extraName) != boost::to_lower_copy(VERUS_CHAINNAME)))
996     {
997         return retVal;
998     }
999     if (CCurrencyDefinition::GetID(currencyStr) == ConnectedChains.ThisChain().GetID())
1000     {
1001         if (pCurrencyDef)
1002         {
1003             *pCurrencyDef = ConnectedChains.ThisChain();
1004         }
1005         return ConnectedChains.ThisChain().GetID();
1006     }
1007     CTxDestination currencyDest = DecodeDestination(currencyStr);
1008     if (currencyDest.which() == COptCCParams::ADDRTYPE_INVALID)
1009     {
1010         currencyDest = DecodeDestination(currencyStr + "@");
1011     }
1012     if (currencyDest.which() != COptCCParams::ADDRTYPE_INVALID)
1013     {
1014         // make sure there is such a currency defined on this chain
1015         CCurrencyDefinition currencyDef;
1016         if (GetCurrencyDefinition(GetDestinationID(currencyDest), currencyDef))
1017         {
1018             retVal = currencyDef.GetID();
1019         }
1020         if (pCurrencyDef)
1021         {
1022             *pCurrencyDef = currencyDef;
1023         }
1024     }
1025     return retVal;
1026 }
1027
1028 uint160 GetChainIDFromParam(const UniValue &param, CCurrencyDefinition *pCurrencyDef=NULL)
1029 {
1030     return ValidateCurrencyName(uni_get_str(param), pCurrencyDef);
1031 }
1032
1033 UniValue getcurrency(const UniValue& params, bool fHelp)
1034 {
1035     if (fHelp || params.size() != 1)
1036     {
1037         throw runtime_error(
1038             "getcurrency \"chainname\"\n"
1039             "\nReturns a complete definition for any given chain if it is registered on the blockchain. If the chain requested\n"
1040             "\nis NULL, chain definition of the current chain is returned.\n"
1041
1042             "\nArguments\n"
1043             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1044
1045             "\nResult:\n"
1046             "  {\n"
1047             "    \"version\" : n,                 (int) version of this chain definition\n"
1048             "    \"name\" : \"string\",           (string) name or symbol of the chain, same as passed\n"
1049             "    \"address\" : \"string\",        (string) cryptocurrency address to send fee and non-converted premine\n"
1050             "    \"currencyid\" : \"i-address\",  (string) string that represents the currency ID, same as the ID behind the currency\n"
1051             "    \"premine\" : n,                 (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
1052             "    \"convertible\" : \"xxxx\"       (bool) if this currency is a fractional reserve currency of Verus\n"
1053             "    \"startblock\" : n,              (int) block # on this chain, which must be notarized into block one of the chain\n"
1054             "    \"endblock\" : n,                (int) block # after which, this chain's useful life is considered to be over\n"
1055             "    \"eras\" : \"[obj, ...]\",       (objarray) different chain phases of rewards and convertibility\n"
1056             "    {\n"
1057             "      \"reward\" : \"[n, ...]\",     (int) reward start for each era in native coin\n"
1058             "      \"decay\" : \"[n, ...]\",      (int) exponential or linear decay of rewards during each era\n"
1059             "      \"halving\" : \"[n, ...]\",    (int) blocks between halvings during each era\n"
1060             "      \"eraend\" : \"[n, ...]\",     (int) block marking the end of each era\n"
1061             "      \"eraoptions\" : \"[n, ...]\", (int) options (reserved)\n"
1062             "    }\n"
1063             "    \"nodes\"      : \"[obj, ..]\",  (objectarray, optional) up to 8 nodes that can be used to connect to the blockchain"
1064             "      [{\n"
1065             "         \"nodeidentity\" : \"txid\", (string,  optional) internet, TOR, or other supported address for node\n"
1066             "         \"paymentaddress\" : n,     (int,     optional) rewards payment address\n"
1067             "       }, .. ]\n"
1068             "    \"bestnotarization\" : {\n"
1069             "     }\n"
1070             "    \"besttxid\" : \"txid\"\n"
1071             "     }\n"
1072             "    \"confirmednotarization\" : {\n"
1073             "     }\n"
1074             "    \"confirmedtxid\" : \"txid\"\n"
1075             "  }\n"
1076
1077             "\nExamples:\n"
1078             + HelpExampleCli("getcurrency", "\"chainname\"")
1079             + HelpExampleRpc("getcurrency", "\"chainname\"")
1080         );
1081     }
1082
1083     CheckPBaaSAPIsValid();
1084     LOCK2(cs_main, mempool.cs);
1085
1086     UniValue ret(UniValue::VOBJ);
1087
1088     CCurrencyDefinition chainDef;
1089     uint160 chainID = GetChainIDFromParam(params[0], &chainDef);
1090
1091     if (chainID.IsNull())
1092     {
1093         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1094     }
1095
1096     if (chainDef.IsValid())
1097     {
1098         ret = chainDef.ToUniValue();
1099
1100         if (chainDef.IsToken() && chainDef.systemID == ASSETCHAINS_CHAINID)
1101         {
1102             ret.push_back(Pair("bestcurrencystate", ConnectedChains.GetCurrencyState(chainID, chainActive.Height() + 1).ToUniValue()));
1103         }
1104         else
1105         {
1106             CChainNotarizationData cnd;
1107             GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
1108
1109             std::vector<CNodeData> vNNodes;
1110             int32_t confirmedHeight = -1, bestHeight = -1;
1111
1112             if (cnd.forks.size())
1113             {
1114                 // get all nodes from notarizations of the best chain into a vector
1115                 for (auto &oneNot : cnd.forks[cnd.bestChain])
1116                 {
1117                     vNNodes.insert(vNNodes.end(), cnd.vtx[oneNot].second.nodes.begin(), cnd.vtx[oneNot].second.nodes.end());
1118                 }
1119
1120                 confirmedHeight = cnd.vtx.size() && cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed].second.notarizationHeight : -1;
1121                 bestHeight = cnd.vtx.size() && cnd.bestChain != -1 ? cnd.vtx[cnd.forks[cnd.bestChain].back()].second.notarizationHeight : -1;
1122
1123                 // shuffle and take 8 nodes,
1124                 // up to two of which will be from the last confirmed notarization if present
1125                 std::random_shuffle(vNNodes.begin(), vNNodes.end());
1126                 int numConfirmedNodes = cnd.lastConfirmed == -1 ? 0 : cnd.vtx[cnd.lastConfirmed].second.nodes.size();
1127                 if (vNNodes.size() > (8 - numConfirmedNodes))
1128                 {
1129                     vNNodes.resize(8);
1130                 }
1131                 if (numConfirmedNodes)
1132                 {
1133                     vNNodes.insert(vNNodes.begin(), cnd.vtx[cnd.lastConfirmed].second.nodes.begin(), cnd.vtx[cnd.lastConfirmed].second.nodes.end());
1134                 }
1135                 if (vNNodes.size())
1136                 {
1137                     UniValue nodeArr(UniValue::VARR);
1138                     for (auto &oneNode : vNNodes)
1139                     {
1140                         nodeArr.push_back(oneNode.ToUniValue());
1141                     }
1142                     ret.push_back(Pair("nodes", nodeArr));
1143                 }
1144             }
1145
1146             if (!chainDef.IsToken())
1147             {
1148                 ret.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
1149                 if (confirmedHeight != -1)
1150                 {
1151                     ret.push_back(Pair("lastconfirmedtxid", cnd.vtx[cnd.lastConfirmed].first.GetHex().c_str()));
1152                     ret.push_back(Pair("lastconfirmedcurrencystate", cnd.vtx[cnd.lastConfirmed].second.currencyState.ToUniValue()));
1153                 }
1154             }
1155             ret.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
1156             if (bestHeight != -1)
1157             {
1158                 ret.push_back(Pair("besttxid", cnd.vtx[cnd.forks[cnd.bestChain].back()].first.GetHex().c_str()));
1159                 ret.push_back(Pair("bestcurrencystate", cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.ToUniValue()));
1160             }
1161         }
1162         return ret;
1163     }
1164     else
1165     {
1166         return NullUniValue;
1167     }
1168 }
1169
1170 UniValue getpendingtransfers(const UniValue& params, bool fHelp)
1171 {
1172     if (fHelp || params.size() > 1)
1173     {
1174         throw runtime_error(
1175             "getpendingtransfers \"chainname\"\n"
1176             "\nReturns all pending transfers for a particular chain that have not yet been aggregated into an export\n"
1177
1178             "\nArguments\n"
1179             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1180
1181             "\nResult:\n"
1182             "  {\n"
1183             "  }\n"
1184
1185             "\nExamples:\n"
1186             + HelpExampleCli("getpendingtransfers", "\"chainname\"")
1187             + HelpExampleRpc("getpendingtransfers", "\"chainname\"")
1188         );
1189     }
1190
1191     CheckPBaaSAPIsValid();
1192
1193     uint160 chainID = GetChainIDFromParam(params[0]);
1194
1195     if (chainID.IsNull())
1196     {
1197         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1198     }
1199
1200     CCurrencyDefinition chainDef;
1201     int32_t defHeight;
1202
1203     LOCK(cs_main);
1204
1205     if ((IsVerusActive() && GetCurrencyDefinition(chainID, chainDef, &defHeight)) || (chainDef = ConnectedChains.NotaryChain().chainDefinition).GetID() == chainID)
1206     {
1207         // look for new exports
1208         multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> inputDescriptors;
1209
1210         if (GetUnspentChainTransfers(inputDescriptors, chainID))
1211         {
1212             UniValue ret(UniValue::VARR);
1213
1214             for (auto &desc : inputDescriptors)
1215             {
1216                 UniValue oneExport(UniValue::VOBJ);
1217
1218                 oneExport.push_back(Pair("currencyid", EncodeDestination(CIdentityID(desc.first))));
1219                 oneExport.push_back(Pair("txid", desc.second.first.txIn.prevout.hash.GetHex()));
1220                 oneExport.push_back(Pair("n", (int32_t)desc.second.first.txIn.prevout.n));
1221                 oneExport.push_back(Pair("valueout", desc.second.first.nValue));
1222                 oneExport.push_back(Pair("reservetransfer", desc.second.second.ToUniValue()));
1223                 ret.push_back(oneExport);
1224             }
1225             if (ret.size())
1226             {
1227                 return ret;
1228             }
1229         }
1230     }
1231     else
1232     {
1233         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
1234     }
1235     
1236     return NullUniValue;
1237 }
1238
1239 UniValue getexports(const UniValue& params, bool fHelp)
1240 {
1241     if (fHelp || params.size() != 1)
1242     {
1243         throw runtime_error(
1244             "getexports \"chainname\"\n"
1245             "\nReturns all pending export transfers that are not yet provable with confirmed notarizations.\n"
1246
1247             "\nArguments\n"
1248             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1249
1250             "\nResult:\n"
1251             "  {\n"
1252             "  }\n"
1253
1254             "\nExamples:\n"
1255             + HelpExampleCli("getexports", "\"chainname\"")
1256             + HelpExampleRpc("getexports", "\"chainname\"")
1257         );
1258     }
1259
1260     CheckPBaaSAPIsValid();
1261
1262     LOCK2(cs_main, mempool.cs);
1263
1264     uint160 chainID = GetChainIDFromParam(params[0]);
1265
1266     if (chainID.IsNull())
1267     {
1268         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1269     }
1270
1271     CCurrencyDefinition chainDef;
1272     int32_t defHeight;
1273
1274     if (GetCurrencyDefinition(chainID, chainDef, &defHeight))
1275     {
1276         // which transaction are we in this block?
1277         std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1278
1279         CChainNotarizationData cnd;
1280         if (GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd))
1281         {
1282             // get all export transactions including and since this one up to the confirmed height
1283             if (GetAddressIndex(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT), 
1284                                 1, addressIndex, cnd.IsConfirmed() ? cnd.vtx[cnd.lastConfirmed].second.crossHeight : defHeight))
1285             {
1286                 UniValue ret(UniValue::VARR);
1287
1288                 for (auto &idx : addressIndex)
1289                 {
1290                     uint256 blkHash;
1291                     CTransaction exportTx;
1292                     if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
1293                     {
1294                         std::vector<CBaseChainObject *> opretTransfers;
1295                         CCrossChainExport ccx;
1296                         if ((ccx = CCrossChainExport(exportTx)).IsValid() && exportTx.vout.back().scriptPubKey.IsOpReturn())
1297                         {
1298                             UniValue oneExport(UniValue::VOBJ);
1299                             UniValue transferArray(UniValue::VARR);
1300                             opretTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
1301                             oneExport.push_back(Pair("blockheight", idx.first.blockHeight));
1302                             oneExport.push_back(Pair("exportid", idx.first.txhash.GetHex()));
1303                             oneExport.push_back(Pair("description", ccx.ToUniValue()));
1304                             for (auto oneTransfer : opretTransfers)
1305                             {
1306                                 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
1307                                 {
1308                                     transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
1309                                 }
1310                             }
1311                             DeleteOpRetObjects(opretTransfers);
1312                             oneExport.push_back(Pair("transfers", transferArray));
1313                             ret.push_back(oneExport);
1314                         }
1315                     }
1316                 }
1317                 if (ret.size())
1318                 {
1319                     return ret;
1320                 }
1321             }
1322         }
1323     }
1324     else
1325     {
1326         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
1327     }
1328     
1329     return NullUniValue;
1330 }
1331
1332 UniValue getimports(const UniValue& params, bool fHelp)
1333 {
1334     if (fHelp || params.size() != 1)
1335     {
1336         throw runtime_error(
1337             "getimports \"chainname\"\n"
1338             "\nReturns all imports from a specific chain.\n"
1339
1340             "\nArguments\n"
1341             "1. \"chainname\"                     (string, optional) name of the chain to look for. no parameter returns current chain in daemon.\n"
1342
1343             "\nResult:\n"
1344             "  {\n"
1345             "  }\n"
1346
1347             "\nExamples:\n"
1348             + HelpExampleCli("getimports", "\"chainname\"")
1349             + HelpExampleRpc("getimports", "\"chainname\"")
1350         );
1351     }
1352
1353     CheckPBaaSAPIsValid();
1354
1355     uint160 chainID = GetChainIDFromParam(params[0]);
1356
1357     if (chainID.IsNull())
1358     {
1359         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
1360     }
1361
1362     CCurrencyDefinition chainDef;
1363     int32_t defHeight;
1364
1365     LOCK(cs_main);
1366
1367     if (GetCurrencyDefinition(chainID, chainDef, &defHeight))
1368     {
1369         // which transaction are we in this block?
1370         std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1371
1372         CBlockIndex *pIndex;
1373
1374         CChainNotarizationData cnd;
1375         // get all import transactions including and since this one up to the confirmed height
1376         if (GetAddressIndex(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT), 1, addressIndex))
1377         {
1378             UniValue ret(UniValue::VARR);
1379
1380             for (auto &idx : addressIndex)
1381             {
1382                 uint256 blkHash;
1383                 CTransaction importTx;
1384                 if (!idx.first.spending && myGetTransaction(idx.first.txhash, importTx, blkHash))
1385                 {
1386                     CCrossChainImport cci;
1387                     if ((cci = CCrossChainImport(importTx)).IsValid() && importTx.vout.back().scriptPubKey.IsOpReturn())
1388                     {
1389                         std::vector<CBaseChainObject *> opretImports;
1390                         CTransaction exportTx;
1391                         CCrossChainExport ccx;
1392
1393                         opretImports = RetrieveOpRetArray(importTx.vout.back().scriptPubKey);
1394
1395                         if (opretImports.size() >= 1 && 
1396                             opretImports[0]->objectType == CHAINOBJ_TRANSACTION_PROOF && 
1397                             !((CChainObject<CPartialTransactionProof> *)opretImports[0])->object.GetPartialTransaction(exportTx).IsNull() &&
1398                             (ccx = CCrossChainExport(exportTx)).IsValid() && 
1399                             ccx.numInputs &&
1400                             exportTx.vout.size() && 
1401                             exportTx.vout.back().scriptPubKey.IsOpReturn())
1402                         {
1403                             std::vector<CBaseChainObject *> opretTransfers;
1404
1405                             UniValue oneImport(UniValue::VOBJ);
1406                             UniValue transferArray(UniValue::VARR);
1407                             opretTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
1408                             oneImport.push_back(Pair("blockheight", idx.first.blockHeight));
1409                             oneImport.push_back(Pair("importid", idx.first.txhash.GetHex()));
1410                             oneImport.push_back(Pair("description", cci.ToUniValue()));
1411                             for (auto oneTransfer : opretTransfers)
1412                             {
1413                                 if (oneTransfer->objectType == CHAINOBJ_RESERVETRANSFER)
1414                                 {
1415                                     transferArray.push_back(((CChainObject<CReserveTransfer> *)oneTransfer)->object.ToUniValue());
1416                                 }
1417                             }
1418                             DeleteOpRetObjects(opretTransfers);
1419                             oneImport.push_back(Pair("transfers", transferArray));
1420                             ret.push_back(oneImport);
1421                         }
1422                         DeleteOpRetObjects(opretImports);
1423                     }
1424                 }
1425             }
1426             if (ret.size())
1427             {
1428                 return ret;
1429             }
1430         }
1431     }
1432     else
1433     {
1434         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unrecognized chain name or chain ID");
1435     }
1436     return NullUniValue;
1437 }
1438
1439 UniValue listcurrencies(const UniValue& params, bool fHelp)
1440 {
1441     if (fHelp || params.size() > 1)
1442     {
1443         throw runtime_error(
1444             "listcurrencies (includeexpired)\n"
1445             "\nReturns a complete definition for any given chain if it is registered on the blockchain. If the chain requested\n"
1446             "\nis NULL, chain definition of the current chain is returned.\n"
1447
1448             "\nArguments\n"
1449             "1. \"includeexpired\"                (bool, optional) if true, include chains that are no longer active\n"
1450
1451             "\nResult:\n"
1452             "[\n"
1453             "  {\n"
1454             "    \"version\" : n,                 (int) version of this chain definition\n"
1455             "    \"name\" : \"string\",           (string) name or symbol of the chain, same as passed\n"
1456             "    \"address\" : \"string\",        (string) cryptocurrency address to send fee and non-converted premine\n"
1457             "    \"currencyid\" : \"hex-string\", (string) i-address that represents the chain ID, same as the ID that launched the chain\n"
1458             "    \"premine\" : n,                 (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
1459             "    \"convertible\" : \"xxxx\"       (bool) if this currency is a fractional reserve currency of Verus\n"
1460             "    \"startblock\" : n,              (int) block # on this chain, which must be notarized into block one of the chain\n"
1461             "    \"endblock\" : n,                (int) block # after which, this chain's useful life is considered to be over\n"
1462             "    \"eras\" : \"[obj, ...]\",       (objarray) different chain phases of rewards and convertibility\n"
1463             "    {\n"
1464             "      \"reward\" : \"[n, ...]\",     (int) reward start for each era in native coin\n"
1465             "      \"decay\" : \"[n, ...]\",      (int) exponential or linear decay of rewards during each era\n"
1466             "      \"halving\" : \"[n, ...]\",    (int) blocks between halvings during each era\n"
1467             "      \"eraend\" : \"[n, ...]\",     (int) block marking the end of each era\n"
1468             "      \"eraoptions\" : \"[n, ...]\", (int) options (reserved)\n"
1469             "    }\n"
1470             "    \"nodes\"      : \"[obj, ..]\",  (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
1471             "      [{\n"
1472             "         \"nodeaddress\" : \"txid\", (string,  optional) internet, TOR, or other supported address for node\n"
1473             "         \"paymentaddress\" : n,     (int,     optional) rewards payment address\n"
1474             "       }, .. ]\n"
1475             "  }, ...\n"
1476             "]\n"
1477
1478             "\nExamples:\n"
1479             + HelpExampleCli("listcurrencies", "true")
1480             + HelpExampleRpc("listcurrencies", "true")
1481         );
1482     }
1483
1484     CheckPBaaSAPIsValid();
1485
1486     UniValue ret(UniValue::VARR);
1487
1488     bool includeExpired = params[0].isBool() ? params[0].get_bool() : false;
1489
1490     vector<CCurrencyDefinition> chains;
1491     {
1492         LOCK2(cs_main, mempool.cs);
1493         GetCurrencyDefinitions(chains, includeExpired);
1494     }
1495
1496     for (auto def : chains)
1497     {
1498         LOCK2(cs_main, mempool.cs);
1499
1500         UniValue oneChain(UniValue::VOBJ);
1501         oneChain.push_back(Pair("currencydefinition", def.ToUniValue()));
1502
1503         CChainNotarizationData cnd;
1504         GetNotarizationData(def.GetID(), IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd);
1505
1506         int32_t confirmedHeight = -1, bestHeight = -1;
1507         confirmedHeight = cnd.vtx.size() && cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed].second.notarizationHeight : -1;
1508         bestHeight = cnd.vtx.size() && cnd.bestChain != -1 ? cnd.vtx[cnd.forks[cnd.bestChain].back()].second.notarizationHeight : -1;
1509
1510         if (!def.IsToken())
1511         {
1512             oneChain.push_back(Pair("lastconfirmedheight", confirmedHeight == -1 ? 0 : confirmedHeight));
1513             if (confirmedHeight != -1)
1514             {
1515                 oneChain.push_back(Pair("lastconfirmedtxid", cnd.vtx[cnd.lastConfirmed].first.GetHex().c_str()));
1516                 oneChain.push_back(Pair("lastconfirmedcurrencystate", cnd.vtx[cnd.lastConfirmed].second.currencyState.ToUniValue()));
1517             }
1518         }
1519         oneChain.push_back(Pair("bestheight", bestHeight == -1 ? 0 : bestHeight));
1520         if (bestHeight != -1)
1521         {
1522             oneChain.push_back(Pair("besttxid", cnd.vtx[cnd.forks[cnd.bestChain].back()].first.GetHex().c_str()));
1523             oneChain.push_back(Pair("bestcurrencystate", cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.ToUniValue()));
1524         }
1525         ret.push_back(oneChain);
1526     }
1527     return ret;
1528 }
1529
1530 // returns all chain transfer outputs, both spent and unspent between a specific start and end block with an optional chainFilter. if the chainFilter is not
1531 // NULL, only transfers to that system are returned
1532 bool GetChainTransfers(multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> &inputDescriptors, uint160 chainFilter, int start, int end, uint32_t flags)
1533 {
1534     if (!flags)
1535     {
1536         flags = CReserveTransfer::VALID;
1537     }
1538     bool nofilter = chainFilter.IsNull();
1539
1540     // which transaction are we in this block?
1541     std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
1542     std::set<uint256> countedTxes;                  // don't count twice
1543
1544     LOCK2(cs_main, mempool.cs);
1545
1546     if (!GetAddressIndex(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER), 1, addressIndex, start, end))
1547     {
1548         return false;
1549     }
1550     else
1551     {
1552         for (auto it = addressIndex.begin(); it != addressIndex.end(); it++)
1553         {
1554             CTransaction ntx;
1555             uint256 blkHash;
1556
1557             // each tx gets counted once
1558             if (countedTxes.count(it->first.txhash))
1559             {
1560                 continue;
1561             }
1562             countedTxes.insert(it->first.txhash);
1563
1564             if (myGetTransaction(it->first.txhash, ntx, blkHash))
1565             {
1566                 /*
1567                 uint256 hashBlk;
1568                 UniValue univTx(UniValue::VOBJ);
1569                 TxToUniv(ntx, hashBlk, univTx);
1570                 printf("tx: %s\n", univTx.write(1,2).c_str());
1571                 */
1572                 for (int i = 0; i < ntx.vout.size(); i++)
1573                 {
1574                     // if this is a transfer output, optionally to this chain, add it to the input vector
1575                     /*
1576                     std::vector<CTxDestination> dests;
1577                     int numRequired = 0;
1578                     txnouttype typeRet;
1579                     if (ExtractDestinations(ntx.vout[i].scriptPubKey, typeRet, dests, numRequired))
1580                     {
1581                         if (!nofilter)
1582                         {
1583                             printf("filter: %s\n", EncodeDestination(CKeyID(chainFilter)).c_str());
1584                         }
1585                         for (auto &oneDest : dests)
1586                         {
1587                             printf("%s\n", EncodeDestination(oneDest).c_str());
1588                         }
1589                     }
1590                     */
1591
1592                     COptCCParams p, m;
1593                     CReserveTransfer rt;
1594                     if (ntx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
1595                         p.evalCode == EVAL_RESERVE_TRANSFER &&
1596                         p.vData.size() > 1 && (rt = CReserveTransfer(p.vData[0])).IsValid() &&
1597                         (m = COptCCParams(p.vData[1])).IsValid() &&
1598                         (nofilter || (m.vKeys.size() > 1 && GetDestinationID(m.vKeys[1]) == chainFilter)) &&
1599                         (rt.flags & flags) == flags)
1600                     {
1601                         inputDescriptors.insert(make_pair(GetDestinationID(m.vKeys[1]),
1602                                                     make_pair(CInputDescriptor(ntx.vout[i].scriptPubKey, ntx.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))), rt)));
1603                     }
1604                     else if (p.IsValid() &&
1605                              p.evalCode == EVAL_RESERVE_TRANSFER &&
1606                              p.version != p.VERSION_V3)
1607                     {
1608                         // if we change the version, stop here in case it wasn't caught
1609                         assert(false);
1610                     }
1611                 }
1612             }
1613             else
1614             {
1615                 LogPrintf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1616                 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1617                 return false;
1618             }
1619         }
1620         return true;
1621     }
1622 }
1623
1624 // returns all unspent chain transfer outputs with an optional chainFilter. if the chainFilter is not
1625 // NULL, only transfers to that chain are returned
1626 bool GetUnspentChainTransfers(multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> &inputDescriptors, uint160 chainFilter)
1627 {
1628     bool nofilter = chainFilter.IsNull();
1629
1630     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1631
1632     LOCK2(cs_main, mempool.cs);
1633
1634     if (!GetAddressUnspent(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER), 1, unspentOutputs))
1635     {
1636         return false;
1637     }
1638     else
1639     {
1640         CCoinsViewCache view(pcoinsTip);
1641
1642         for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1643         {
1644             CCoins coins;
1645
1646             if (view.GetCoins(it->first.txhash, coins))
1647             {
1648                 if (coins.IsAvailable(it->first.index))
1649                 {
1650                     // if this is a transfer output, optionally to this chain, add it to the input vector
1651                     // chain filter was applied in index search
1652                     COptCCParams p;
1653                     COptCCParams m;
1654                     CReserveTransfer rt;
1655                     if (::IsPayToCryptoCondition(coins.vout[it->first.index].scriptPubKey, p) && p.evalCode == EVAL_RESERVE_TRANSFER &&
1656                         p.vData.size() > 1 && 
1657                         p.version >= p.VERSION_V3 &&
1658                         (m = COptCCParams(p.vData.back())).IsValid() &&
1659                         (rt = CReserveTransfer(p.vData[0])).IsValid() &&
1660                         m.vKeys.size() > 1 &&
1661                         (nofilter || GetDestinationID(m.vKeys[1]) == chainFilter))
1662                     {
1663                         inputDescriptors.insert(make_pair(GetDestinationID(m.vKeys[1]),
1664                                                 make_pair(CInputDescriptor(coins.vout[it->first.index].scriptPubKey, 
1665                                                                            coins.vout[it->first.index].nValue, 
1666                                                                            CTxIn(COutPoint(it->first.txhash, it->first.index))), rt)));
1667                     }
1668                 }
1669             }
1670             else
1671             {
1672                 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1673             }
1674         }
1675         return true;
1676     }
1677 }
1678
1679 // returns all unspent chain transfer outputs with an optional chainFilter. if the chainFilter is not
1680 // NULL, only transfers to that chain are returned
1681 bool GetUnspentChainExports(uint160 chainID, multimap<uint160, pair<int, CInputDescriptor>> &exportOutputs)
1682 {
1683     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1684
1685     LOCK2(cs_main, mempool.cs);
1686
1687     if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT), 1, unspentOutputs))
1688     {
1689         return false;
1690     }
1691     else
1692     {
1693         CCoinsViewCache view(pcoinsTip);
1694
1695         for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1696         {
1697             CCoins coins;
1698
1699             if (view.GetCoins(it->first.txhash, coins))
1700             {
1701                 for (int i = 0; i < coins.vout.size(); i++)
1702                 {
1703                     if (coins.IsAvailable(i))
1704                     {
1705                         // if this is an export output, optionally to this chain, add it to the input vector
1706                         COptCCParams p;
1707                         CCrossChainExport cx;
1708                         if (coins.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
1709                             p.vData.size() && (cx = CCrossChainExport(p.vData[0])).IsValid())
1710                         {
1711                             exportOutputs.insert(make_pair(cx.systemID,
1712                                                      make_pair(coins.nHeight, CInputDescriptor(coins.vout[i].scriptPubKey, coins.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))))));
1713                         }
1714                     }
1715                 }
1716             }
1717             else
1718             {
1719                 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1720                 return false;
1721             }
1722         }
1723         return true;
1724     }
1725 }
1726
1727 bool GetNotarizationData(uint160 chainID, uint32_t ecode, CChainNotarizationData &notarizationData, vector<pair<CTransaction, uint256>> *optionalTxOut)
1728 {
1729     notarizationData.version = PBAAS_VERSION;
1730
1731     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1732     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentFinalizations;
1733     CCurrencyDefinition chainDef;
1734
1735     bool isEarned = ecode == EVAL_EARNEDNOTARIZATION;
1736     bool allFinalized = false;
1737
1738     // get the last unspent notarization for this currency
1739     if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZE_NOTARIZATION), 1, unspentFinalizations))
1740     {
1741         return false;
1742     }
1743     else
1744     {
1745         multimap<int32_t, pair<uint256, CPBaaSNotarization>> sorted;
1746         multimap<int32_t, pair<CTransaction, uint256>> sortedTxs;
1747
1748         notarizationData.lastConfirmed = 0;
1749
1750         // filter out all transactions that do not spend from the notarization thread, or originate as the
1751         // chain definition
1752         for (auto it = unspentFinalizations.begin(); it != unspentFinalizations.end(); it++)
1753         {
1754             // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1755             CTransaction ntx;
1756             uint256 blkHash;
1757
1758             if (myGetTransaction(it->first.txhash, ntx, blkHash))
1759             {
1760                 if (!chainDef.IsValid())
1761                 {
1762                     // try to make a chain definition out of each transaction, and keep the first one that is valid
1763                     std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(ntx);
1764                     if (chainDefs.size())
1765                     {
1766                         chainDef = chainDefs[0];
1767                     }
1768                 }
1769                 CPBaaSNotarization notarization = CPBaaSNotarization(ntx);
1770                 if (notarization.IsValid())
1771                 {
1772                     auto blkit = mapBlockIndex.find(blkHash);
1773                     if (blkit != mapBlockIndex.end())
1774                     {
1775                         // sort by block height, index by transaction id
1776                         sorted.insert(make_pair(blkit->second->GetHeight(), make_pair(it->first.txhash, notarization)));
1777                         if (optionalTxOut)
1778                         {
1779                             sortedTxs.insert(make_pair(blkit->second->GetHeight(), make_pair(ntx, blkHash)));
1780                         }
1781                     }
1782                     // if we are not on block 1 of a PBaaS chain and we are a first notarization that is not confirmed, none can be confirmed yet
1783                     if (notarization.prevHeight == 0 && !isEarned)
1784                     {
1785                         notarizationData.lastConfirmed = -1;
1786                     }
1787                 }
1788             }
1789             else
1790             {
1791                 printf("cannot retrieve transaction %s, may need to reindex\n", it->first.txhash.GetHex().c_str());
1792                 //return false;
1793             }
1794         }
1795
1796         if (!sorted.size())
1797         {
1798             allFinalized = true;
1799
1800             if (GetAddressUnspent(CCrossChainRPCData::GetConditionID(chainID, ecode), 1, unspentOutputs))
1801             {
1802                 // filter out all transactions that do not spend from the notarization thread, or originate as the
1803                 // chain definition
1804                 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
1805                 {
1806                     // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
1807                     CTransaction ntx;
1808                     uint256 blkHash;
1809
1810                     if (myGetTransaction(it->first.txhash, ntx, blkHash))
1811                     {
1812                         if (!chainDef.IsValid())
1813                         {
1814                             // try to make a chain definition out of each transaction, and keep the first one that is valid
1815                             std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(ntx);
1816                             if (chainDefs.size())
1817                             {
1818                                 chainDef = chainDefs[0];
1819                             }
1820                         }
1821                         CPBaaSNotarization notarization = CPBaaSNotarization(ntx);
1822                         if (notarization.IsValid())
1823                         {
1824                             auto blkit = mapBlockIndex.find(blkHash);
1825                             if (blkit != mapBlockIndex.end())
1826                             {
1827                                 // sort by block height, index by transaction id
1828                                 sorted.insert(make_pair(blkit->second->GetHeight(), make_pair(it->first.txhash, notarization)));
1829                                 if (optionalTxOut)
1830                                 {
1831                                     sortedTxs.insert(make_pair(blkit->second->GetHeight(), make_pair(ntx, blkHash)));
1832                                 }
1833                             }
1834                             // if we are not on block 1 of a PBaaS chain and we are a first notarization that is not confirmed, none can be confirmed yet
1835                             if (notarization.prevHeight == 0 && !isEarned)
1836                             {
1837                                 notarizationData.lastConfirmed = -1;
1838                             }
1839                         }
1840                     }
1841                     else
1842                     {
1843                         printf("cannot retrieve transaction %s, may need to reindex 2\n", it->first.txhash.GetHex().c_str());
1844                         //return false;
1845                     }
1846                 }
1847             }
1848
1849             if (!sorted.size())
1850             {
1851                 //printf("no notarizations found\n");
1852                 return false;
1853             }
1854         }
1855
1856         if (allFinalized)
1857         {
1858             notarizationData.vtx.push_back(sorted.begin()->second);
1859             if (optionalTxOut && sortedTxs.size())
1860             {
1861                 optionalTxOut->push_back(sortedTxs.begin()->second);
1862             }
1863             notarizationData.forks.push_back(vector<int32_t>(0));
1864             notarizationData.forks.back().push_back(0);
1865             notarizationData.bestChain = 0;
1866             return true;
1867         }
1868
1869         if (!chainDef.IsValid())
1870         {
1871             // the first entry of all forks must reference a confirmed transaction if there is one
1872             CTransaction rootTx;
1873             uint256 blkHash;
1874             auto prevHash = sorted.begin()->second.second.prevNotarization;
1875             if (!prevHash.IsNull())
1876             {
1877                 if (!myGetTransaction(prevHash, rootTx, blkHash))
1878                 {
1879                     return false;
1880                 }
1881
1882                 // ensure that we have a finalization output
1883                 COptCCParams p;
1884                 CPBaaSNotarization notarization;
1885                 CTransactionFinalization finalization;
1886                 uint32_t notarizeIdx, finalizeIdx;
1887
1888                 if (GetNotarizationAndFinalization(ecode, CMutableTransaction(rootTx), notarization, &notarizeIdx, &finalizeIdx))
1889                 {
1890                     notarizationData.vtx.insert(notarizationData.vtx.begin(), make_pair(prevHash, notarization));
1891                     notarizationData.lastConfirmed = 0;
1892                     if (optionalTxOut)
1893                     {
1894                         optionalTxOut->insert(optionalTxOut->begin(), make_pair(rootTx, blkHash));
1895                     }
1896                 }
1897                 // debugging, this else is not needed
1898                 else
1899                 {
1900                     printf("previous transaction does not have both notarization and finalizaton outputs\n");
1901                 }
1902             }
1903             else
1904             {
1905                 if (!isEarned)
1906                 {
1907                     notarizationData.lastConfirmed = -1;
1908                 }
1909             }
1910         }
1911         else if (!chainDef.IsToken())
1912         {
1913             if (!isEarned)
1914             {
1915                 // we still have the chain definition in our forks, so no notarization has been confirmed yet
1916                 notarizationData.lastConfirmed = -1;
1917             }
1918         }
1919
1920         multimap<uint256, pair<int32_t, int32_t>> references;       // associates the txid, the fork index, and the index in the fork
1921
1922         for (auto p : sorted)
1923         {
1924             notarizationData.vtx.push_back(make_pair(p.second.first, p.second.second));
1925         }
1926
1927         if (optionalTxOut)
1928         {
1929             for (auto p : sortedTxs)
1930             {
1931                 optionalTxOut->push_back(p.second);
1932             }
1933         }
1934
1935         // we now have all unspent notarizations sorted by block height, and the last confirmed notarization as first, if there
1936         // is one. if there is a confirmed notarization, all forks should refer to it, or they are invalid and should be spent.
1937
1938         // find roots and create a chain from each
1939         for (int32_t i = 0; i < notarizationData.vtx.size(); i++)
1940         {
1941             auto &nzp = notarizationData.vtx[i];
1942             auto it = nzp.second.prevNotarization.IsNull() ? references.end() : references.find(nzp.second.prevNotarization);
1943
1944             int32_t chainIdx = 0;
1945             int32_t posIdx = 0;
1946
1947             // do we refer to a notarization that is already in a fork?
1948             if (it != references.end())
1949             {
1950                 std::vector<int32_t> &fork = notarizationData.forks[it->second.first];
1951
1952                 // if it is the end of the fork, put this entry there, if not the end, copy up to it and start another fork
1953                 if (it->second.second == (fork.size() - 1))
1954                 {
1955                     fork.push_back(i);
1956                     chainIdx = it->second.first;
1957                     posIdx = fork.size() - 1;
1958                 }
1959                 else
1960                 {
1961                     notarizationData.forks.push_back(vector<int32_t>(&fork[0], &fork[it->second.second] + 1));
1962                     notarizationData.forks.back().push_back(i);
1963                     chainIdx = notarizationData.forks.size() - 1;
1964                     posIdx = notarizationData.forks.back().size() - 1;
1965                 }
1966             }
1967             else
1968             {
1969                 // start a new fork that references no one else
1970                 notarizationData.forks.push_back(vector<int32_t>(0));
1971                 notarizationData.forks.back().push_back(i);
1972                 chainIdx = notarizationData.forks.size() - 1;
1973                 posIdx = notarizationData.forks.back().size() - 1;
1974             }
1975             references.insert(make_pair(nzp.first, make_pair(chainIdx, posIdx)));
1976         }
1977
1978         CChainPower best;
1979
1980         // now, we should have all forks in vectors
1981         // they should all have roots that point to the same confirmed or initial notarization, which should be enforced by chain rules
1982         // the best chain should simply be the tip with most power
1983         for (int i = 0; i < notarizationData.forks.size(); i++)
1984         {
1985             CChainPower curPower = ExpandCompactPower(notarizationData.vtx[notarizationData.forks[i].back()].second.compactPower, i);
1986             if (curPower > best)
1987             {
1988                 best = curPower;
1989             }
1990         }
1991         notarizationData.bestChain = best.nHeight;
1992         return true;
1993     }
1994 }
1995
1996 UniValue getnotarizationdata(const UniValue& params, bool fHelp)
1997 {
1998     if (fHelp || params.size() < 1 || params.size() > 2)
1999     {
2000         throw runtime_error(
2001             "getnotarizationdata \"currencyid\" accepted\n"
2002             "\nReturns the latest PBaaS notarization data for the specifed currencyid.\n"
2003
2004             "\nArguments\n"
2005             "1. \"currencyid\"                  (string, required) the hex-encoded ID or string name  search for notarizations on\n"
2006             "2. \"accepted\"                    (bool, optional) accepted, not earned notarizations, default: true if on\n"
2007             "                                                    VRSC or VRSCTEST, false otherwise\n"
2008
2009             "\nResult:\n"
2010             "{\n"
2011             "  \"version\" : n,                 (numeric) The notarization protocol version\n"
2012             "}\n"
2013
2014             "\nExamples:\n"
2015             + HelpExampleCli("getnotarizationdata", "\"currencyid\" true")
2016             + HelpExampleRpc("getnotarizationdata", "\"currencyid\"")
2017         );
2018     }
2019
2020     CheckPBaaSAPIsValid();
2021
2022     uint160 chainID;
2023     CChainNotarizationData nData;
2024     uint32_t ecode;
2025     
2026     if (IsVerusActive())
2027     {
2028         ecode = EVAL_ACCEPTEDNOTARIZATION;
2029     }
2030     else
2031     {
2032         ecode = EVAL_EARNEDNOTARIZATION;
2033     }
2034
2035     LOCK2(cs_main, mempool.cs);
2036
2037     chainID = GetChainIDFromParam(params[0]);
2038
2039     if (chainID.IsNull())
2040     {
2041         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid currencyid");
2042     }
2043
2044     if (params.size() > 1)
2045     {
2046         if (!params[1].get_bool())
2047         {
2048             ecode = EVAL_EARNEDNOTARIZATION;
2049         }
2050     }
2051
2052     if (GetNotarizationData(chainID, ecode, nData))
2053     {
2054         return nData.ToUniValue();
2055     }
2056     else
2057     {
2058         return NullUniValue;
2059     }
2060 }
2061
2062 // get inputs for all of the unspent reward outputs sent to a specific reward type for the range specified
2063 // returns the total input amount
2064 CAmount GetUnspentRewardInputs(const CCurrencyDefinition &chainDef, vector<CInputDescriptor> &inputs, int32_t serviceCode, int32_t height)
2065 {
2066     CAmount retval = 0;
2067     uint160 chainID = chainDef.GetID();
2068
2069     // look for unspent outputs that match the addressout hashed with service code
2070     CKeyID keyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, serviceCode), EVAL_SERVICEREWARD));
2071
2072     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
2073
2074     if (GetAddressUnspent(keyID, 1, unspentOutputs))
2075     {
2076         // spend all billing periods prior or equal to this one
2077         int billingPeriod = height / chainDef.billingPeriod;
2078
2079         // we need to look through the inputs to ensure that they are
2080         // actual service reward outputs in the correct billing periof, since we don't currently prevent other types of transaction outputs from being
2081         // sent to the same address, though doing so would burn its value anyhow
2082
2083         LOCK(cs_main);
2084         for (auto output : unspentOutputs)
2085         {
2086             // printf("txid: %s\n", it->first.txhash.GetHex().c_str());
2087             CServiceReward sr;
2088             CCoins coins;
2089             if (pcoinsTip->GetCoins(output.first.txhash, coins))
2090             {
2091                 for (auto txout : coins.vout)
2092                 {
2093                     COptCCParams p;
2094                     if (!txout.IsNull() && IsPayToCryptoCondition(txout.scriptPubKey, p) && p.evalCode == EVAL_SERVICEREWARD)
2095                     {
2096                         FromVector(p.vData[0], sr);
2097                         if (sr.IsValid())
2098                         {
2099                             inputs.push_back(CInputDescriptor(txout.scriptPubKey, txout.nValue, CTxIn(output.first.txhash, output.first.index)));
2100                             retval += txout.nValue;
2101                         }
2102                     }
2103                     else
2104                     {
2105                         LogPrintf("GetUnspentRewardInputs: cannot retrieve transaction %s\n", output.first.txhash.GetHex().c_str());
2106                         printf("GetUnspentRewardInputs: cannot retrieve transaction %s\n", output.first.txhash.GetHex().c_str());
2107                     }
2108                 }
2109             }
2110         }
2111     }
2112     return retval;
2113 }
2114
2115 // this adds any new notarization rewards that have been sent to the notarization reward pool for this
2116 // billing period since last accepted notarization, up to a maximum number of inputs
2117 CAmount AddNewNotarizationRewards(CCurrencyDefinition &chainDef, vector<CInputDescriptor> &inputs, CMutableTransaction mnewTx, int32_t height)
2118 {
2119     // get current chain info
2120     CAmount newIn = 0;
2121     newIn = GetUnspentRewardInputs(chainDef, inputs, SERVICE_NOTARIZATION, height);
2122     for (auto input : inputs)
2123     {
2124         mnewTx.vin.push_back(input.txIn);
2125     }
2126     return newIn;
2127 }
2128
2129 UniValue submitacceptednotarization(const UniValue& params, bool fHelp)
2130 {
2131     if (fHelp || params.size() != 1)
2132     {
2133         throw runtime_error(
2134             "submitacceptednotarization \"hextx\"\n"
2135             "\nFinishes an almost complete notarization transaction based on the notary chain and the current wallet or pubkey.\n"
2136             "\nIf successful in submitting the transaction based on all rules, a transaction ID is returned, otherwise, NULL.\n"
2137
2138             "\nArguments\n"
2139             "1. \"hextx\"                       (hexstring, required) partial hex-encoded notarization transaction to submit\n"
2140             "                                   transaction should have only one notarization and one opret output\n"
2141
2142             "\nResult:\n"
2143             "txid                               (hexstring) transaction ID of submitted transaction\n"
2144
2145             "\nExamples:\n"
2146             + HelpExampleCli("submitacceptednotarization", "\"hextx\"")
2147             + HelpExampleRpc("submitacceptednotarization", "\"hextx\"")
2148         );
2149     }
2150
2151     CheckPBaaSAPIsValid();
2152
2153     // decode the transaction and ensure that it is formatted as expected
2154     CTransaction notarization;
2155     CPBaaSNotarization pbn;
2156
2157     if (!DecodeHexTx(notarization, params[0].get_str()) || 
2158         notarization.vin.size() || 
2159         notarization.vout.size() != 2 ||
2160         !(pbn = CPBaaSNotarization(notarization)).IsValid() ||
2161         !notarization.vout.back().scriptPubKey.IsOpReturn())
2162     {
2163         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid notarization transaction");
2164     }
2165
2166     LOCK2(cs_main, mempool.cs);
2167
2168     CCurrencyDefinition chainDef;
2169     int32_t chainDefHeight;
2170     if (!GetCurrencyDefinition(pbn.currencyID, chainDef, &chainDefHeight))
2171     {
2172         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain notarization");
2173     }
2174
2175     // ensure we are still eligible to submit
2176     // finalize all transactions we can and send the notarization reward, plus all orphaned finalization outputs
2177     // to the confirmed recipient
2178
2179     CChainNotarizationData nData;
2180     vector<pair<CTransaction, uint256>> txesBlkHashes;
2181
2182     bool success = GetNotarizationData(pbn.currencyID, EVAL_ACCEPTEDNOTARIZATION, nData, &txesBlkHashes);
2183
2184     // get notarization data and check all transactions
2185     if (success)
2186     {
2187
2188         //LogPrintf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
2189         //printf("Accepted notarization GetNotarizationData returns %lu entries\n", nData.vtx.size());
2190
2191         // if any notarization exists that is accepted and more recent than the last one, but one we still agree with,
2192         // we cannot submit, aside from that, we will prepare and submit
2193         set<uint256> priorBlocks;
2194         map<uint256, CPBaaSNotarization *> notarizationData;
2195
2196         // printf("opRet: %s\n", notarization.vout[notarization.vout.size() - 1].scriptPubKey.ToString().c_str());
2197
2198         auto chainObjects = RetrieveOpRetArray(notarization.vout[notarization.vout.size() - 1].scriptPubKey);
2199
2200         bool stillValid = false;
2201         if (chainObjects.size() && chainObjects.back()->objectType == CHAINOBJ_PRIORBLOCKS)
2202         {
2203             // once here, default to true
2204             stillValid = true;
2205
2206             CPriorBlocksCommitment &pbc = ((CChainObject<CPriorBlocksCommitment> *)chainObjects.back())->object;
2207             for (auto prior : pbc.priorBlocks)
2208             {
2209                 priorBlocks.insert(prior);
2210             }
2211
2212             for (auto it = nData.vtx.rbegin(); it != nData.vtx.rend(); it++)
2213             {
2214                 // if any existing, accepted notarization is a subset of us, don't post, we will be rejected anyhow
2215                 if (priorBlocks.count(it->second.notarizationPreHash))
2216                 {
2217                     stillValid = false;
2218                     break;
2219                 }
2220                 else
2221                 {
2222                     notarizationData.insert(make_pair(it->first, &it->second));
2223                 }
2224             }
2225         }
2226
2227         DeleteOpRetObjects(chainObjects);
2228
2229         auto lastIt = notarizationData.find(pbn.prevNotarization);
2230
2231         if (!stillValid || (lastIt == notarizationData.end()))
2232         {
2233             //printf("Notarization not matched or invalidated by prior notarization\n");
2234             throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization not matched or invalidated by prior notarization");
2235         }
2236
2237         if (pbn.prevHeight != lastIt->second->notarizationHeight)
2238         {
2239             printf("Notarization heights not matched with previous notarization\n");
2240             throw JSONRPCError(RPC_VERIFY_REJECTED, "Notarization heights not matched with previous notarization");
2241         }
2242
2243         if (pbn.prevHeight != 0 && (pbn.prevHeight + CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED > pbn.notarizationHeight))
2244         {
2245             //printf("Less than minimum number of blocks between notarizations\n");
2246             throw JSONRPCError(RPC_VERIFY_REJECTED, "Less than minimum number of blocks between notarizations");
2247         }
2248
2249         // valid, spend notarization outputs, all needed finalizations, add any applicable reward output for 
2250         // confirmation to confirmed notary and miner of block,
2251         // add our output address and submit
2252         CMutableTransaction mnewTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
2253         vector<CInputDescriptor> notarizationInputs;
2254         for (auto input : notarization.vin)
2255         {
2256             mnewTx.vin.push_back(input);
2257         }
2258         for (auto output : notarization.vout)
2259         {
2260             mnewTx.vout.push_back(output);
2261         }
2262
2263         CTransaction lastTx = txesBlkHashes.back().first;
2264
2265         int32_t confirmedInput = -1;
2266         int32_t confirmedIndex;
2267         CTxDestination payee;
2268
2269         uint32_t notarizationIdx = -1, finalizationIdx = -1;
2270         CPBaaSNotarization dummy;
2271
2272         notarizationInputs = AddSpendsAndFinalizations(nData, pbn.prevNotarization, mnewTx, &confirmedInput, &confirmedIndex, &payee);
2273
2274         // if we got our inputs, add finalization
2275         if (notarizationInputs.size())
2276         {
2277             CCcontract_info CC;
2278             CCcontract_info *cp;
2279             cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2280
2281             // use public key of cc
2282             CPubKey pk(ParseHex(CC.CChexstr));
2283             std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(pbn.currencyID, EVAL_FINALIZE_NOTARIZATION))});
2284
2285             std::vector<CTxDestination> dests;
2286             if (chainDef.notarizationProtocol == chainDef.NOTARIZATION_NOTARY_CHAINID)
2287             {
2288                 dests = std::vector<CTxDestination>({CIdentityID(chainDef.GetID())});
2289             }
2290             else
2291             {
2292                 dests = std::vector<CTxDestination>({pk});
2293             }
2294
2295             CTransactionFinalization nf(confirmedInput);
2296
2297             mnewTx.vout.insert(mnewTx.vout.begin() + (mnewTx.vout.size() - 1), 
2298                                 CTxOut(CCurrencyDefinition::DEFAULT_OUTPUT_VALUE, 
2299                                        MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf), &indexDests)));
2300         }
2301
2302         if (notarizationInputs.size() && GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, mnewTx, dummy, &notarizationIdx, &finalizationIdx))
2303         {
2304             // we need to add outputs to pay the reward to the confirmed notary and block miner/staker of that notarization
2305             // the rest goes back into the notarization thread
2306             // first input should be the notarization thread
2307             CTransaction newTx(mnewTx);
2308             CTransaction confirmedTx;
2309             CPBaaSNotarization confirmedPBN;
2310             CBlockIndex *pindex = NULL;
2311             uint256 hashBlock;
2312             CBlock confirmedBlock;
2313
2314             CAmount valueIn;
2315
2316             BlockMap::iterator it;
2317             {
2318                 LOCK2(cs_main, mempool.cs);
2319                 // get value in
2320                 CCoinsViewCache view(pcoinsTip);
2321                 int64_t dummyInterest;
2322                 valueIn = view.GetValueIn(chainActive.LastTip()->GetHeight(), &dummyInterest, newTx, chainActive.LastTip()->nTime);
2323
2324                 if (!valueIn)
2325                 {
2326                     throw JSONRPCError(RPC_TRANSACTION_REJECTED, "unable to spend necessary transaction outputs");
2327                 }
2328
2329                 if (confirmedInput != -1)
2330                 {
2331                     // get data from confirmed tx and block that contains confirmed tx
2332                     confirmedTx = txesBlkHashes[confirmedIndex].first;
2333                     hashBlock = txesBlkHashes[confirmedIndex].second;
2334                     if ((it = mapBlockIndex.find(hashBlock)) != mapBlockIndex.end())
2335                     {
2336                         pindex = mapBlockIndex.find(hashBlock)->second;
2337                     }
2338
2339                     // add all inputs that might provide notary reward and calculate notary reward based on that plus current
2340                     // notarization input value divided by number of blocks left in billing period, times blocks per notarization
2341                     if (pindex)
2342                     {
2343                         valueIn += AddNewNotarizationRewards(chainDef, notarizationInputs, mnewTx, pindex->GetHeight());
2344                     }
2345                     else
2346                     {
2347                         LogPrintf("submitacceptednotarization: cannot find chain %s, possible corrupted database\n", chainDef.name.c_str());
2348                         printf("submitacceptednotarization: cannot find chain %s, possible corrupted database\n", chainDef.name.c_str());
2349                     }
2350                 }
2351             }
2352
2353             // recipient of notary rewards and miner to share it with
2354             // notary recipient is the one from the confirmed notarization
2355             // and miner recipient is from the block it was mined into
2356             CTxDestination notaryRecipient, minerRecipient;
2357
2358             // get recipients of any reward output
2359             if (confirmedInput != -1)
2360             {
2361                 LOCK(cs_main);
2362                 if (pindex && ReadBlockFromDisk(confirmedBlock, pindex, Params().GetConsensus()) && 
2363                     (confirmedPBN = CPBaaSNotarization(confirmedTx)).IsValid() &&
2364                     ExtractDestination(confirmedBlock.vtx[0].vout[0].scriptPubKey, minerRecipient, false))
2365                 {
2366                     notaryRecipient = confirmedPBN.notaryDest;
2367                 }
2368                 else
2369                 {
2370                     throw JSONRPCError(RPC_DATABASE_ERROR, "unable to retrieve confirmed notarization data");
2371                 }
2372             }
2373
2374             // minimum amount must go to main thread and finalization, then divide what is left among blocks in the billing period
2375             uint64_t blocksLeft = chainDef.billingPeriod - ((confirmedPBN.notarizationHeight - chainDef.startBlock) % chainDef.billingPeriod);
2376             CAmount valueOut = 0;
2377             CAmount notaryValueOut;
2378
2379             if (valueIn > (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1))
2380             {
2381                 if (confirmedInput != -1)
2382                 {
2383                     if (valueIn < (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1)))
2384                     {
2385                         valueOut = valueIn - (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1);
2386                     }
2387                     else
2388                     {
2389                         valueOut = (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * (valueIn - (CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1))) / blocksLeft;
2390                     }
2391                 }
2392                 notaryValueOut = valueIn - ((CCurrencyDefinition::DEFAULT_OUTPUT_VALUE << 1) + valueOut);
2393             }
2394             else
2395             {
2396                 notaryValueOut = 0;
2397             }
2398
2399             if (notaryValueOut >= (PBAAS_MINNOTARIZATIONOUTPUT << 1))
2400             {
2401                 // pay the confirmed notary with
2402                 // notarization reward for this billing period / remaining blocks in the billing period * min blocks in notarization
2403                 // the finalization out has minimum, the notarization out has all the remainder
2404                 // outputs we should have here:
2405                 // 1) notarization out
2406                 // 2) finalization out
2407                 // 3) op_ret
2408                 //
2409                 // send:
2410                 // 66% of output to notary address
2411                 // 33% of output to primary address of block reward
2412                 auto insertIt = mnewTx.vout.begin() + (finalizationIdx + 1);
2413                 if (valueOut)
2414                 {
2415                     CAmount minerOutput = valueOut / 3;
2416                     CAmount notaryOutput = valueOut / 3 * 2;
2417                     mnewTx.vout.insert(insertIt, CTxOut(minerOutput, GetScriptForDestination(minerRecipient)));
2418                     mnewTx.vout.insert(insertIt, CTxOut(notaryOutput, GetScriptForDestination(notaryRecipient)));
2419                 }
2420             }
2421             else
2422             {
2423                 notaryValueOut += valueOut;
2424                 valueOut = 0;
2425             }
2426             
2427             if ((notaryValueOut + valueOut + CCurrencyDefinition::DEFAULT_OUTPUT_VALUE) > valueIn)
2428             {
2429                 notaryValueOut = valueIn;
2430                 printf("Not enough funds to notarize %s\n", chainDef.name.c_str());
2431                 LogPrintf("Not enough funds to notarize %s\n", chainDef.name.c_str());
2432             }
2433
2434             CCcontract_info CC;
2435             CCcontract_info *cp;
2436
2437             // make the output for the other chain's notarization
2438             cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
2439             CPubKey pk(ParseHex(CC.CChexstr));
2440             std::vector<CTxDestination> dests;
2441
2442             std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(pbn.currencyID, EVAL_ACCEPTEDNOTARIZATION))});
2443             if (chainDef.notarizationProtocol == chainDef.NOTARIZATION_NOTARY_CHAINID)
2444             {
2445                 dests = std::vector<CTxDestination>({CIdentityID(chainDef.GetID())});
2446             }
2447             else
2448             {
2449                 dests = std::vector<CTxDestination>({pk});
2450             }
2451             
2452             mnewTx.vout[notarizationIdx] = CTxOut(notaryValueOut, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn), &indexDests));
2453
2454             CTransaction ntx(mnewTx);
2455
2456             uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
2457
2458             // sign the transaction and submit
2459             for (int i = 0; i < ntx.vin.size(); i++)
2460             {
2461                 bool signSuccess;
2462                 SignatureData sigdata;
2463                 CAmount value;
2464                 const CScript *pScriptPubKey;
2465
2466                 if (i < notarizationInputs.size())
2467                 {
2468                     pScriptPubKey = &notarizationInputs[i].scriptPubKey;
2469                     value = notarizationInputs[i].nValue;
2470
2471                     signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, value, SIGHASH_ALL), *pScriptPubKey, sigdata, consensusBranchId);
2472
2473                     if (!signSuccess)
2474                     {
2475                         fprintf(stderr,"submitacceptednotarization: failure to sign accepted notarization\n");
2476                         throw JSONRPCError(RPC_VERIFY_ERROR, "Failed to sign notarizaton for " + chainDef.name);
2477                     } else {
2478                         UpdateTransaction(mnewTx, i, sigdata);
2479                     }
2480                 }
2481             }
2482
2483             // add to mempool and submit transaction
2484             CTransaction tx(mnewTx);
2485
2486             CValidationState state;
2487             bool fMissingInputs;
2488             bool accepted;
2489             {
2490                 LOCK2(cs_main, mempool.cs);
2491                 accepted = AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs);
2492             }
2493             if (!accepted) {
2494                 if (state.GetRejectReason() != "")
2495                 {
2496                     printf("Cannot enter notarization into mempool for chain %s, %s\n", chainDef.name.c_str(), state.GetRejectReason().c_str());
2497                 }
2498                 if (state.IsInvalid()) {
2499                     throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
2500                 } else {
2501                     if (fMissingInputs) {
2502                         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
2503                     }
2504                     throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason());
2505                 }
2506             }
2507             else
2508             {
2509                 RelayTransaction(tx);
2510             }
2511
2512             return newTx.GetHash().GetHex();
2513         }
2514     }
2515     throw JSONRPCError(RPC_VERIFY_REJECTED, "Failed to get notarizaton data for chainID: " + pbn.currencyID.GetHex());
2516 }
2517
2518 UniValue getcrossnotarization(const UniValue& params, bool fHelp)
2519 {
2520     if (fHelp || params.size() < 2 || params.size() > 3)
2521     {
2522         throw runtime_error(
2523             "getcrossnotarization \"systemid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'\n"
2524             "\nCreates and returns a cross notarization of this chain back to the system id caller,\n"
2525             "assuming that a prior notarization for that system is found.\n"
2526
2527             "\nArguments\n"
2528             "1. \"systemid\"                    (string, required) the currency system id to search for notarizations on\n"
2529             "2. \"txidlist\"                    (stringarray, optional) list of transaction ids to check in preferred order, first found is returned\n"
2530             "3. \"accepted\"                    (bool, optional) accepted, not earned notarizations, default: true if on\n"
2531             "                                                    VRSC or VRSCTEST, false otherwise\n"
2532
2533             "\nResult:\n"
2534             "{\n"
2535             "  \"crosstxid\" : \"xxxx\",        (hexstring) cross-transaction id of the notarization that matches, which is one in the arguments\n"
2536             "  \"txid\" : \"xxxx\",             (hexstring) transaction id of the notarization that was found\n"
2537             "  \"rawtx\" : \"hexdata\",         (hexstring) entire matching transaction data, serialized\n"
2538             "  \"newtx\" : \"hexdata\"          (hexstring) the proposed notarization transaction with an opret and opretproof\n"
2539             "}\n"
2540
2541             "\nExamples:\n"
2542             + HelpExampleCli("getcrossnotarization", "\"systemid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
2543             + HelpExampleRpc("getcrossnotarization", "\"systemid\" '[\"notarizationtxid1\", \"notarizationtxid2\", ...]'")
2544         );
2545     }
2546
2547     uint160 chainID;
2548     uint32_t ecode;
2549     UniValue ret(UniValue::VOBJ);
2550
2551     if (IsVerusActive())
2552     {
2553         ecode = EVAL_ACCEPTEDNOTARIZATION;
2554     }
2555     else
2556     {
2557         ecode = EVAL_EARNEDNOTARIZATION;
2558     }
2559
2560     if (params[0].type() == UniValue::VSTR)
2561     {
2562         try
2563         {
2564             chainID.SetHex(params[0].get_str());
2565         }
2566         catch(const std::exception& e)
2567         {
2568         }
2569     }
2570
2571     if (chainID.IsNull())
2572     {
2573         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid currencyid");
2574     }
2575
2576     if (params.size() > 2)
2577     {
2578         if (!params[2].get_bool())
2579         {
2580             ecode = EVAL_EARNEDNOTARIZATION;
2581         }
2582     }
2583
2584     uint32_t crosscode;
2585     if (ecode == EVAL_ACCEPTEDNOTARIZATION)
2586     {
2587         crosscode = EVAL_EARNEDNOTARIZATION;
2588     }
2589     else
2590     {
2591         crosscode = EVAL_ACCEPTEDNOTARIZATION;
2592     }
2593
2594     if (params[1].type() != UniValue::VARR)
2595     {
2596         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid second parameter object type: " + itostr(params[1].type()));
2597     }
2598
2599     vector<UniValue> values = params[1].getValues();
2600     set<uint256> txids;
2601     for (int32_t i = 0; i < values.size(); i++)
2602     {
2603         auto txid = uint256S(values[i].get_str());
2604         if (txid.IsNull())
2605         {
2606             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter for notarization ID: " + values[i].get_str());
2607         }
2608         txids.insert(txid);
2609     }
2610
2611     CChainNotarizationData nData;
2612
2613     LOCK2(cs_main, mempool.cs);
2614
2615     CCurrencyDefinition chainDef;
2616     if (!GetCurrencyDefinition(chainID, chainDef))
2617     {
2618         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain ID: " + EncodeDestination(CIdentityID(chainID)));
2619     }
2620
2621     vector<pair<CTransaction, uint256>> nTxes;
2622
2623     // get notarization data and check all transactions
2624     if (GetNotarizationData(chainID, ecode, nData, &nTxes))
2625     {
2626         CTransaction tx;
2627         CPBaaSNotarization ourLast;
2628         uint256 blkHash;
2629         bool found = false;
2630
2631         // loop in reverse through list, as most recent is at end
2632         for (int32_t i = nData.vtx.size() - 1; i >= 0; i--)
2633         {
2634             const pair<uint256, CPBaaSNotarization> &nzp = nData.vtx[i];
2635             tx = nTxes[i].first;
2636             blkHash = nTxes[i].second;
2637             auto nit = txids.find(nzp.second.crossNotarization);
2638             if (i == 0 || !(nit == txids.end()))
2639             {
2640                 found = true;
2641                 // we have the first matching transaction, return it
2642                 ret.push_back(Pair("crosstxid", nzp.second.crossNotarization.GetHex()));
2643                 ret.push_back(Pair("txid", nzp.first.GetHex()));
2644                 ret.push_back(Pair("rawtx", EncodeHexTx(tx)));
2645                 break;
2646             }
2647         }
2648
2649         // now make the basic notarization for this chain that the other chain daemon can complete
2650         // after it is returned
2651         if (found)
2652         {
2653             // make sure our MMR matches our tip height, etc.
2654             LOCK(cs_main);
2655
2656             CPBaaSNotarization prevNotarization(tx);
2657
2658             if(!prevNotarization.IsValid())
2659             {
2660                 throw JSONRPCError(RPC_TRANSACTION_ERROR, "Invalid prior notarization");
2661             }
2662
2663             int32_t proofheight = chainActive.Height();
2664             ChainMerkleMountainView mmv(chainActive.GetMMR(), proofheight);
2665             uint256 mmrRoot = mmv.GetRoot();
2666             uint256 preHash = mmv.mmr.GetNode(proofheight).hash;
2667
2668             // prove the last notarization txid with new MMR, which also provides its blockhash and power as part of proof
2669             CBlock block;
2670             CBlockIndex *pnindex = mapBlockIndex.find(blkHash)->second;
2671
2672             if(!pnindex || !ReadBlockFromDisk(block, pnindex, Params().GetConsensus(), 0))
2673             {
2674                 throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
2675             }
2676
2677             int32_t prevHeight = pnindex->GetHeight();
2678
2679             // which transaction are we in this block?
2680             std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2681
2682             if (!GetAddressIndex(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZE_NOTARIZATION), 1, addressIndex, prevHeight, prevHeight))
2683             {
2684                 throw JSONRPCError(RPC_INTERNAL_ERROR, "Address index read error - possible corruption in address index");
2685             }
2686
2687             uint256 txHash = tx.GetHash();
2688             unsigned int txIndex;
2689             for (txIndex = 0; txIndex < addressIndex.size(); txIndex++)
2690             {
2691                 if (addressIndex[txIndex].first.txhash == txHash)
2692                 {
2693                     break;
2694                 }
2695             }
2696
2697             if (txIndex == addressIndex.size())
2698             {
2699                 throw JSONRPCError(RPC_INTERNAL_ERROR, "Notarization not found in address index - possible corruption");
2700             }
2701             else
2702             {
2703                 // get index in the block as our transaction index for proofs
2704                 txIndex = addressIndex[txIndex].first.index;
2705             }
2706
2707             CMMRProof blockProof;
2708             chainActive.GetBlockProof(mmv, blockProof, proofheight);
2709
2710             // create and store the notarization proof of chain
2711             vector<CBaseChainObject *> chainObjects;
2712             COpRetProof orp;
2713
2714             // if bock headers are merge mined, keep header refs, not headers
2715             CBlockHeaderAndProof bhp(blockProof, chainActive[proofheight]->GetBlockHeader());
2716             CChainObject<CBlockHeaderAndProof> latestHeaderObj(CHAINOBJ_HEADER, bhp);
2717
2718             chainObjects.push_back(&latestHeaderObj);
2719             orp.AddObject(CHAINOBJ_HEADER, bhp.BlockHash());
2720
2721             // prove the important last notarization components in the tx.
2722             // we care about the following components:
2723             //  input 0 - can be used to determine if the tx is a coinbase and is the spending thread for notarization
2724             //  notarization output - the notarization state being attested to
2725             //  currency definition output matching notarization output - signifies first notarization
2726             std::vector<std::pair<int16_t, int16_t>> txComponents({{(int16_t)CTransactionHeader::TX_PREVOUTSEQ, (int16_t)0}});
2727             for (int i = 0; i < tx.vout.size(); i++)
2728             {
2729                 COptCCParams p;
2730                 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
2731                 {
2732                     if (p.evalCode == ecode)
2733                     {
2734                         txComponents.push_back({CTransactionHeader::TX_OUTPUT, i});
2735                     }
2736                     else if (p.evalCode == EVAL_CURRENCY_DEFINITION && 
2737                              p.vData.size())
2738                     {
2739                         CCurrencyDefinition definedCurrency(p.vData[0]);
2740                         if (definedCurrency.IsValid() &&
2741                             definedCurrency.GetID() == chainID)
2742                         {
2743                             txComponents.push_back({CTransactionHeader::TX_OUTPUT, i});
2744                         }
2745                     }
2746                 }
2747             }
2748             CPartialTransactionProof txProof = block.GetPartialTransactionProof(tx, txIndex, txComponents);
2749
2750             // add the cross transaction from this chain to return
2751             CChainObject<CPartialTransactionProof> strippedTxObj(CHAINOBJ_TRANSACTION_PROOF, txProof);
2752
2753             chainObjects.push_back(&strippedTxObj);
2754             orp.AddObject(CHAINOBJ_TRANSACTION_PROOF, tx.GetHash());
2755
2756             // add the MMR block nodes between the last notarization and this one, containing root that combines merkle, block, and compact power hashes
2757             CPriorBlocksCommitment priorBlocks;
2758             int numPriorBlocks = proofheight - ourLast.crossHeight;
2759
2760             if (numPriorBlocks > PBAAS_MAXPRIORBLOCKS || numPriorBlocks > (proofheight - 1))
2761                 numPriorBlocks = PBAAS_MAXPRIORBLOCKS > (proofheight - 1) ? ((proofheight - 1) < 1 ? 0 : (proofheight - 1)) : PBAAS_MAXPRIORBLOCKS;
2762
2763             // push back the merkle, block hash, and block power commitments for prior blocks to ensure no
2764             // unintended notary overlap
2765             for (int i = numPriorBlocks; i >= 0; i--)
2766             {
2767                 priorBlocks.priorBlocks.push_back(mmv.mmr.GetNode(proofheight - i).hash);
2768             }
2769
2770             CChainObject<CPriorBlocksCommitment> priorBlocksObj(CHAINOBJ_PRIORBLOCKS, priorBlocks);
2771             chainObjects.push_back(&priorBlocksObj);
2772             orp.AddObject(CHAINOBJ_PRIORBLOCKS, ::GetHash(priorBlocks));
2773
2774             // get node keys and addresses
2775             vector<CNodeData> nodes;
2776             const static int MAX_NODES = 2;
2777
2778             {
2779                 LOCK(cs_vNodes);
2780                 if (!vNodes.empty())
2781                 {
2782                     for (int i = 0; i < vNodes.size(); i++)
2783                     {
2784                         CNodeStats stats;
2785                         vNodes[i]->copyStats(stats);
2786                         if (vNodes[i]->fSuccessfullyConnected && !vNodes[i]->fInbound)
2787                         {
2788                             CIdentityID idID(vNodes[i]->hashPaymentAddress);
2789                             nodes.push_back(CNodeData(vNodes[i]->addr.ToString(), EncodeDestination(CTxDestination(idID))));
2790                         }
2791                     }
2792                 }
2793             }
2794
2795             // reduce number to max by removing randomly
2796             while (nodes.size() > MAX_NODES)
2797             {
2798                 int toErase = GetRandInt(nodes.size() - 1);
2799                 nodes.erase(nodes.begin() + toErase);
2800             }
2801
2802             CTxDestination notaryDest;
2803             if (!VERUS_DEFAULTID.IsNull())
2804             {
2805                 notaryDest = VERUS_DEFAULTID;
2806             }
2807             else if (USE_EXTERNAL_PUBKEY)
2808             {
2809                 CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
2810                 if (pubKey.IsFullyValid())
2811                 {
2812                     notaryDest = pubKey;
2813                 }
2814             }
2815             else
2816             {
2817                 static bool printMsg = true;
2818                 if (printMsg)
2819                 {
2820                     printf("No notary public key recipient has been set, so this node cannot receive rewards for notarization\n");
2821                     LogPrintf("No notary public key recipient has been set, so this node cannot receive rewards for notarization\n");
2822                     printMsg = false;
2823                 }
2824             }
2825
2826             CBlockIndex *nzIndex = chainActive[proofheight];
2827             CCurrencyState currencyState = ConnectedChains.GetCurrencyState(proofheight);
2828
2829             // get the current block's MMR root and proof height
2830             CPBaaSNotarization notarization = CPBaaSNotarization(CCurrencyDefinition::NOTARIZATION_AUTO, 
2831                                                                  ASSETCHAINS_CHAINID,
2832                                                                  notaryDest,
2833                                                                  proofheight,
2834                                                                  mmrRoot,
2835                                                                  preHash,
2836                                                                  ArithToUint256(GetCompactPower(nzIndex->nNonce, nzIndex->nBits, nzIndex->nVersion)),
2837                                                                  currencyState,
2838                                                                  uint256(), 0,
2839                                                                  tx.GetHash(), prevNotarization.notarizationHeight,
2840                                                                  orp,
2841                                                                  nodes);
2842
2843             // we now have the chain objects, all associated proofs, and notarization data, make an appropriate transaction template with opret
2844             // and return it. notarization will need to be completed, so the only thing we really need to construct on this chain is the opret
2845             CMutableTransaction newNotarization = CreateNewContextualCMutableTransaction(Params().GetConsensus(), proofheight);
2846
2847             CCcontract_info CC;
2848             CCcontract_info *cp;
2849
2850             // make the output for the other chain's notarization
2851             cp = CCinit(&CC, crosscode);
2852
2853             std::vector<CTxDestination> dests;
2854             std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, crosscode))});
2855             if (chainDef.notarizationProtocol == chainDef.NOTARIZATION_NOTARY_CHAINID)
2856             {
2857                 dests = std::vector<CTxDestination>({CIdentityID(chainDef.GetID())});
2858             }
2859             else
2860             {
2861                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2862             }
2863             newNotarization.vout.push_back(CTxOut(CCurrencyDefinition::DEFAULT_OUTPUT_VALUE, 
2864                                                   MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(crosscode, dests, 1, &notarization), &indexDests)));
2865
2866             // make the unspent finalization output
2867             cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2868             if (chainDef.notarizationProtocol != chainDef.NOTARIZATION_NOTARY_CHAINID)
2869             {
2870                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2871             }
2872             indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_FINALIZE_NOTARIZATION))});
2873
2874             CTransactionFinalization nf;
2875             newNotarization.vout.push_back(CTxOut(DEFAULT_TRANSACTION_FEE, 
2876                                                   MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf), &indexDests)));
2877
2878             newNotarization.vout.push_back(CTxOut(0, StoreOpRetArray(chainObjects)));
2879
2880             CTransaction newTx(newNotarization);
2881             ret.push_back(Pair("newtx", EncodeHexTx(newTx)));
2882         }
2883     }
2884     return ret;
2885 }
2886
2887 UniValue paynotarizationrewards(const UniValue& params, bool fHelp)
2888 {
2889     if (fHelp || (params.size() != 2 && params.size() != 3))
2890     {
2891         throw runtime_error(
2892             "paynotarizationrewards \"currencyid\" \"amount\" \"billingperiod\"\n"
2893             "\nAdds some amount of funds to a specific billing period of a PBaaS chain, which will be released\n"
2894             "\nin the form of payments to notaries whose notarizations are confirmed.\n"
2895
2896             "\nArguments\n"
2897
2898             "\nResult:\n"
2899
2900             "\nExamples:\n"
2901             + HelpExampleCli("paynotarizationrewards", "\"hextx\"")
2902             + HelpExampleRpc("paynotarizationrewards", "\"hextx\"")
2903         );
2904     }
2905     CheckPBaaSAPIsValid();
2906
2907     uint160 chainID;
2908
2909     if (!pwalletMain)
2910     {
2911         throw JSONRPCError(RPC_INVALID_PARAMETER, "Paying notarization rewards requires an active wallet");
2912     }
2913
2914     chainID = GetChainIDFromParam(params[0]);
2915     if (chainID.IsNull())
2916     {
2917         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or currencyid");
2918     }
2919
2920     CAmount amount = AmountFromValue(params[1]);
2921     uint32_t billingPeriod = 0;
2922
2923     if (params.size() == 3)
2924     {
2925         uint32_t billingPeriod = uni_get_int(params[2]);
2926     }
2927
2928     CServiceReward sr = CServiceReward(SERVICE_NOTARIZATION, billingPeriod);
2929     
2930     CCcontract_info CC;
2931     CCcontract_info *cp;
2932     cp = CCinit(&CC, EVAL_SERVICEREWARD);
2933
2934     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2935
2936     std::vector<CTxDestination> dests({CKeyID(CCrossChainRPCData::GetConditionID(CCrossChainRPCData::GetConditionID(chainID, SERVICE_NOTARIZATION), EVAL_SERVICEREWARD))});
2937
2938     std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeCC1of1Vout(EVAL_SERVICEREWARD, amount, pk, dests, sr).scriptPubKey, amount}});
2939     CWalletTx wtx;
2940
2941     // create the transaction with native coin as input
2942     LOCK2(cs_main, pwalletMain->cs_wallet);
2943
2944     CReserveKey reserveKey(pwalletMain);
2945     CAmount fee;
2946     int nChangePos;
2947     string failReason;
2948
2949     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
2950     {
2951         throw JSONRPCError(RPC_TRANSACTION_ERROR, chainID.GetHex() + ": " + failReason);
2952     }
2953     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
2954     {
2955         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
2956     }
2957     return UniValue(wtx.GetHash().GetHex());
2958 }
2959
2960 UniValue listreservetransactions(const UniValue& params, bool fHelp)
2961 {
2962     if (fHelp || params.size() != 1)
2963     {
2964         throw runtime_error(
2965             "listreservetransactions (maxnumber) (minconfirmations)\n"
2966             "\nLists all reserve coin transactions sent to/from the current wallet.\n"
2967
2968             "\nArguments\n"
2969
2970             "\nResult:\n"
2971
2972             "\nExamples:\n"
2973             + HelpExampleCli("listreservetransactions", "100 0")
2974             + HelpExampleRpc("listreservetransactions", "100 0")
2975         );
2976     }
2977     // lists all transactions in a wallet that are 
2978     return NullUniValue;
2979 }
2980
2981 // this must be called after all initial contributions are updated in the currency definition.
2982 CCoinbaseCurrencyState GetInitialCurrencyState(const CCurrencyDefinition &chainDef)
2983 {
2984     bool isFractional = chainDef.IsFractional();
2985     CCurrencyState cState;
2986
2987     // calculate contributions and conversions
2988     const std::vector<CAmount> reserveFees(chainDef.currencies.size());
2989     std::vector<int64_t> conversions = chainDef.conversions;
2990
2991     CAmount nativeFees = 0;
2992     if (isFractional)
2993     {
2994         cState = CCurrencyState(chainDef.currencies,
2995                                 chainDef.weights,
2996                                 chainDef.contributions.size() ? chainDef.contributions : std::vector<int64_t>(chainDef.currencies.size(), 0),
2997                                 chainDef.initialFractionalSupply,
2998                                 0,
2999                                 chainDef.initialFractionalSupply,
3000                                 CCurrencyState::FLAG_VALID + CCurrencyState::FLAG_FRACTIONAL);
3001         cState.UpdateWithEmission(chainDef.GetTotalPreallocation());
3002         conversions = cState.PricesInReserve();
3003     }
3004     else
3005     {
3006         cState.currencies = chainDef.currencies;
3007         cState.reserves = conversions;
3008         CAmount PreconvertedNative = cState.ReserveToNative(CCurrencyValueMap(chainDef.currencies, chainDef.preconverted));
3009         cState = CCurrencyState(chainDef.currencies, 
3010                                 std::vector<int32_t>(0), 
3011                                 conversions,
3012                                 0, 
3013                                 PreconvertedNative,
3014                                 PreconvertedNative, 
3015                                 CCurrencyState::FLAG_VALID);
3016         cState.UpdateWithEmission(chainDef.GetTotalPreallocation());
3017     }
3018
3019     CCoinbaseCurrencyState retVal(cState, 
3020                                   0, 
3021                                   0, 
3022                                   chainDef.preconverted, 
3023                                   std::vector<int64_t>(chainDef.currencies.size()), 
3024                                   std::vector<int64_t>(chainDef.currencies.size()), 
3025                                   conversions,
3026                                   reserveFees,
3027                                   reserveFees);
3028
3029     return retVal;
3030 }
3031
3032 UniValue reserveexchange(const UniValue& params, bool fHelp)
3033 {
3034     if (fHelp || params.size() != 1)
3035     {
3036         throw runtime_error(
3037             "reserveexchange '[{\"toreserve\": 1, \"recipient\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'\n"
3038             "\nThis sends a Verus output as a JSON object or lists of Verus outputs as a list of objects to an address on the same or another chain.\n"
3039             "\nFunds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
3040
3041             "\nArguments\n"
3042             "       {\n"
3043             "           \"toreserve\"      : \"bool\",  (bool,   optional) if present, conversion is to the underlying reserve (Verus), if false, from Verus\n"
3044             "           \"recipient\"      : \"Rxxx\",  (string, required) recipient of converted funds or funds that failed to convert\n"
3045             "           \"amount\"         : n,         (int64,  required) amount of source coins that will be converted, depending on the toreserve flag, the rest is change\n"
3046             "           \"limit\"          : n,         (int64,  optional) price in reserve limit, below which for buys and above which for sells, execution will occur\n"
3047             "           \"validbefore\"    : n,         (int,    optional) block before which this can execute as a conversion, otherwise, it executes as a send with normal network fee\n"
3048             "           \"subtractfee\"    : \"bool\",  (bool,   optional) if true, reduce amount to destination by the fee amount, otherwise, add from inputs to cover fee"
3049             "       }\n"
3050
3051             "\nResult:\n"
3052             "       \"txid\" : \"transactionid\" (string) The transaction id.\n"
3053
3054             "\nExamples:\n"
3055             + HelpExampleCli("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
3056             + HelpExampleRpc("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
3057         );
3058     }
3059
3060     CheckPBaaSAPIsValid();
3061     throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Not yet implemented. Use sendcurrency in this release.");
3062 }
3063
3064 UniValue sendcurrency(const UniValue& params, bool fHelp)
3065 {
3066     if (fHelp || params.size() < 2 || params.size() > 3)
3067     {
3068         throw runtime_error(
3069             "sendcurrency \"fromaddress\" '[{\"address\":... ,\"amount\":...},...]' (returntx)\n"
3070             "\nThis sends one or many Verus outputs to one or many addresses on the same or another chain.\n"
3071             "Funds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
3072             "If \"fromaddress\" is specified, all funds will be taken from that address, otherwise funds may come\n"
3073             "from any source set of UTXOs controlled by the wallet.\n"
3074
3075             "\nArguments\n"
3076             "1. \"fromaddress\"             (string, required) The VerusID or address to send the funds from. \"*\" means all addresses\n"
3077             "2. \"outputs\"                 (array, required) An array of json objects representing currencies, amounts, and destinations to send.\n"
3078             "    [{\n"
3079             "      \"currency\": \"name\"   (string, required) Name of the source currency to send in this output, defaults to native of chain\n"
3080             "      \"amount\":amount        (numeric, required) The numeric amount of currency, denominated in source currency\n"
3081             "      \"convertto\":\"name\",  (string, optional) Valid currency to convert to, either a reserve of a native source, or fractional of reserve\n"
3082             "      \"address\":\"dest\"     (string, required) The address and optionally chain/system after the \"@\" as a system specific destination\n"
3083             "      \"refundto\":\"dest\"    (string, optional) For pre-conversions, this is where refunds will go, defaults to fromaddress\n"
3084             "      \"memo\":memo            (string, optional) If destination is a zaddr (not supported on testnet), a string message (not hexadecimal) to include.\n"
3085             "      \"preconvert\":\"false\", (bool,   optional) auto-convert to PBaaS currency at market price, this only works if the order is mined before block start of the chain\n"
3086             "      \"subtractfee\":\"bool\", (bool,   optional) if true, output must be of native or convertible reserve currency, and output will be reduced by share of fee\n"
3087             "    }, ... ]\n"
3088             "3. \"returntx\"                (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed and returned\n"
3089
3090             "\nResult:\n"
3091             "   \"txid\" : \"transactionid\" (string) The transaction id if (returntx) is false\n"
3092             "   \"hextx\" : \"hex\"         (string) The hexadecimal, serialized transaction if (returntx) is true\n"
3093
3094             "\nExamples:\n"
3095             + HelpExampleCli("sendcurrency", "\"*\" '[{\"currency\":\"btc\",\"address\":\"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\" ,\"amount\":500.0},...]'")
3096             + HelpExampleRpc("sendcurrency", "\"bob@\" '[{\"currency\":\"btc\", \"address\":\"alice@quad\", \"amount\":500.0},...]'")
3097         );
3098     }
3099
3100     std::string sourceAddress = uni_get_str(params[0]);
3101     CTxDestination sourceDest;
3102
3103     if (!(sourceAddress == "*" || (sourceDest = DecodeDestination(sourceAddress)).which() != COptCCParams::ADDRTYPE_INVALID))
3104     {
3105         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. First parameter must be source address/identity or \"*\". See help.");
3106     }
3107
3108     if (!params[1].isArray() || !params[1].size())
3109     {
3110         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. Second parameter must be array of outputs. See help.");
3111     }
3112
3113     bool returnTx = false;
3114     if (params.size() > 2)
3115     {
3116         returnTx = uni_get_bool(params[2]);
3117     }
3118
3119     bool isVerusActive = IsVerusActive();
3120     CCurrencyDefinition &thisChain = ConnectedChains.ThisChain();
3121     uint160 thisChainID = thisChain.GetID();
3122     bool toFractional = false;
3123
3124     std::vector<CRecipient> outputs;
3125
3126     const UniValue &uniOutputs = params[1];
3127
3128     uint32_t height = chainActive.Height();
3129
3130     try
3131     {
3132         for (int i = 0; i < uniOutputs.size(); i++)
3133         {        
3134             auto currencyStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "currency")));
3135             CAmount sourceAmount = AmountFromValue(find_value(uniOutputs[i], "amount"));
3136             auto convertToStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "convertto")));
3137             auto destStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "address")));
3138             auto refundToStr = TrimSpaces(uni_get_str(find_value(uniOutputs[i], "refundto")));
3139             auto memoStr = uni_get_str(find_value(uniOutputs[i], "memo"));
3140             bool preConvert = uni_get_bool(find_value(uniOutputs[i], "preconvert"));
3141             bool subtractFee = uni_get_bool(find_value(uniOutputs[i], "subtractfee"));
3142             bool mintNew = uni_get_bool(find_value(uniOutputs[i], "mintnew"));
3143
3144             if (currencyStr.size() ||
3145                 convertToStr.size() ||
3146                 refundToStr.size() ||
3147                 memoStr.size() ||
3148                 preConvert ||
3149                 mintNew)
3150             {
3151                 CheckPBaaSAPIsValid();
3152             }
3153
3154             LOCK2(cs_main, mempool.cs);
3155
3156             CCurrencyDefinition sourceCurrencyDef;
3157             uint160 sourceCurrencyID;
3158             if (currencyStr != "")
3159             {
3160                 sourceCurrencyID = ValidateCurrencyName(currencyStr, &sourceCurrencyDef);
3161                 if (sourceCurrencyID.IsNull())
3162                 {
3163                     throw JSONRPCError(RPC_INVALID_PARAMETER, "If source currency is specified, it must be valid.");
3164                 }
3165             }
3166             else
3167             {
3168                 sourceCurrencyDef = thisChain;
3169                 sourceCurrencyID = sourceCurrencyDef.GetID();
3170                 currencyStr = thisChain.name;
3171             }
3172
3173             CCurrencyDefinition convertToCurrencyDef;
3174             uint160 convertToCurrencyID;
3175             if (convertToStr != "")
3176             {
3177                 convertToCurrencyID = ValidateCurrencyName(convertToStr, &convertToCurrencyDef);
3178                 if (convertToCurrencyID.IsNull())
3179                 {
3180                     throw JSONRPCError(RPC_INVALID_PARAMETER, "If currency conversion is requested, destination currency must be valid.");
3181                 }
3182             }
3183
3184             std::string systemDestStr;
3185             uint160 destSystemID = thisChainID;
3186             CCurrencyDefinition destSystemDef = thisChain;
3187             std::vector<std::string> subNames = ParseSubNames(destStr, systemDestStr, true);
3188             if (systemDestStr != "")
3189             {
3190                 destSystemID = ValidateCurrencyName(systemDestStr, &destSystemDef);
3191                 if (destSystemID.IsNull() || destSystemDef.IsToken() || destSystemDef.systemID != destSystemDef.GetID())
3192                 {
3193                     throw JSONRPCError(RPC_INVALID_PARAMETER, "If destination system is specified, destination system or chain must be valid.");
3194                 }
3195             }
3196
3197             if (mintNew && 
3198                 (!(sourceCurrencyDef.IsToken() &&
3199                    GetDestinationID(sourceDest) == sourceCurrencyID &&
3200                    sourceCurrencyDef.proofProtocol == sourceCurrencyDef.PROOF_CHAINID &&
3201                    destSystemID == thisChainID &&
3202                    !preConvert &&
3203                    convertToCurrencyID.IsNull())))
3204             {
3205                 // attempt to mint currency that isn't under the source ID's control
3206                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Only the ID of a mintable currency can mint such a currency. Minting cannot be combined with conversion.");
3207             }
3208
3209             CTxDestination destination = ValidateDestination(destStr);
3210             CTransferDestination transferDestination;
3211             if (destination.which() == COptCCParams::ADDRTYPE_INVALID)
3212             {
3213                 if (destSystemDef.options & destSystemDef.OPTION_GATEWAY)
3214                 {
3215                     std::vector<unsigned char> rawDestBytes;
3216                     for (int i = 0; i < subNames.size(); i++)
3217                     {
3218                         if (i)
3219                         {
3220                             rawDestBytes.push_back('.');
3221                         }
3222                         rawDestBytes.insert(rawDestBytes.end(), subNames[i].begin(), subNames[i].end());
3223                     }
3224                     if (!rawDestBytes.size())
3225                     {
3226                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Specified destination must be valid.");
3227                     }
3228                     transferDestination = CTransferDestination(CTransferDestination::DEST_RAW, rawDestBytes);
3229                 }
3230                 else
3231                 {
3232                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Specified destination must be valid.");
3233                 }
3234             }
3235
3236             CTxDestination refundDestination = DecodeDestination(refundToStr);
3237             if (refundDestination.which() == COptCCParams::ADDRTYPE_ID &&
3238                 GetDestinationID(refundDestination) != GetDestinationID(destination))
3239             {
3240                 if (!CIdentity::LookupIdentity(GetDestinationID(refundDestination)).IsValid())
3241                 {
3242                     throw JSONRPCError(RPC_INVALID_PARAMETER, "When refunding to an ID, the ID must be valid.");
3243                 }
3244             }
3245             else if (refundDestination.which() == COptCCParams::ADDRTYPE_INVALID)
3246             {
3247                 refundDestination = destination;
3248             }
3249
3250             if (memoStr.size() > 512)
3251             {
3252                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo, if specified, must be under 512 characters.");
3253             }
3254
3255             // make one output
3256             CRecipient oneOutput;
3257
3258             // send a reserve transfer preconvert
3259             uint32_t flags = CReserveTransfer::VALID;
3260             if (preConvert)
3261             {
3262                 flags |= CReserveTransfer::PRECONVERT;
3263             }
3264             if (!convertToCurrencyID.IsNull())
3265             {
3266                 flags |= CReserveTransfer::CONVERT;
3267             }
3268             if (mintNew)
3269             {
3270                 flags |= CReserveTransfer::MINT_CURRENCY;
3271                 convertToCurrencyID = sourceCurrencyID;
3272                 convertToCurrencyDef = sourceCurrencyDef;
3273             }
3274
3275             // are we a system/chain transfer with or without conversion?
3276             if (destSystemID != thisChainID)
3277             {
3278                 CCcontract_info CC;
3279                 CCcontract_info *cp;
3280                 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
3281                 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3282
3283                 if (preConvert)
3284                 {
3285                     if (convertToCurrencyID.IsNull())
3286                     {
3287                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot preconvert to unspecified or invalid currency.");
3288                     }
3289                     CCurrencyValueMap validConversionCurrencies = CCurrencyValueMap(convertToCurrencyDef.currencies, 
3290                                                                                     std::vector<CAmount>(convertToCurrencyDef.currencies.size()));
3291                     if (!validConversionCurrencies.valueMap.count(sourceCurrencyID))
3292                     {
3293                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ".");
3294                     }
3295                     if (convertToCurrencyDef.startBlock <= (height + 1))
3296                     {
3297                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Too late to convert " + sourceCurrencyDef.name + " to " + convertToStr + ", as pre-launch is over.");
3298                     }
3299
3300                     CReserveTransfer rt = CReserveTransfer(flags, 
3301                                                            sourceCurrencyID, 
3302                                                            sourceAmount,
3303                                                            0,
3304                                                            convertToCurrencyID,
3305                                                            DestinationToTransferDestination(destination));
3306                     rt.nFees = rt.CalculateTransferFee();
3307
3308                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3309                     std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(destSystemID)});
3310
3311                     oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + rt.CalculateTransferFee() : 0;
3312                     oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3313                 }
3314                 // only valid conversion for a cross-chain send is to the native currency of the chain
3315                 else if (convertToCurrencyID == destSystemID)
3316                 {
3317                     flags |= CReserveTransfer::CONVERT;
3318                     // native currency must be the currency we are converting to, and the source must be a reserve
3319                     auto reserveMap = convertToCurrencyDef.GetCurrenciesMap();
3320                     if (!reserveMap.count(sourceCurrencyID))
3321                     {
3322                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 3");
3323                     }
3324                     // converting from reserve to a fractional of that reserve
3325                     auto dest = DestinationToTransferDestination(destination);
3326                     auto fees = CReserveTransfer::CalculateTransferFee(dest);
3327                     CReserveTransfer rt = CReserveTransfer(flags,
3328                                                            sourceCurrencyID, 
3329                                                            sourceAmount, 
3330                                                            fees, 
3331                                                            convertToCurrencyID, 
3332                                                            dest);
3333
3334                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3335                     std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(destSystemID)});
3336
3337                     oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + fees : fees;
3338                     oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3339                 }
3340                 else if (convertToCurrencyID.IsNull())
3341                 {
3342                     auto dest = DestinationToTransferDestination(destination);
3343                     auto fees = CReserveTransfer::CalculateTransferFee(dest);
3344                     CReserveTransfer rt = CReserveTransfer(flags,
3345                                                            sourceCurrencyID, 
3346                                                            sourceAmount, 
3347                                                            fees, 
3348                                                            sourceCurrencyID, 
3349                                                            dest);
3350
3351                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3352                     std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(destSystemID)});
3353
3354                     oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + fees : fees;
3355                     oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3356                 }
3357             }
3358             // a currency conversion without transfer?
3359             else if (!convertToCurrencyID.IsNull())
3360             {
3361                 // if pre-converting to a token, it must be a non-reserve preconvert.
3362                 // if we pre-convert for a new chain launch, which is required for a reserve, it will not be as a token
3363                 if (convertToCurrencyDef.IsToken() && preConvert)
3364                 {
3365                     if (convertToCurrencyDef.startBlock <= (height + 1))
3366                     {
3367                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Too late to convert " + sourceCurrencyDef.name + " to " + convertToStr + ", as pre-launch is over.");
3368                     }
3369
3370                     CCurrencyValueMap validConversionCurrencies = CCurrencyValueMap(convertToCurrencyDef.currencies, 
3371                                                                                     std::vector<CAmount>(convertToCurrencyDef.currencies.size()));
3372                     if (!validConversionCurrencies.valueMap.count(sourceCurrencyID))
3373                     {
3374                         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 1");
3375                     }
3376
3377                     CCcontract_info CC;
3378                     CCcontract_info *cp;
3379                     cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
3380                     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3381
3382                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3383                     std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), 
3384                                                                                           CKeyID(convertToCurrencyID)});
3385
3386                     CReserveTransfer rt = CReserveTransfer(flags, 
3387                                                             sourceCurrencyID, 
3388                                                             sourceAmount,
3389                                                             0,
3390                                                             convertToCurrencyID,
3391                                                             DestinationToTransferDestination(destination));
3392                     rt.nFees = rt.CalculateTransferFee();
3393                     oneOutput.nAmount = (sourceCurrencyID == thisChainID) ? sourceAmount + rt.nFees : 0;
3394                     oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3395                 }
3396                 else if (!preConvert &&
3397                          (mintNew ||
3398                           (toFractional = convertToCurrencyDef.IsFractional() && convertToCurrencyDef.GetCurrenciesMap().count(sourceCurrencyID)) ||
3399                           (sourceCurrencyDef.IsFractional() && sourceCurrencyDef.GetCurrenciesMap().count(convertToCurrencyID))))
3400                 {
3401                     // the following cases end up here:
3402                     //   1. we are minting currency
3403                     //   2. we are converting from a fractional currency to its reserve or back
3404
3405                     CCcontract_info CC;
3406                     CCcontract_info *cp;
3407                     cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
3408                     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3409                     if (mintNew)
3410                     {
3411                         // we onnly allow minting of tokens right now
3412                         // TODO: support centralized minting of native AND fractional currency
3413                         // minting of fractional currency should emit coins without changing price by
3414                         // adjusting reserve ratio
3415                         if (!convertToCurrencyDef.IsToken() || convertToCurrencyDef.IsFractional())
3416                         {
3417                             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot mint native or fractional currency " + convertToCurrencyDef.name);
3418                         }
3419                         std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
3420                         std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), 
3421                                                                                               CKeyID(convertToCurrencyID)});
3422
3423                         CReserveTransfer rt = CReserveTransfer(flags, 
3424                                                                thisChainID, 
3425                                                                sourceAmount,
3426                                                                0,
3427                                                                convertToCurrencyID,
3428                                                                DestinationToTransferDestination(destination));
3429                         rt.nFees = rt.CalculateTransferFee();
3430                         oneOutput.nAmount = rt.CalculateTransferFee();
3431                         oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3432                     }
3433                     else
3434                     {
3435                         // we are only 
3436                         flags |= CReserveTransfer::CONVERT;
3437
3438                         // determine the currency that is the fractional currency, whether that is the source
3439                         // or destination
3440                         CCurrencyDefinition *pFractionalCurrency = &sourceCurrencyDef;
3441
3442                         // determine the reserve currency of the destination that we are relevant to,
3443                         // again, whether source or destination
3444                         CCurrencyDefinition *pReserveCurrency = &convertToCurrencyDef;
3445
3446                         // is our destination currency, the conversion destination?
3447                         if (toFractional)
3448                         {
3449                             pReserveCurrency = pFractionalCurrency;
3450                             pFractionalCurrency = &convertToCurrencyDef;
3451                         }
3452
3453                         if (pFractionalCurrency->startBlock > height)
3454                         {
3455                             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + " except through preconvert before the startblock has passed.");
3456                         }
3457
3458                         auto reserveMap = pFractionalCurrency->GetCurrenciesMap();
3459                         auto reserveIndexIt = reserveMap.find(pReserveCurrency->GetID());
3460                         if (reserveIndexIt == reserveMap.end())
3461                         {
3462                             throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". Must have reserve<->fractional relationship.");
3463                         }
3464                         int reserveIndex = reserveIndexIt->second;
3465
3466                         // converting from reserve to a fractional of that reserve
3467                         auto dest = DestinationToTransferDestination(destination);
3468                         auto fees = CReserveTransfer::CalculateTransferFee(dest);
3469
3470                         // since this is conversion to a reserve token, which must be fungible with Verus, we will take the fee from
3471                         // source currency, in an amount we calculate to be equivalent to the expected fee amount in Verus + some buffer,
3472                         // based on the current price, even if that price must be first converted from reserve to token value, then
3473                         // back to Verus
3474                         CChainNotarizationData cnd;
3475                         if (!GetNotarizationData(pFractionalCurrency->GetID(), EVAL_ACCEPTEDNOTARIZATION, cnd))
3476                         {
3477                             throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to get reserve currency data for " + pFractionalCurrency->name + ".");
3478                         }
3479
3480                         CReserveTransfer rt = CReserveTransfer(flags,
3481                                                                sourceCurrencyID, 
3482                                                                sourceAmount, 
3483                                                                fees, 
3484                                                                convertToCurrencyID, 
3485                                                                dest);
3486
3487                         std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), refundDestination});
3488                         std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(thisChain.GetConditionID(EVAL_RESERVE_TRANSFER)), CKeyID(pFractionalCurrency->GetID())});
3489
3490                         oneOutput.nAmount = sourceCurrencyID == thisChainID ? sourceAmount + fees : 0;
3491                         oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests);
3492
3493                         /*
3494                         // TODO: must be integrated into the functions above
3495                         if (sourceCurrencyDef.IsFractional())
3496                         {
3497                             // native currency must be the currency we are converting to, and the source must be a fractional with that as a reserve
3498                             auto reserveMap = sourceCurrencyDef.GetCurrenciesMap();
3499                             if (!reserveMap.count(convertToCurrencyID))
3500                             {
3501                                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 3");
3502                             }
3503                             // converting from reserve to a fractional of that reserve
3504
3505                             CReserveExchange re(flags, convertToCurrencyID, sourceAmount);
3506                             std::vector<CTxDestination> dests = std::vector<CTxDestination>({destination});
3507                             oneOutput.nAmount = 0;
3508                             oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CReserveExchange>(EVAL_RESERVE_EXCHANGE, dests, 1, &re));
3509
3510                         }
3511                         */
3512                     }
3513                 }
3514                 else
3515                 {
3516                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + sourceCurrencyDef.name + " to " + convertToStr + ". 4");
3517                 }
3518             }
3519             // or a normal native or reserve output?
3520             else
3521             {
3522                 if (sourceCurrencyID == thisChainID)
3523                 {
3524                     oneOutput.nAmount = sourceAmount;
3525                     oneOutput.scriptPubKey = GetScriptForDestination(destination);
3526                 }
3527                 else
3528                 {
3529                     oneOutput.nAmount = 0;
3530
3531                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({destination});
3532                     CTokenOutput to(sourceCurrencyID, sourceAmount);
3533
3534                     oneOutput.scriptPubKey = MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &to));
3535                 }
3536             }
3537             if (!oneOutput.scriptPubKey.size())
3538             {
3539                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Failure to make currency output");
3540             }
3541             oneOutput.fSubtractFeeFromAmount = subtractFee;
3542             outputs.push_back(oneOutput);
3543         }
3544     }
3545     catch(const std::exception& e)
3546     {
3547         std::cerr << e.what() << '\n';
3548         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters.");
3549     }
3550
3551     // now figure out if we are pulling from a single address or all addresses
3552     // make, sign, and send or return the transaction
3553     CTxDestination *pSourceDest = (sourceDest.which() == COptCCParams::ADDRTYPE_INVALID) ? nullptr : &sourceDest;
3554
3555     CWalletTx wtx;
3556     CReserveKey reserveKey(pwalletMain);
3557     CAmount nFeesRet;
3558     int nChangePosRet, nChangeOutputs;
3559     std::string failReason;
3560     int errorRet;
3561
3562     UniValue ret;
3563     if ((errorRet = 
3564          pwalletMain->CreateReserveTransaction(outputs, wtx, reserveKey, nFeesRet, nChangePosRet, nChangeOutputs, failReason, NULL, pSourceDest, true))
3565           == CWallet::RPC_OK)
3566     {
3567         // now, we either return or commit the transaction
3568         if (returnTx)
3569         {
3570             // keep the change key
3571             reserveKey.KeepKey();
3572             ret = UniValue(EncodeHexTx(wtx));
3573         }
3574         else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
3575         {
3576             UniValue jsonTx(UniValue::VOBJ);
3577             TxToUniv(wtx, uint256(), jsonTx);
3578             LogPrintf("%s\n", jsonTx.write(1,2).c_str());
3579             throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit reserve transaction " + wtx.GetHash().GetHex());
3580         }
3581         else
3582         {
3583             ret = UniValue(wtx.GetHash().GetHex());
3584         }
3585     }
3586     else
3587     {
3588         throw JSONRPCError(errorRet, failReason);
3589     }
3590     return ret;
3591 }
3592
3593 bool GetLastImportIn(uint160 chainID, CTransaction &lastImportTx)
3594 {
3595     // look for unspent chain transfer outputs for all chains
3596     CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
3597
3598     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
3599
3600     LOCK(cs_main);
3601
3602     if (!GetAddressUnspent(keyID, 1, unspentOutputs))
3603     {
3604         return false;
3605     }
3606
3607     for (auto output : unspentOutputs)
3608     {
3609         // find the first one that is either in block 1, part of a chain definition transaction, or spends a prior
3610         // import output in input 0, which cannot be done unless the output is rooted in a valid import thread. anyone can try to send
3611         // coins to this address without being part of the chain, but they will be wasted and unspendable.
3612         COptCCParams p;
3613         if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
3614         {
3615             // we actually don't care what is in the transaction here, only that it is a valid cross chain import tx
3616             // that is either the first in a chain or spends a valid cross chain import output
3617             CCrossChainImport cci(p.vData[0]);
3618             CTransaction lastTx;
3619             uint256 blkHash;
3620             if (cci.IsValid() && myGetTransaction(output.first.txhash, lastTx, blkHash))
3621             {
3622                 if (output.second.blockHeight == 1 && lastTx.IsCoinBase())
3623                 {
3624                     lastImportTx = lastTx;
3625                     return true;
3626                 }
3627                 else
3628                 {
3629                     if (IsVerusActive())
3630                     {
3631                         // if this is the Verus chain, then we need to either be part of a chain definition transaction
3632                         // or spend a valid import output
3633                         std::vector<CCurrencyDefinition> definedChains = CCurrencyDefinition::GetCurrencyDefinitions(lastTx);
3634                         if (definedChains.size() == 1 && definedChains[0].IsValid())
3635                         {
3636                             lastImportTx = lastTx;
3637                             return true;
3638                         }
3639                         // if we get here, we must spend a valid import output
3640                         CTransaction inputTx;
3641                         uint256 inputBlkHash;
3642                         if (lastTx.vin.size() && myGetTransaction(lastTx.vin[0].prevout.hash, inputTx, inputBlkHash) && CCrossChainImport(lastTx).IsValid())
3643                         {
3644                             lastImportTx = lastTx;
3645                             return true;
3646                         }
3647                     }
3648                 }
3649             }
3650         }
3651     }
3652     return false;
3653 }
3654
3655 UniValue getlastimportin(const UniValue& params, bool fHelp)
3656 {
3657     if (fHelp || params.size() != 1)
3658     {
3659         throw runtime_error(
3660             "getlastimportin \"fromname\"\n"
3661             "\nThis returns the last import transaction from the chain specified and a blank transaction template to use when making new\n"
3662             "\nimport transactions. Since the typical use for this call is to make new import transactions from the other chain that will be then\n"
3663             "\nbroadcast to this chain, we include the template by default.\n"
3664
3665             "\nArguments\n"
3666             "   \"fromname\"                (string, required) name of the chain to get the last import transaction in from\n"
3667
3668             "\nResult:\n"
3669             "   {\n"
3670             "       \"lastimporttransaction\": \"hex\"      Hex encoded serialized import transaction\n"
3671             "       \"lastconfirmednotarization\" : \"hex\" Hex encoded last confirmed notarization transaction\n"
3672             "       \"importtxtemplate\": \"hex\"           Hex encoded import template for new import transactions\n"
3673             "       \"nativeimportavailable\": \"amount\"   Total amount of native import currency available to import as native\n"
3674             "       \"tokenimportavailable\": \"array\"     ([{\"currencyid\":amount},..], required) tokens available to import, if controlled by this chain\n"
3675             "   }\n"
3676
3677             "\nExamples:\n"
3678             + HelpExampleCli("getlastimportin", "jsondefinition")
3679             + HelpExampleRpc("getlastimportin", "jsondefinition")
3680         );
3681     }
3682     // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3683     // import transactions using the importtxtemplate as a template
3684     CheckPBaaSAPIsValid();
3685
3686     uint160 chainID = GetChainIDFromParam(params[0]);
3687
3688     if (chainID.IsNull())
3689     {
3690         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
3691     }
3692
3693     CTransaction lastImportTx, lastConfirmedTx;
3694
3695     CChainNotarizationData cnd;
3696     std::vector<std::pair<CTransaction, uint256>> txesOut;
3697
3698     {
3699         LOCK2(cs_main, mempool.cs);
3700
3701         if (!GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_EARNEDNOTARIZATION, cnd, &txesOut))
3702         {
3703             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No chain notarizations for " + uni_get_str(params[0]) + " found");
3704         }
3705     }
3706
3707     UniValue ret(UniValue::VNULL);
3708
3709     if (cnd.IsConfirmed())
3710     {
3711         lastConfirmedTx = txesOut[cnd.lastConfirmed].first;
3712         if (GetLastImportIn(chainID, lastImportTx))
3713         {
3714             ret = UniValue(UniValue::VOBJ);
3715             CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height());
3716             // find the import output
3717             COptCCParams p;
3718             int importIdx;
3719             for (importIdx = 0; importIdx < lastImportTx.vout.size(); importIdx++)
3720             {
3721                 if (lastImportTx.vout[importIdx].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
3722                 {
3723                     break;
3724                 }
3725             }
3726             if (importIdx >= lastImportTx.vout.size())
3727             {
3728                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid lastImport transaction");
3729             }
3730
3731             txTemplate.vin.push_back(CTxIn(lastImportTx.GetHash(), importIdx, CScript()));
3732
3733             // if Verus is active, our template import will gather all RESERVE_DEPOSITS that it can to spend into the
3734             // import thread
3735             if (IsVerusActive())
3736             {
3737                 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> reserveDeposits;
3738
3739                 LOCK2(cs_main, mempool.cs);
3740
3741                 if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(chainID, EVAL_RESERVE_DEPOSIT)), 1, reserveDeposits))
3742                 {
3743                     for (auto deposit : reserveDeposits)
3744                     {
3745                         COptCCParams p;
3746                         if (deposit.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_RESERVE_DEPOSIT)
3747                         {
3748                             txTemplate.vin.push_back(CTxIn(deposit.first.txhash, deposit.first.index, CScript()));
3749                         }
3750                     }
3751                 }
3752             }
3753
3754             CCoinsViewCache view(pcoinsTip);
3755             int64_t dummyInterest;
3756             CAmount totalImportAvailable = view.GetValueIn(cnd.vtx[cnd.lastConfirmed].second.notarizationHeight, &dummyInterest, txTemplate);
3757             CCurrencyValueMap reserveImportAvailable = view.GetReserveValueIn(cnd.vtx[cnd.lastConfirmed].second.notarizationHeight, txTemplate);
3758
3759             ret.push_back(Pair("lastimporttransaction", EncodeHexTx(lastImportTx)));
3760             ret.push_back(Pair("lastconfirmednotarization", EncodeHexTx(lastConfirmedTx)));
3761             ret.push_back(Pair("importtxtemplate", EncodeHexTx(txTemplate)));
3762             ret.push_back(Pair("nativeimportavailable", totalImportAvailable));
3763             ret.push_back(Pair("tokenimportavailable", reserveImportAvailable.ToUniValue()));
3764         }
3765     }
3766     return ret;
3767 }
3768
3769 UniValue getlatestimportsout(const UniValue& params, bool fHelp)
3770 {
3771     if (fHelp || params.size() != 1)
3772     {
3773         throw runtime_error(
3774             "getlatestimportsout \"name\" \"lastimporttransaction\" \"importtxtemplate\"\n"
3775             "\nThis creates and returns all new imports for the specified chain that are exported from this blockchain.\n"
3776
3777             "\nArguments\n"
3778             "{\n"
3779             "   \"name\" : \"string\"                   (string, required) name of the chain to get the export transactions for\n"
3780             "   \"lastimporttransaction\" : \"hex\"     (hex,    required) the last import transaction to follow, return list starts with import that spends this\n"
3781             "   \"lastconfirmednotarization\" : \"hex\" (hex,    required) the last confirmed notarization transaction\n"
3782             "   \"importtxtemplate\" : \"hex\"          (hex,    required) template transaction to use, so we create a transaction compatible with the other chain\n"
3783             "   \"nativeimportavailable\": \"amount\"   (number, required) Total amount of native import currency available to import as native\n"
3784             "   \"tokenimportavailable\": \"array\"     ([{\"currencyid\":amount},..], required) tokens available to import, if controlled by this chain\n"
3785             "}\n"
3786
3787             "\nResult:\n"
3788             "UniValue array of hex transactions"
3789
3790             "\nExamples:\n"
3791             + HelpExampleCli("getlatestimportsout", "jsonargs")
3792             + HelpExampleRpc("getlatestimportsout", "jsonargs")
3793         );
3794     }
3795
3796     UniValue ret(UniValue::VARR);
3797
3798     // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3799     // import transactions using the importtxtemplate as a template
3800     CheckPBaaSAPIsValid();
3801
3802     bool isVerusActive = IsVerusActive();
3803
3804     uint160 chainID = GetChainIDFromParam(find_value(params[0], "name"));
3805
3806     if (chainID.IsNull())
3807     {
3808         return ret;
3809     }
3810
3811     // starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
3812     // import transactions using the importtxtemplate as a template
3813     CCurrencyDefinition chainDef;
3814
3815     if (!GetCurrencyDefinition(chainID, chainDef))
3816     {
3817         return ret;
3818     }
3819
3820     std::string lastImportHex = uni_get_str(find_value(params[0], "lastimporttx"));
3821     CTransaction lastImportTx;
3822
3823     std::string templateTxHex = uni_get_str(find_value(params[0], "importtxtemplate"));
3824     CTransaction templateTx;
3825
3826     std::string lastConfirmedTxHex = uni_get_str(find_value(params[0], "lastconfirmednotarization"));
3827     CTransaction lastConfirmedNotarization;
3828
3829     CAmount nativeImportAvailable = uni_get_int64(find_value(params[0], "nativeimportavailable"));
3830     CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
3831
3832     std::vector<CBaseChainObject *> chainObjs;
3833     std::vector<CCurrencyDefinition> curDefs = CCurrencyDefinition::GetCurrencyDefinitions(lastImportTx);
3834     if (!(DecodeHexTx(lastImportTx, lastImportHex) && 
3835           DecodeHexTx(templateTx, templateTxHex) &&
3836           DecodeHexTx(lastConfirmedNotarization, lastConfirmedTxHex) &&
3837           CCrossChainImport(lastImportTx).IsValid() &&
3838           (curDefs.size() && curDefs[0].IsValid() ||
3839           (lastImportTx.vout.back().scriptPubKey.IsOpReturn() &&
3840           (chainObjs = RetrieveOpRetArray(lastImportTx.vout.back().scriptPubKey)).size() == 1 &&
3841           chainObjs[0]->objectType == CHAINOBJ_CROSSCHAINPROOF))))
3842     {
3843         DeleteOpRetObjects(chainObjs);
3844         return ret;
3845     }
3846
3847     //UniValue txUniv(UniValue::VOBJ);
3848     //TxToUniv(lastImportTx, uint256(), txUniv);
3849     //printf("lastImportTx%s\n", txUniv.write(1,2).c_str());
3850
3851     DeleteOpRetObjects(chainObjs);
3852
3853     std::vector<CTransaction> newImports;
3854     if (!ConnectedChains.CreateLatestImports(chainDef, lastImportTx, templateTx, lastConfirmedNotarization, tokenImportAvailable, nativeImportAvailable, newImports))
3855     {
3856         return ret;
3857     }
3858
3859     for (auto import : newImports)
3860     {
3861         ret.push_back(EncodeHexTx(import));
3862     }
3863     return ret;
3864 }
3865
3866 // refunds a failed launch if it is eligible and not already refunded. if it fails to refund, it returns false
3867 // and a string with the reason for failure
3868 bool RefundFailedLaunch(uint160 currencyID, CTransaction &lastImportTx, std::vector<CTransaction> &newRefunds, std::string &errorReason)
3869 {
3870     int32_t defHeight = 0;
3871     CCurrencyDefinition chainDef;
3872     CTransaction chainDefTx;
3873     uint160 thisChainID = ConnectedChains.ThisChain().GetID();
3874
3875     LOCK2(cs_main, mempool.cs);
3876
3877     if (!GetCurrencyDefinition(currencyID, chainDef, &defHeight) || currencyID == thisChainID)
3878     {
3879         errorReason = "currency-not-found-or-ineligible";
3880         return false;
3881     }
3882
3883     if (!GetLastImportIn(chainDef.IsToken() ? chainDef.GetID() : chainDef.systemID, lastImportTx))
3884     {
3885         errorReason = "no-import-thread-found";
3886         return false;
3887     }
3888
3889     std::vector<CBaseChainObject *> chainObjs;
3890     CPartialTransactionProof lastExportTxProof;
3891
3892     // either a fully valid import with an export or the first import either in block 1 or chain definition
3893     // on the Verus chain
3894     std::vector<CCurrencyDefinition> curDefs = CCurrencyDefinition::GetCurrencyDefinitions(lastImportTx);
3895     if (!(lastImportTx.vout.back().scriptPubKey.IsOpReturn() &&
3896           (chainObjs = RetrieveOpRetArray(lastImportTx.vout.back().scriptPubKey)).size() >= 1 &&
3897           chainObjs[0]->objectType == CHAINOBJ_TRANSACTION_PROOF &&
3898           !(lastExportTxProof = ((CChainObject<CPartialTransactionProof> *)(chainObjs[0]))->object).txProof.proofSequence.size() < 3) &&
3899           !(chainObjs.size() == 0 && curDefs.size() && curDefs[0].IsValid()))
3900     {
3901         DeleteOpRetObjects(chainObjs);
3902         errorReason = "invalid-import-thread-found";
3903         return false;
3904     }
3905
3906     DeleteOpRetObjects(chainObjs);
3907
3908     bool isVerusActive = IsVerusActive();
3909
3910     uint256 lastExportHash;
3911     CTransaction lastExportTx;
3912
3913     uint32_t blkHeight = defHeight;
3914     bool found = false;
3915
3916     if (lastExportTxProof.txProof.proofSequence.size())
3917     {
3918         lastExportHash = lastExportTxProof.TransactionHash();
3919         CTransaction tx;
3920         uint256 blkHash;
3921         BlockMap::iterator blkMapIt;
3922         if (myGetTransaction(lastExportHash, tx, blkHash) &&
3923             (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() && 
3924             blkMapIt->second)
3925         {
3926             found = true;
3927             blkHeight = blkMapIt->second->GetHeight();
3928         }
3929     }
3930     else
3931     {
3932         // if we haven't processed any imports/refunds yet, setup to payout from the first export and continue
3933         CCrossChainExport ccx(lastImportTx);
3934         if (ccx.IsValid())
3935         {
3936             found = true;
3937             lastExportTx = lastImportTx;
3938             lastExportHash = lastImportTx.GetHash();
3939         }
3940     }
3941
3942     if (!found)
3943     {
3944         LogPrintf("%s: No export thread found\n", __func__);
3945         //printf("%s: No export thread found\n", __func__);
3946         return false;
3947     }
3948
3949     // we need to get the last import transaction and handle refunds like exports without conversion or
3950     // launch fees, so we can make incremental progress if necessary and know when there are none left
3951     int32_t nHeight = chainActive.Height();
3952
3953     // make sure the chain is qualified for a refund
3954     CCurrencyValueMap minPreMap, preConvertedMap;
3955     CCoinbaseCurrencyState currencyState = ConnectedChains.GetCurrencyState(chainDef.GetID(), chainDef.startBlock - 1);
3956     if (!(chainDef.minPreconvert.size() &&
3957         (minPreMap = CCurrencyValueMap(chainDef.currencies, chainDef.minPreconvert)) > preConvertedMap &&
3958         chainDef.startBlock < nHeight && 
3959         (preConvertedMap = CCurrencyValueMap(chainDef.currencies, currencyState.reserveIn)) < minPreMap))
3960     {
3961         errorReason = "chain-ineligible";
3962         return false;
3963     }
3964     else
3965     {
3966         // convert exports & conversions intended for the failed currency into imports back to this chain that spend from the import thread
3967         // all reservetransfers are refunded and charged 1/2 of an export fee
3968         uint160 chainID = chainDef.systemID;
3969
3970         std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
3971
3972         // get all exports for the chain
3973         CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
3974
3975         CBlockIndex *pIndex;
3976
3977         // get all export transactions that were posted to the chain, since none can be sent
3978         // TODO:PBAAS - all sends to a failed chain after start block should fail. this may not want to
3979         // refund all exports, in case we decide to reuse chain names
3980         if (GetAddressIndex(keyID, 1, addressIndex, defHeight, nHeight))
3981         {
3982             // get all exports from first to last, and make sure they spend from the export thread consecutively
3983             bool found = false;
3984             uint256 lastHash = lastExportHash;
3985
3986             // indexed by input hash
3987             std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
3988
3989             // validate, order, and relate them with their inputs
3990             for (auto utxo : addressIndex)
3991             {
3992                 // if current tx spends lastHash, then we have our next valid transaction to create an import with
3993                 CTransaction tx, inputtx;
3994                 uint256 blkHash1, blkHash2;
3995                 BlockMap::iterator blkIt;
3996                 CCrossChainExport ccx;
3997                 COptCCParams p;
3998                 if (!utxo.first.spending &&
3999                     !(utxo.first.txhash == lastExportHash) &&
4000                     myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
4001                     (ccx = CCrossChainExport(tx)).IsValid() &&
4002                     ccx.numInputs &&
4003                     (!tx.IsCoinBase() && 
4004                     tx.vin.size() && 
4005                     myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)) &&
4006                     inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && 
4007                     p.IsValid() && 
4008                     p.evalCode == EVAL_CROSSCHAIN_EXPORT)
4009                 {
4010                     validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
4011                 }
4012             }
4013
4014             CTransaction lastImport(lastImportTx);
4015             CCrossChainImport lastCCI;
4016
4017             for (auto aixIt = validExports.find(lastExportHash); 
4018                 aixIt != validExports.end(); 
4019                 aixIt = validExports.find(lastExportHash))
4020             {
4021                 // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
4022                 // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
4023                 // 2. Creates reserve outputs for unconverted imports
4024                 // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
4025                 // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
4026                 // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
4027                 // then signs the transaction considers it the latest import and the new export the latest export and
4028                 // loops until there are no more confirmed, consecutive, valid export transactions to export
4029
4030                 // aixIt has an input from the export thread of last transaction, an optional deposit to the reserve, and an opret of all outputs + 1 fee
4031                 assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
4032
4033                 int32_t lastImportPrevoutN = 0, lastExportPrevoutN = 0;
4034                 lastCCI = CCrossChainImport();
4035                 COptCCParams p;
4036
4037                 for (int i = 0; i < lastImport.vout.size(); i++)
4038                 {
4039                     if (lastImport.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size() && (lastCCI = CCrossChainImport(p.vData[0])).IsValid())
4040                     {
4041                         lastImportPrevoutN = i;
4042                         break;
4043                     }
4044                 }
4045
4046                 int32_t ccxOutputNum = 0;
4047                 CCrossChainExport ccx(aixIt->second.second, &ccxOutputNum);
4048                 if (!lastCCI.IsValid() || !ccx.IsValid())
4049                 {
4050                     LogPrintf("%s: POSSIBLE CORRUPTION bad import/export data in transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
4051                     printf("%s: POSSIBLE CORRUPTION bad import/export data transaction %s (import) or %s (export)\n", __func__, lastImport.GetHash().GetHex().c_str(), aixIt->first.GetHex().c_str());
4052                     return false;
4053                 }
4054
4055                 CMutableTransaction newImportTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
4056                 newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), lastImportPrevoutN));
4057
4058                 // if no prepared input, make one
4059                 newImportTx.vout.clear();
4060                 newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
4061
4062                 // we need to recalculate fees, as there will be no conversions
4063
4064                 CReserveTransactionDescriptor rtxd;
4065
4066                 std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
4067
4068                 // loop through and recalculate every transfer to charge a normal transfer fee and be a normal output
4069                 // recalculate the fee output amount to be based on the new export fee calculation after canceling conversions
4070                 ccx.totalFees = CCurrencyValueMap();
4071
4072                 CCurrencyValueMap feeMap;
4073                 bool inFeeOutputs = false;
4074
4075                 for (auto &objPtr : exportOutputs)
4076                 {
4077                     if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
4078                     {
4079                         CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
4080
4081                         // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
4082                         if (rt.destination.type == CTransferDestination::DEST_FULLID)
4083                         {
4084                             CIdentity(rt.destination.destination);
4085                             rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
4086                         }
4087
4088                         // turn it into a normal transfer, which will create an unconverted output
4089                         rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
4090
4091                         if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
4092                         {
4093                             rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
4094                             rt.nValue = 0;
4095                         }
4096                         rt.destCurrencyID = rt.currencyID;
4097
4098                         // once we get to fees, we should have processed all non-fee outputs and be able to accurately calculate fees
4099                         if (inFeeOutputs || rt.flags & CReserveTransfer::FEE_OUTPUT)
4100                         {
4101                             // will be recalculated
4102                             if (!inFeeOutputs)
4103                             {
4104                                 inFeeOutputs = true;
4105                                 ccx.totalFees = feeMap;
4106                                 //printf("total fees:\n%s\n", feeMap.ToUniValue().write(1,2).c_str());
4107                             }
4108                             rt.nFees = 0;
4109                             rt.nValue = ccx.CalculateExportFee().valueMap[rt.currencyID];
4110                             //printf("one fee out:\n%s\n", rt.ToUniValue().write(1,2).c_str());
4111                         }
4112                         else
4113                         {
4114                             feeMap.valueMap[rt.currencyID] += rt.nFees;
4115                         }
4116                     }
4117                 }
4118
4119                 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(),
4120                                                           chainDef, 
4121                                                           currencyState,
4122                                                           exportOutputs, 
4123                                                           newImportTx.vout))
4124                 {
4125                     LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
4126                     printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
4127                     // free the memory
4128                     DeleteOpRetObjects(exportOutputs);
4129                     return false;
4130                 }
4131
4132                 // free the memory
4133                 DeleteOpRetObjects(exportOutputs);
4134
4135                 // emit a crosschain import output as summary
4136                 CCcontract_info CC;
4137                 CCcontract_info *cpcp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
4138                 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
4139
4140                 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(ccx.systemID, EVAL_CROSSCHAIN_IMPORT)))});
4141                 std::vector<CTxDestination> dests;
4142
4143                 if (ConnectedChains.ThisChain().proofProtocol == CCurrencyDefinition::PROOF_PBAASMMR ||
4144                     ConnectedChains.ThisChain().proofProtocol == CCurrencyDefinition::PROOF_CHAINID)
4145                 {
4146                     dests = std::vector<CTxDestination>({pk});
4147                 }
4148                 else
4149                 {
4150                     LogPrintf("%s: ERROR - invalid proof protocol\n", __func__);
4151                     printf("%s: ERROR - invalid proof protocol\n", __func__);
4152                     return false;
4153                 }
4154
4155                 CCrossChainImport cci = CCrossChainImport(ccx.systemID, CCurrencyValueMap(), CCurrencyValueMap());
4156
4157                 newImportTx.vout[0] = CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests));
4158
4159                 // now add unspent reserve deposit that came from this export as input to return it back to senders, less transfer fees
4160                 // based on consensus rules, this should provide the necessary funds by definition
4161                 uint256 depositTxId = aixIt->second.second.GetHash();
4162                 for (int i = 0; i < aixIt->second.second.vout.size(); i++)
4163                 {
4164                     COptCCParams p;
4165                     if (aixIt->second.second.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_RESERVE_DEPOSIT)
4166                     {
4167                         newImportTx.vin.push_back(CTxIn(depositTxId, i, CScript()));
4168                     }
4169                 }
4170
4171                 // add a proof of the export transaction at the notarization height
4172                 CBlock block;
4173                 if (!ReadBlockFromDisk(block, chainActive[aixIt->second.first.blockHeight], Params().GetConsensus(), false))
4174                 {
4175                     LogPrintf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4176                     printf("%s: POSSIBLE CORRUPTION cannot read block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4177                     return false;
4178                 }
4179
4180                 // prove ccx input 0, export output, and opret
4181                 std::vector<std::pair<int16_t, int16_t>> parts({{CTransactionHeader::TX_HEADER, (int16_t)0},
4182                                                                 {CTransactionHeader::TX_PREVOUTSEQ, (int16_t)0},
4183                                                                 {CTransactionHeader::TX_OUTPUT, ccxOutputNum},
4184                                                                 {CTransactionHeader::TX_OUTPUT, (int16_t)(aixIt->second.second.vout.size() - 1)}});
4185
4186                 // prove our transaction up to the MMR root of the last transaction
4187                 CPartialTransactionProof exportProof = block.GetPartialTransactionProof(aixIt->second.second, aixIt->second.first.txindex, parts);
4188                 if (!exportProof.components.size())
4189                 {
4190                     LogPrintf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4191                     printf("%s: could not create partial transaction proof in block %s\n", __func__, chainActive[aixIt->second.first.blockHeight]->GetBlockHash().GetHex().c_str());
4192                     return false;
4193                 }
4194                 exportProof.txProof << block.MMRProofBridge();
4195
4196                 // TODO: don't include chain MMR proof for exports from the same chain
4197                 ChainMerkleMountainView(chainActive.GetMMR(), nHeight).GetProof(exportProof.txProof, aixIt->second.first.blockHeight);
4198
4199                 CChainObject<CPartialTransactionProof> exportXProof(CHAINOBJ_TRANSACTION_PROOF, exportProof);
4200
4201                 // add the opret with the transaction and proof
4202                 newImportTx.vout.push_back(CTxOut(0, StoreOpRetArray(std::vector<CBaseChainObject *>({&exportXProof}))));
4203
4204                 // we now have an Import transaction for the chainID chain, it is the latest, and the export we used is now the latest as well
4205                 // work our way forward
4206                 newRefunds.push_back(newImportTx);
4207                 lastImport = newImportTx;
4208                 lastExportHash = aixIt->second.first.txhash;
4209             }
4210         }
4211         return true;
4212     }
4213 }
4214
4215 UniValue refundfailedlaunch(const UniValue& params, bool fHelp)
4216 {
4217     if (fHelp || params.size() != 1)
4218     {
4219         throw runtime_error(
4220             "refundfailedlaunch \"currencyid\"\n"
4221             "\nRefunds any funds sent to the chain if they are eligible for refund.\n"
4222             "This attempts to refund all transactions for all contributors.\n"
4223
4224             "\nArguments\n"
4225             "\"currencyid\"         (iaddress or full chain name, required)   the chain to refund contributions to\n"
4226
4227             "\nResult:\n"
4228
4229             "\nExamples:\n"
4230             + HelpExampleCli("refundfailedlaunch", "\"currencyid\"")
4231             + HelpExampleRpc("refundfailedlaunch", "\"currencyid\"")
4232         );
4233     }
4234     CheckPBaaSAPIsValid();
4235
4236     uint160 chainID;
4237
4238     chainID = GetChainIDFromParam(params[0]);
4239     if (chainID.IsNull())
4240     {
4241         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid PBaaS name or currencyid");
4242     }
4243
4244     if (chainID == ConnectedChains.ThisChain().GetID() || chainID == ConnectedChains.NotaryChain().chainDefinition.GetID())
4245     {
4246         throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot refund the specified chain");
4247     }
4248
4249     CTransaction lastImportTx;
4250     std::vector<CTransaction> refundTxes;
4251     std::string failReason;
4252
4253     if (!RefundFailedLaunch(chainID, lastImportTx, refundTxes, failReason))
4254     {
4255         throw JSONRPCError(RPC_INVALID_REQUEST, failReason);
4256     }
4257
4258     uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
4259
4260     UniValue ret(UniValue::VARR);
4261
4262     CCoinsViewCache view(pcoinsTip);
4263
4264     // sign and commit the transactions
4265     for (auto tx : refundTxes)
4266     {
4267         LOCK2(cs_main, mempool.cs);
4268
4269         CMutableTransaction newTx(tx);
4270
4271         // sign the transaction and submit
4272         bool signSuccess;
4273         for (int i = 0; i < tx.vin.size(); i++)
4274         {
4275             SignatureData sigdata;
4276             CAmount value;
4277             CScript outputScript;
4278
4279             if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
4280             {
4281                 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
4282                 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
4283             }
4284             else
4285             {
4286                 CCoinsViewCache view(pcoinsTip);
4287                 CCoins coins;
4288                 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
4289                 {
4290                     fprintf(stderr,"refundfailedlaunch: cannot get input coins from tx: %s, output: %d\n", tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
4291                     LogPrintf("refundfailedlaunch: cannot get input coins from tx: %s, output: %d\n", tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
4292                     break;
4293                 }
4294                 value = coins.vout[tx.vin[i].prevout.n].nValue;
4295                 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
4296             }
4297
4298             signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
4299
4300             if (!signSuccess)
4301             {
4302                 fprintf(stderr,"refundfailedlaunch: failure to sign refund transaction\n");
4303                 LogPrintf("refundfailedlaunch: failure to sign refund transaction\n");
4304                 break;
4305             } else {
4306                 UpdateTransaction(newTx, i, sigdata);
4307             }
4308         }
4309
4310         if (signSuccess)
4311         {
4312             // push to local node and sync with wallets
4313             CValidationState state;
4314             bool fMissingInputs;
4315             CTransaction signedTx(newTx);
4316             if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
4317                 if (state.IsInvalid()) {
4318                     fprintf(stderr,"refundfailedlaunch: rejected by memory pool for %s\n", state.GetRejectReason().c_str());
4319                     LogPrintf("refundfailedlaunch: rejected by memory pool for %s\n", state.GetRejectReason().c_str());
4320                 } else {
4321                     if (fMissingInputs) {
4322                         fprintf(stderr,"refundfailedlaunch: missing inputs\n");
4323                         LogPrintf("refundfailedlaunch: missing inputs\n");
4324                     }
4325                     else
4326                     {
4327                         fprintf(stderr,"refundfailedlaunch: rejected by memory pool for\n");
4328                         LogPrintf("refundfailedlaunch: rejected by memory pool for\n");
4329                     }
4330                 }
4331                 break;
4332             }
4333             else
4334             {
4335                 RelayTransaction(signedTx);
4336                 ret.push_back(signedTx.GetHash().GetHex());
4337             }
4338         }
4339     }
4340     return ret;
4341 }
4342
4343 UniValue getinitialcurrencystate(const UniValue& params, bool fHelp)
4344 {
4345     if (fHelp || params.size() != 1)
4346     {
4347         throw runtime_error(
4348             "getinitialcurrencystate \"name\"\n"
4349             "\nReturns the total amount of preconversions that have been confirmed on the blockchain for the specified PBaaS chain.\n"
4350             "This should be used to get information about chains that are not this chain, but are being launched by it.\n"
4351
4352             "\nArguments\n"
4353             "   \"name\"                    (string, required) name or chain ID of the chain to get the export transactions for\n"
4354
4355             "\nResult:\n"
4356             "   [\n"
4357             "       {\n"
4358             "           \"flags\" : n,\n"
4359             "           \"initialratio\" : n,\n"
4360             "           \"initialsupply\" : n,\n"
4361             "           \"emitted\" : n,\n"
4362             "           \"supply\" : n,\n"
4363             "           \"reserve\" : n,\n"
4364             "           \"currentratio\" : n,\n"
4365             "       },\n"
4366             "   ]\n"
4367
4368             "\nExamples:\n"
4369             + HelpExampleCli("getinitialcurrencystate", "name")
4370             + HelpExampleRpc("getinitialcurrencystate", "name")
4371         );
4372     }
4373     CheckPBaaSAPIsValid();
4374
4375     uint160 chainID = GetChainIDFromParam(params[0]);
4376
4377     if (chainID.IsNull())
4378     {
4379         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
4380     }
4381
4382     LOCK(cs_main);
4383
4384     CCurrencyDefinition chainDef;
4385     int32_t definitionHeight;
4386     if (!GetCurrencyDefinition(chainID, chainDef, &definitionHeight))
4387     {
4388         throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain " + params[0].get_str() + " not found");
4389     }
4390     return ConnectedChains.GetCurrencyState(chainDef.GetID(), chainDef.startBlock - 1).ToUniValue();
4391 }
4392
4393 UniValue getcurrencystate(const UniValue& params, bool fHelp)
4394 {
4395     if (fHelp || params.size() > 1)
4396     {
4397         throw runtime_error(
4398             "getcurrencystate \"n\"\n"
4399             "\nReturns the total amount of preconversions that have been confirmed on the blockchain for the specified chain.\n"
4400
4401             "\nArguments\n"
4402             "   \"n\" or \"m,n\" or \"m,n,o\"         (int or string, optional) height or inclusive range with optional step at which to get the currency state\n"
4403             "                                                                   If not specified, the latest currency state and height is returned\n"
4404
4405             "\nResult:\n"
4406             "   [\n"
4407             "       {\n"
4408             "           \"height\": n,\n"
4409             "           \"blocktime\": n,\n"
4410             "           \"currencystate\": {\n"
4411             "               \"flags\" : n,\n"
4412             "               \"initialratio\" : n,\n"
4413             "               \"initialsupply\" : n,\n"
4414             "               \"emitted\" : n,\n"
4415             "               \"supply\" : n,\n"
4416             "               \"reserve\" : n,\n"
4417             "               \"currentratio\" : n,\n"
4418             "           \"}\n"
4419             "       },\n"
4420             "   ]\n"
4421
4422             "\nExamples:\n"
4423             + HelpExampleCli("getcurrencystate", "name")
4424             + HelpExampleRpc("getcurrencystate", "name")
4425         );
4426     }
4427     CheckPBaaSAPIsValid();
4428
4429     uint64_t lStart;
4430     uint64_t startEnd[3] = {0};
4431
4432     lStart = startEnd[1] = startEnd[0] = chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 1;
4433
4434     if (params.size() == 1)
4435     {
4436         if (uni_get_int(params[0], -1) == -1 && params[0].isStr())
4437         {
4438             Split(params[0].get_str(), startEnd, startEnd[0], 3);
4439         }
4440     }
4441
4442     if (startEnd[0] > startEnd[1])
4443     {
4444         startEnd[0] = startEnd[1];
4445     }
4446
4447     if (startEnd[1] > lStart)
4448     {
4449         startEnd[1] = lStart;
4450     }
4451
4452     if (startEnd[1] < startEnd[0])
4453     {
4454         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range for currency state");
4455     }
4456
4457     if (startEnd[2] == 0)
4458     {
4459         startEnd[2] = 1;
4460     }
4461
4462     if (startEnd[2] > INT_MAX)
4463     {
4464         startEnd[2] = INT_MAX;
4465     }
4466
4467     uint32_t start = startEnd[0], end = startEnd[1], step = startEnd[2];
4468
4469     UniValue ret(UniValue::VARR);
4470
4471     for (int i = start; i <= end; i += step)
4472     {
4473         LOCK(cs_main);
4474         CCoinbaseCurrencyState currencyState = ConnectedChains.GetCurrencyState(i);
4475         UniValue entry(UniValue::VOBJ);
4476         entry.push_back(Pair("height", i));
4477         entry.push_back(Pair("blocktime", (uint64_t)chainActive.LastTip()->nTime));
4478         CAmount price;
4479         entry.push_back(Pair("currencystate", currencyState.ToUniValue()));
4480         ret.push_back(entry);
4481     }
4482     return ret;
4483 }
4484
4485 UniValue getsaplingtree(const UniValue& params, bool fHelp)
4486 {
4487     if (fHelp || params.size() > 1)
4488     {
4489         throw runtime_error(
4490             "getsaplingtree \"n\"\n"
4491             "\nReturns the entries for a light wallet Sapling tree state.\n"
4492
4493             "\nArguments\n"
4494             "   \"n\" or \"m,n\" or \"m,n,o\"         (int or string, optional) height or inclusive range with optional step at which to get the Sapling tree state\n"
4495             "                                                                   If not specified, the latest currency state and height is returned\n"
4496             "\nResult:\n"
4497             "   [\n"
4498             "       {\n"
4499             "           \"network\": \"VRSC\",\n"
4500             "           \"height\": n,\n"
4501             "           \"hash\": \"hex\"\n"
4502             "           \"time\": n,\n"
4503             "           \"tree\": \"hex\"\n"
4504             "       },\n"
4505             "   ]\n"
4506
4507             "\nExamples:\n"
4508             + HelpExampleCli("getsaplingtree", "name")
4509             + HelpExampleRpc("getsaplingtree", "name")
4510         );
4511     }
4512
4513     uint64_t lStart;
4514     uint64_t startEnd[3] = {0};
4515
4516     lStart = startEnd[1] = startEnd[0] = chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 1;
4517
4518     if (params.size() == 1)
4519     {
4520         if (uni_get_int(params[0], -1) == -1 && params[0].isStr())
4521         {
4522             Split(params[0].get_str(), startEnd, startEnd[0], 3);
4523         }
4524     }
4525
4526     if (startEnd[0] > startEnd[1])
4527     {
4528         startEnd[0] = startEnd[1];
4529     }
4530
4531     if (startEnd[1] > lStart)
4532     {
4533         startEnd[1] = lStart;
4534     }
4535
4536     if (startEnd[1] < startEnd[0])
4537     {
4538         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block range for currency state");
4539     }
4540
4541     if (startEnd[2] == 0)
4542     {
4543         startEnd[2] = 1;
4544     }
4545
4546     if (startEnd[2] > INT_MAX)
4547     {
4548         startEnd[2] = INT_MAX;
4549     }
4550
4551     uint32_t start = startEnd[0], end = startEnd[1], step = startEnd[2];
4552
4553     UniValue ret(UniValue::VARR);
4554
4555     LOCK(cs_main);
4556     CCoinsViewCache view(pcoinsTip);
4557     SaplingMerkleTree tree;
4558
4559     for (int i = start; i <= end; i += step)
4560     {
4561         CBlockIndex &blkIndex = *(chainActive[i]);
4562         if (view.GetSaplingAnchorAt(blkIndex.hashFinalSaplingRoot, tree))
4563         {
4564             UniValue entry(UniValue::VOBJ);
4565             entry.push_back(Pair("network", ConnectedChains.ThisChain().name));
4566             entry.push_back(Pair("height", blkIndex.GetHeight()));
4567             entry.push_back(Pair("hash", blkIndex.GetBlockHash().GetHex()));
4568             entry.push_back(Pair("time", (uint64_t)chainActive.LastTip()->nTime));
4569             std::vector<unsigned char> treeBytes = ::AsVector(tree);
4570             entry.push_back(Pair("tree", HexBytes(treeBytes.data(), treeBytes.size())));
4571             ret.push_back(entry);
4572         }
4573     }
4574     return ret;
4575 }
4576
4577 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
4578
4579 UniValue definecurrency(const UniValue& params, bool fHelp)
4580 {
4581     if (fHelp || params.size() != 1)
4582     {
4583         throw runtime_error(
4584             "definecurrency '{\"name\": \"BAAS\", ..., \"nodes\":[{\"networkaddress\":\"identity\"},..]}'\n"
4585             "\nThis defines a blockchain currency, either as an independent blockchain, or as a token on this blockchain. It also spends\n"
4586             "the identity after which this currency is named and sets a bit indicating that it has a currently active blockchain in its name.\n"
4587             "\nTo create a currency of any kind, the identity it is named after must be minted on the blockchain on which the currency is created.\n"
4588             "Once a currency is activated for an identity name, the same symbol may not be reused for another currency or blockchain, even\n"
4589             "if the identity is transferred, revoked or recovered, unless there is an endblock specified and the currency or blockchain has\n"
4590             "deactivated as of that end block.\n"
4591             "\nAll funds to start the currency and for initial conversion amounts must be available to spend from the identity with the same\n"
4592             "name and ID as the currency being defined.\n"
4593             "\nArguments\n"
4594             "      {\n"
4595             "         \"options\" : n,                  (int,    optional) bits:\n"
4596             "                                                             1 = FRACTIONAL, 2 = IDRESTRICTED, 4 = IDSTAKING, 8 = IDREFERRALS\n"
4597             "                                                             0x10 = IDREFERRALSREQUIRED, 0x20 = TOKEN, 0x40 = CANBERESERVE\n"
4598             "         \"name\" : \"xxxx\",              (string, required) name of existing identity with no active or pending blockchain\n"
4599             "         \"idregistrationprice\" : \"xx.xx\", (value, required) price of an identity in native currency\n"
4600             "         \"idreferrallevels\" : n,         (int, required) how many levels ID referrals go back in reward\n"
4601
4602             "         \"notaries\" : \"[identity,..]\", (list, optional) list of identities that are assigned as chain notaries\n"
4603             "         \"minnotariesconfirm\" : n,       (int, optional) unique notary signatures required to confirm an auto-notarization\n"
4604             "         \"notarizationreward\" : \"xx.xx\", (value,  required) default VRSC notarization reward total for first billing period\n"
4605             "         \"billingperiod\" : n,            (int,    optional) number of blocks in each billing period\n"
4606             "         \"proofprotocol\" : n,            (int,    optional) if 2, currency can be minted by whoever controls the ID\n"
4607
4608             "         \"startblock\"    : n,            (int,    optional) VRSC block must be notarized into block 1 of PBaaS chain, default curheight + 100\n"
4609             "         \"endblock\"      : n,            (int,    optional) chain is considered inactive after this block height, and a new one may be started\n"
4610
4611             "         \"currencies\"    : \"[\"VRSC\",..]\", (list, optional) reserve currencies backing this chain in equal amounts\n"
4612             "         \"conversions\"   : \"[\"xx.xx\",..]\", (list, optional) if present, must be same size as currencies. pre-launch conversion ratio overrides\n"
4613             "         \"minpreconversion\" : \"[\"xx.xx\",..]\", (list, optional) must be same size as currencies. minimum in each currency to launch\n"
4614             "         \"maxpreconversion\" : \"[\"xx.xx\",..]\", (list, optional) maximum in each currency allowed\n"
4615
4616             "         \"initialcontributions\" : \"[\"xx.xx\",..]\", (list, optional) initial contribution in each currency\n"
4617             "         \"prelaunchdiscount\" : \"xx.xx\" (value, optional) for fractional reserve currencies less than 100%, discount on final price at launch"
4618             "         \"initialsupply\" : \"xx.xx\"    (value, required for fractional) supply after conversion of contributions, before preallocation\n"
4619             "         \"prelaunchcarveouts\" : \"[{\"identity\":xx.xx}..]\", (list, optional) identities and % of pre-converted amounts from each reserve currency\n"
4620             "         \"preallocations\" : \"[{\"identity\":xx.xx}..]\", (list, optional)  list of identities and amounts from pre-allocation\n"
4621
4622             "         \"eras\"          : \"objarray\", (array, optional) data specific to each era, maximum 3\n"
4623             "         {\n"
4624             "            \"reward\"     : n,           (int64,  optional) native initial block rewards in each period\n"
4625             "            \"decay\"      : n,           (int64,  optional) reward decay for each era\n"
4626             "            \"halving\"    : n,           (int,    optional) halving period for each era\n"
4627             "            \"eraend\"     : n,           (int,    optional) ending block of each era\n"
4628             "         }\n"
4629             "         \"nodes\"         : \"[obj, ..]\", (objectarray, optional) up to 2 nodes that can be used to connect to the blockchain"
4630             "         [{\n"
4631             "            \"networkaddress\" : \"txid\", (string,  optional) internet, TOR, or other supported address for node\n"
4632             "            \"nodeidentity\" : \"name@\",  (string, optional) rewards payment and identity\n"
4633             "         }, .. ]\n"
4634             "      }\n"
4635
4636             "\nResult:\n"
4637             "{\n"
4638             "  \"txid\" : \"transactionid\", (string) The transaction id\n"
4639             "  \"tx\"   : \"json\",          (json)   The transaction decoded as a transaction\n"
4640             "  \"hex\"  : \"data\"           (string) Raw data for signed transaction\n"
4641             "}\n"
4642
4643             "\nExamples:\n"
4644             + HelpExampleCli("definecurrency", "jsondefinition")
4645             + HelpExampleRpc("definecurrency", "jsondefinition")
4646         );
4647     }
4648
4649     CheckPBaaSAPIsValid();
4650     bool isVerusActive = IsVerusActive();
4651
4652     uint160 thisChainID = ConnectedChains.ThisChain().GetID();
4653
4654     if (!params[0].isObject())
4655     {
4656         throw JSONRPCError(RPC_INVALID_PARAMETER, "JSON object required. see help.");
4657     }
4658
4659     if (!pwalletMain)
4660     {
4661         throw JSONRPCError(RPC_WALLET_ERROR, "must have active wallet to define PBaaS chain");
4662     }
4663
4664     UniValue valStr(UniValue::VSTR);
4665     if (!valStr.read(params[0].write()))
4666     {
4667         throw JSONRPCError(RPC_INVALID_PARAMS, "Invalid characters in blockchain definition");
4668     }
4669
4670     CCurrencyDefinition newChain(params[0]);
4671
4672     if (!newChain.IsValid())
4673     {
4674         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid currency definition. see help.");
4675     }
4676
4677     LOCK2(cs_main, pwalletMain->cs_wallet);
4678
4679     CCurrencyDefinition checkDef;
4680     if (GetCurrencyDefinition(newChain.name, checkDef))
4681     {
4682         throw JSONRPCError(RPC_INVALID_PARAMETER, newChain.name + " chain already defined. see help.");
4683     }
4684
4685     // a new blockchain can only be defined with the current chain as parent
4686     if (newChain.parent.IsNull())
4687     {
4688         throw JSONRPCError(RPC_INVALID_PARAMETER, params[0].write() + " invalid chain name.");
4689     }
4690
4691     if (newChain.parent != thisChainID)
4692     {
4693         // parent chain must be current chain
4694         throw JSONRPCError(RPC_INVALID_PARAMETER, "attempting to define a chain relative to a parent that is not the current chain.");
4695     }
4696
4697     for (auto &oneID : newChain.preAllocation)
4698     {
4699         if (!CIdentity::LookupIdentity(CIdentityID(oneID.first)).IsValid())
4700         {
4701             throw JSONRPCError(RPC_INVALID_PARAMETER, "attempting to pre-allocate currency to a non-existent ID.");
4702         }
4703     }
4704
4705     uint160 newChainID = newChain.GetID();
4706
4707     std::vector<CNodeData> vNodes;
4708     UniValue nodeArr = find_value(params[0], "nodes");
4709     if (nodeArr.isArray() && nodeArr.size())
4710     {
4711         for (int i = 0; i < nodeArr.size(); i++)
4712         {
4713             CNodeData nd(nodeArr[i]);
4714             if (nd.IsValid())
4715             {
4716                 vNodes.push_back(nd);
4717                 if (vNodes.size() == 2)
4718                 {
4719                     break;
4720                 }
4721             }
4722         }
4723     }
4724
4725     // a new currency definition must spend an ID that currently has no active currency, which sets a semaphore that "blocks"
4726     // that ID from having more than one at once. Before submitting the transaction, it must be properly signed by the primary authority. 
4727     // This also has the effect of piggybacking on the ID protocol's deconfliction between mined blocks to avoid name conflicts, 
4728     // as the ID can only have its bit set or unset by one transaction at any time and only as part of a transaction that changes the
4729     // the state of a potentially active currency.
4730
4731     if (newChain.IsToken())
4732     {
4733         if (newChain.rewards.size())
4734         {
4735             throw JSONRPCError(RPC_INVALID_PARAMETER, "currency cannot be both a token and also specify a mining and staking rewards schedule.");
4736         }
4737         // if this is a token definition, the systemID is this chain
4738         newChain.systemID = ConnectedChains.ThisChain().GetID();
4739     }
4740     else
4741     {
4742         // it is a PBaaS chain, and it is its own system, responsible for its own communication and currency control
4743         newChain.systemID = newChainID;
4744     }
4745
4746     CIdentity launchIdentity;
4747     uint32_t height = chainActive.Height();
4748     uint32_t idHeight = 0;
4749     CTxIn idTxIn;
4750     bool canSign = false, canSpend = false;
4751
4752     std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
4753     if (pwalletMain->GetIdentity(newChainID, keyAndIdentity))
4754     {
4755         canSign = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SIGN;
4756         canSpend = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SPEND;
4757         launchIdentity = static_cast<CIdentity>(keyAndIdentity.second);
4758     }
4759
4760     if (!(launchIdentity = CIdentity::LookupIdentity(newChainID, 0, &idHeight, &idTxIn)).IsValidUnrevoked() || launchIdentity.HasActiveCurrency())
4761     {
4762         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID " + newChain.name + " not found, is revoked, or already has an active currency defined");
4763     }
4764
4765     if (launchIdentity.parent != thisChainID)
4766     {
4767         throw JSONRPCError(RPC_INVALID_PARAMETER, "Currency can only be defined using an ID issued by " + VERUS_CHAINNAME);
4768     }
4769
4770     // refunding a currency after its launch is aborted, or shutting it down after the endblock has passed must be completed
4771     // to fully decommission a blockchain and clear the active blockchain bit from an ID
4772
4773     //if (!newChain.startBlock || newChain.startBlock < (chainActive.Height() + PBAAS_MINSTARTBLOCKDELTA))
4774     //{
4775     //    newChain.startBlock = chainActive.Height() + (PBAAS_MINSTARTBLOCKDELTA + 5);    // give a little time to send the tx
4776     //}
4777     if (!newChain.startBlock || newChain.startBlock < (chainActive.Height() + 10))
4778     {
4779         newChain.startBlock = chainActive.Height() + 15;    // give a little time to send the tx
4780     }
4781
4782     if (newChain.endBlock && newChain.endBlock < (newChain.startBlock + CCurrencyDefinition::MIN_CURRENCY_LIFE))
4783     {
4784         throw JSONRPCError(RPC_INVALID_PARAMS, "If endblock (" + to_string(newChain.endBlock) + 
4785                                                ") is specified, it must be at least " + to_string(CCurrencyDefinition::MIN_CURRENCY_LIFE) + 
4786                                                " blocks after startblock (" + to_string(newChain.startBlock) + ")\n");
4787     }
4788
4789     if (!newChain.IsToken())
4790     {
4791         throw JSONRPCError(RPC_INVALID_PARAMS, "Current testnet does not support launch of independent PBaaS blockchains. Please use token currencies for now.\n");
4792
4793         if (newChain.billingPeriod < CCurrencyDefinition::MIN_BILLING_PERIOD || (newChain.notarizationReward / newChain.billingPeriod) < CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION)
4794         {
4795             throw JSONRPCError(RPC_INVALID_PARAMS, "Billing period of at least " + 
4796                                                 to_string(CCurrencyDefinition::MIN_BILLING_PERIOD) + 
4797                                                 " blocks and per-block notary rewards of >= " + to_string(CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION) + 
4798                                                 " are required to define an active currency\n");
4799         }
4800
4801         // TODO: check to see if rewards obviously lead to an unstable currency
4802         //for (int i = 0; i < newChain.rewards.size(); i++)
4803         //{
4804         //}
4805
4806         // if we have no emission parameters, this is not a PBaaS blockchain, it is a controlled or bridged token.
4807         // controlled tokens can be centrally or algorithmically controlled.
4808         if (newChain.rewards.empty())
4809         {
4810             throw JSONRPCError(RPC_INVALID_PARAMS, "A currency must either be based on a token protocol or must specify blockchain rewards, even if 0\n");
4811         }
4812     }
4813
4814     if (newChain.currencies.empty() && newChain.IsFractional())
4815     {
4816         throw JSONRPCError(RPC_INVALID_PARAMS, "Fractional reserve currencies must specify at least one reserve currency\n");
4817     }
4818
4819     // if this is a fractional reserve currency, ensure that all reserves are currently active 
4820     // with at least as long of a life as this currency and that at least one of the currencies
4821     // is VRSC or VRSCTEST.
4822     std::vector<CCurrencyDefinition> reserveCurrencies;
4823     bool hasCoreReserve = false;
4824     if (newChain.IsFractional())
4825     {
4826         if (newChain.contributions.size() != newChain.currencies.size())
4827         {
4828             throw JSONRPCError(RPC_INVALID_PARAMETER, "All reserves must have non-zero initial contributions for each reserve for fractional currency " + newChain.name);
4829         }
4830         for (int i = 0; i < newChain.contributions.size(); i++)
4831         {
4832             if (newChain.contributions[i] <= 0)
4833             {
4834                 throw JSONRPCError(RPC_INVALID_PARAMETER, "All reserves must have non-zero initial contributions " + EncodeDestination(CIdentityID(newChain.currencies[i])));
4835             }
4836         }
4837         newChain.preconverted = newChain.contributions;
4838         for (auto &currency : newChain.currencies)
4839         {
4840             reserveCurrencies.emplace_back();
4841             if (!GetCurrencyDefinition(currency, reserveCurrencies.back()) ||
4842                 reserveCurrencies.back().startBlock >= height)
4843             {
4844                 throw JSONRPCError(RPC_INVALID_PARAMETER, "All reserve currencies of a fractional currency must be valid and past the start block " + EncodeDestination(CIdentityID(currency)));
4845             }
4846
4847             if (reserveCurrencies.back().endBlock && (!newChain.endBlock || reserveCurrencies.back().endBlock < newChain.endBlock))
4848             {
4849                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Reserve currency " + EncodeDestination(CIdentityID(currency)) + " ends its life before the fractional currency's endblock");
4850             }
4851
4852             if (!reserveCurrencies.back().CanBeReserve())
4853             {
4854                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Currency " + EncodeDestination(CIdentityID(currency)) + " may not be used as a reserve");
4855             }
4856
4857             if (currency == VERUS_CHAINID)
4858             {
4859                 hasCoreReserve = true;
4860             }
4861         }
4862         if (!hasCoreReserve)
4863         {
4864             throw JSONRPCError(RPC_INVALID_PARAMETER, "Fractional currency requires a reserve of " + VERUS_CHAINNAME + " in addition to any other reserves");
4865         }
4866     }
4867
4868     // now, create the outputs:
4869     // 1. Updated identity with active currency
4870     // 2. Currency definition
4871     // 3. Notarization thread
4872     // 4. Import thread
4873     // 5. Export thread
4874     // 6. Initial contribution exports
4875     // 7. Critical identity exports (chain identity, notaries, pre-allocation recipients)
4876     //
4877     // ensure that the appropriate identity is an input to the transaction,
4878     // and fund the transaction
4879     std::vector<CRecipient> vOutputs;
4880
4881     // first, we need the identity output with currency activated
4882     launchIdentity.ActivateCurrency();
4883     vOutputs.push_back({launchIdentity.IdentityUpdateOutputScript(), 0, false});
4884
4885     // now, create the currency definition output
4886     CCcontract_info CC;
4887     CCcontract_info *cp;
4888     cp = CCinit(&CC, EVAL_CURRENCY_DEFINITION);
4889     CPubKey pk(ParseHex(CC.CChexstr));
4890
4891     std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_CURRENCY_DEFINITION)),
4892                                             CKeyID(newChain.GetConditionID(EVAL_CURRENCY_DEFINITION))});
4893     std::vector<CTxDestination> dests({pk});
4894
4895     vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCurrencyDefinition>(EVAL_CURRENCY_DEFINITION, dests, 1, &newChain), &indexDests), 
4896                                          CCurrencyDefinition::DEFAULT_OUTPUT_VALUE, 
4897                                          false});
4898
4899     // get initial currency state at this height
4900     CCoinbaseCurrencyState newCurrencyState = ConnectedChains.GetCurrencyState(newChain, chainActive.Height());
4901
4902     // we need to make a notarization, notarize this information and block 0, which is the same as out block 0
4903     // our authorization will be that we are the chain definition
4904     uint256 mmvRoot, nodePreHash;
4905
4906     auto mmr = chainActive.GetMMR();
4907     auto mmv = ChainMerkleMountainView(mmr);
4908     mmv.resize(1);
4909     mmvRoot = mmv.GetRoot();
4910     nodePreHash = mmr.GetNode(0).hash;
4911
4912     CTxDestination notaryDest;
4913     extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY;
4914     // use default ID as notary ID if there is one
4915     if (!VERUS_DEFAULTID.IsNull())
4916     {
4917         notaryDest = VERUS_DEFAULTID;
4918     }
4919     // use pub key if there is one
4920     else if (USE_EXTERNAL_PUBKEY)
4921     {
4922         CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
4923         if (pubKey.IsFullyValid())
4924         {
4925             notaryDest = pubKey.GetID();
4926         }
4927     }
4928
4929     CPBaaSNotarization pbn = CPBaaSNotarization(newChain.notarizationProtocol, 
4930                                                 newChainID, 
4931                                                 notaryDest, 
4932                                                 0, 
4933                                                 mmvRoot, 
4934                                                 nodePreHash, 
4935                                                 mmr.GetNode(0).power,
4936                                                 newCurrencyState,
4937                                                 uint256(),
4938                                                 0,
4939                                                 uint256(),
4940                                                 0,
4941                                                 COpRetProof(),
4942                                                 vNodes);
4943
4944     // make the first chain notarization output
4945     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
4946     CTxDestination notarizationDest;
4947
4948     if (newChain.notarizationProtocol == newChain.NOTARIZATION_AUTO || newChain.notarizationProtocol == newChain.NOTARIZATION_NOTARY_CONFIRM)
4949     {
4950         notarizationDest = CPubKey(ParseHex(CC.CChexstr));
4951     }
4952     else if (newChain.notarizationProtocol == newChain.NOTARIZATION_NOTARY_CHAINID)
4953     {
4954         notarizationDest = CIdentityID(newChainID);
4955     }
4956     else
4957     {
4958         throw JSONRPCError(RPC_INVALID_PARAMETER, "None or notarization protocol specified");
4959     }
4960
4961     indexDests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_ACCEPTEDNOTARIZATION))});
4962     dests = std::vector<CTxDestination>({notarizationDest});
4963
4964     vOutputs.push_back({MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn), &indexDests), 
4965                                          newChain.notarizationReward, 
4966                                          false});
4967
4968     // make the finalization output
4969     cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
4970     pk = CPubKey(ParseHex(CC.CChexstr));
4971
4972     indexDests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_FINALIZE_NOTARIZATION))});
4973     dests = std::vector<CTxDestination>({pk});
4974
4975     CTransactionFinalization nf;
4976
4977     vOutputs.push_back({MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf), &indexDests), 
4978                                          CTransactionFinalization::DEFAULT_OUTPUT_VALUE, 
4979                                          false});
4980
4981     std::set<CIdentityID> idExportSet;
4982
4983     if (!newChain.IsToken())
4984     {
4985         idExportSet = std::set<CIdentityID>({newChainID});
4986
4987         // create chain transfer exports for launch identity, notaries, and preallocation recipients
4988         for (auto &notary : newChain.notaries)
4989         {
4990             idExportSet.insert(notary);
4991         }
4992         for (auto &oneAlloc : newChain.preAllocation)
4993         {
4994             idExportSet.insert(oneAlloc.first);
4995         }
4996
4997         // now, look them all up and create exports for zero value to move the IDs
4998         for (auto &oneID : idExportSet)
4999         {
5000             CIdentity oneIdentity = (oneID == newChainID) ? launchIdentity : CIdentity::LookupIdentity(oneID);
5001             if (!oneIdentity.IsValid())
5002             {
5003                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid specified identity: " + EncodeDestination(CIdentityID(oneID)));
5004             }
5005
5006             // emit a reserve transfer output
5007             cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
5008             CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
5009
5010             // send a zero output to this ID and pass the full ID to do so
5011             std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
5012             indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
5013                                                       CKeyID(newChain.systemID)});
5014
5015             CTransferDestination transferDest = IdentityToTransferDestination(oneIdentity);
5016             CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID, 
5017                                                    ASSETCHAINS_CHAINID, 
5018                                                    0, 
5019                                                    CReserveTransfer::CalculateTransferFee(transferDest), 
5020                                                    newChain.GetID(),
5021                                                    transferDest);
5022
5023             vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests), 
5024                                                  rt.nFees, 
5025                                                  false});
5026         }
5027     }
5028     else
5029     {
5030         // output preallocation
5031         for (auto &oneAlloc : newChain.preAllocation)
5032         {
5033             // emit a reserve transfer output, which will be imported on successful launch
5034             cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
5035             CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
5036
5037             std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
5038             indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
5039                                                       CKeyID(newChainID)});
5040
5041             CTransferDestination transferDest = DestinationToTransferDestination(CIdentityID(oneAlloc.first));
5042             CReserveTransfer rt = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::PREALLOCATE, 
5043                                                    newChain.systemID, 
5044                                                    oneAlloc.second,
5045                                                    CReserveTransfer::CalculateTransferFee(transferDest), 
5046                                                    newChainID,
5047                                                    transferDest);
5048
5049             vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests), 
5050                                 CReserveTransfer::CalculateTransferFee(transferDest), false});
5051         }
5052     }
5053
5054     // create import and export outputs
5055     cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
5056     pk = CPubKey(ParseHex(CC.CChexstr));
5057
5058     CTxDestination proofDest;
5059
5060     if (newChain.proofProtocol == newChain.PROOF_PBAASMMR || newChain.proofProtocol == newChain.PROOF_CHAINID)
5061     {
5062         proofDest = CPubKey(ParseHex(CC.CChexstr));
5063     }
5064     else
5065     {
5066         throw JSONRPCError(RPC_INVALID_PARAMETER, "None or notarization protocol specified");
5067     }
5068
5069     // import thread is specific to the chain importing from
5070     dests = std::vector<CTxDestination>({proofDest});
5071     indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_IMPORT))});
5072     // if this is a token on this chain, the transfer that is output here is burned through the export 
5073     // and merges with the import thread. we multiply new input times 2, to cover both the import thread output 
5074     // and the reserve transfer outputs.
5075     CCrossChainImport cci(newChainID, CCurrencyValueMap(), CCurrencyValueMap());
5076     vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests), 0, false});
5077
5078     // export thread
5079     cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
5080     dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
5081     indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_EXPORT))});
5082     if (newChain.systemID != ASSETCHAINS_CHAINID)
5083     {
5084         indexDests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(newChain.systemID, EVAL_CROSSCHAIN_EXPORT)));
5085     }
5086     CCrossChainExport ccx = CCrossChainExport(newChainID, 0, CCurrencyValueMap(), CCurrencyValueMap());
5087     vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests), 0, false});
5088
5089     // make the outputs for initial contributions
5090     if (newChain.contributions.size() && newChain.contributions.size() == newChain.currencies.size())
5091     {
5092         for (int i = 0; i < newChain.currencies.size(); i++)
5093         {
5094             if (newChain.contributions[i] > 0)
5095             {
5096                 CAmount contribution = newChain.contributions[i] + 
5097                                         CReserveTransactionDescriptor::CalculateAdditionalConversionFee(newChain.contributions[i]);
5098                 CAmount fee = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1;
5099
5100                 CReserveTransfer rt = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::PRECONVERT,
5101                                                        newChain.currencies[i],
5102                                                        contribution,
5103                                                        fee,
5104                                                        newChainID,
5105                                                        DestinationToTransferDestination(CIdentityID(newChainID)));
5106
5107                 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
5108                 CPubKey pk(ParseHex(CC.CChexstr));
5109
5110                 indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
5111                                                           CKeyID(newChain.GetID())});
5112                 dests = std::vector<CTxDestination>({pk});
5113
5114                 vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests), 
5115                                                      newChain.currencies[i] == thisChainID ? contribution + fee : 0, 
5116                                                      false});
5117                 
5118                 // TODO: send export and conversion fees to reserve deposit, as appropriate, to enable payment
5119             }
5120         }
5121     }
5122
5123     // create the transaction
5124     CWalletTx wtx;
5125     CMutableTransaction mtx(wtx);
5126     mtx.vin.push_back(idTxIn);
5127     *static_cast<CTransaction*>(&wtx) = mtx;
5128
5129     {
5130         LOCK2(cs_main, pwalletMain->cs_wallet);
5131
5132         CReserveKey reserveKey(pwalletMain);
5133         CAmount fee;
5134         int nChangePos;
5135         int nChangeOutput;
5136         string failReason;
5137
5138         CTxDestination chainDefSource = CIdentityID(newChainID);
5139
5140         int errorRet;
5141         if ((errorRet = pwalletMain->CreateReserveTransaction(vOutputs, 
5142                                                               wtx, 
5143                                                               reserveKey, 
5144                                                               fee, 
5145                                                               nChangePos, 
5146                                                               nChangeOutput, 
5147                                                               failReason, 
5148                                                               NULL, 
5149                                                               &chainDefSource)) != pwalletMain->RPC_OK)
5150         {
5151             throw JSONRPCError(RPC_TRANSACTION_ERROR, newChain.name + ": " + failReason);
5152         }
5153     }
5154
5155     UniValue uvret(UniValue::VOBJ);
5156     std::vector<CCurrencyDefinition> curDefs = CCurrencyDefinition::GetCurrencyDefinitions(wtx);
5157     uvret.push_back(Pair("currencydefinition", (curDefs.size() ? curDefs[0] : CCurrencyDefinition()).ToUniValue()));
5158
5159     uvret.push_back(Pair("basenotarization", CPBaaSNotarization(wtx).ToUniValue()));
5160
5161     uvret.push_back(Pair("txid", wtx.GetHash().GetHex()));
5162
5163     UniValue txJSon(UniValue::VOBJ);
5164     TxToJSON(wtx, uint256(), txJSon);
5165     uvret.push_back(Pair("tx",  txJSon));
5166
5167     string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
5168     uvret.push_back(Pair("hex", strHex));
5169
5170     return uvret;
5171 }
5172
5173 UniValue registernamecommitment(const UniValue& params, bool fHelp)
5174 {
5175     if (fHelp || (params.size() < 2 && params.size() > 3))
5176     {
5177         throw runtime_error(
5178             "registernamecommitment \"name\" \"controladdress\" (\"referralidentity\")\n"
5179             "\nRegisters a name commitment, which is required as a source for the name to be used when registering an identity. The name commitment hides the name itself\n"
5180             "while ensuring that the miner who mines in the registration cannot front-run the name unless they have also registered a name commitment for the same name or\n"
5181             "are willing to forfeit the offer of payment for the chance that a commitment made now will allow them to register the name in the future.\n"
5182
5183             "\nArguments\n"
5184             "\"name\"                           (string, required)  the unique name to commit to. creating a name commitment is not a registration, and if one is\n"
5185             "                                                       created for a name that exists, it may succeed, but will never be able to be used.\n"
5186             "\"controladdress\"                 (address, required) address that will control this commitment\n"
5187             "\"referralidentity\"               (identity, optional)friendly name or identity address that is provided as a referral mechanism and to lower network cost of the ID\n"
5188
5189             "\nResult: obj\n"
5190             "{\n"
5191             "    \"txid\" : \"hexid\"\n"
5192             "    \"namereservation\" :\n"
5193             "    {\n"
5194             "        \"name\"    : \"namestr\",     (string) the unique name in this commitment\n"
5195             "        \"salt\"    : \"hexstr\",      (hex)    salt used to hide the commitment\n"
5196             "        \"referral\": \"identityaddress\", (base58) address of the referring identity if there is one\n"
5197             "        \"parent\"  : \"namestr\",   (string) name of the parent if not Verus or Verus test\n"
5198             "        \"nameid\"  : \"address\",   (base58) identity address for this identity if it is created\n"
5199             "    }\n"
5200             "}\n"
5201
5202             "\nExamples:\n"
5203             + HelpExampleCli("registernamecommitment", "\"name\"")
5204             + HelpExampleRpc("registernamecommitment", "\"name\"")
5205         );
5206     }
5207
5208     CheckIdentityAPIsValid();
5209
5210     uint160 parent;
5211     std::string name = CleanName(uni_get_str(params[0]), parent, true);
5212
5213     // if either we have an invalid name or an implied parent, that is not valid
5214     if (name == "" || !(parent == ASSETCHAINS_CHAINID) || name != uni_get_str(params[0]))
5215     {
5216         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid name for commitment. Names must not have leading or trailing spaces and must not include any of the following characters between parentheses (\\/:*?\"<>|@)");
5217     }
5218
5219     parent = ConnectedChains.ThisChain().GetID();
5220
5221     CTxDestination dest = DecodeDestination(uni_get_str(params[1]));
5222     if (dest.which() == COptCCParams::ADDRTYPE_INVALID)
5223     {
5224         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid control address for commitment");
5225     }
5226
5227     // create the transaction with native coin as input
5228     LOCK2(cs_main, pwalletMain->cs_wallet);
5229
5230     CIdentityID referrer;
5231     if (params.size() > 2)
5232     {
5233         CTxDestination referDest = DecodeDestination(uni_get_str(params[2]));
5234         if (referDest.which() != COptCCParams::ADDRTYPE_ID)
5235         {
5236             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid referral identity for commitment, must be a currently registered friendly name or i-address");
5237         }
5238         referrer = CIdentityID(GetDestinationID(referDest));
5239         if (!CIdentity::LookupIdentity(referrer).IsValidUnrevoked())
5240         {
5241             throw JSONRPCError(RPC_INVALID_PARAMETER, "Referral identity for commitment must be a currently valid, unrevoked friendly name or i-address");
5242         }
5243     }
5244
5245     CNameReservation nameRes(name, referrer, GetRandHash());
5246     CCommitmentHash commitment(nameRes.GetCommitment());
5247     
5248     CConditionObj<CCommitmentHash> condObj(EVAL_IDENTITY_COMMITMENT, std::vector<CTxDestination>({dest}), 1, &commitment);
5249     std::vector<CRecipient> outputs = std::vector<CRecipient>({{MakeMofNCCScript(condObj, &dest), CCommitmentHash::DEFAULT_OUTPUT_AMOUNT, false}});
5250     CWalletTx wtx;
5251
5252     if (CIdentity::LookupIdentity(CIdentity::GetID(name, parent)).IsValid())
5253     {
5254         throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity already exists.");
5255     }
5256
5257     CReserveKey reserveKey(pwalletMain);
5258     CAmount fee;
5259     int nChangePos;
5260     string failReason;
5261
5262     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason))
5263     {
5264         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create commitment transaction: " + failReason);
5265     }
5266     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5267     {
5268         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5269     }
5270
5271     UniValue ret(UniValue::VOBJ);
5272     ret.push_back(Pair("txid", wtx.GetHash().GetHex()));
5273     ret.push_back(Pair("namereservation", nameRes.ToUniValue()));
5274     return ret;
5275 }
5276
5277 UniValue registeridentity(const UniValue& params, bool fHelp)
5278 {
5279     if (fHelp || params.size() < 1 || params.size() > 2)
5280     {
5281         throw runtime_error(
5282             "registeridentity \"jsonidregistration\" feeoffer\n"
5283             "\n\n"
5284
5285             "\nArguments\n"
5286             "{\n"
5287             "    \"txid\" : \"hexid\",          (hex)    the transaction ID of the name committment for this ID name\n"
5288             "    \"namereservation\" :\n"
5289             "    {\n"
5290             "        \"name\": \"namestr\",     (string) the unique name in this commitment\n"
5291             "        \"salt\": \"hexstr\",      (hex)    salt used to hide the commitment\n"
5292             "        \"referrer\": \"identityID\", (name@ or address) must be a valid ID to use as a referrer to receive a discount\n"
5293             "    },\n"
5294             "    \"identity\" :\n"
5295             "    {\n"
5296             "        \"name\": \"namestr\",     (string) the unique name for this identity\n"
5297             "        ...\n"
5298             "    }\n"
5299             "}\n"
5300             "feeoffer                           (amount, optional) amount to offer miner/staker for the registration fee, if missing, uses standard price\n\n"
5301
5302             "\nResult:\n"
5303             "   transactionid                   (hexstr)\n"
5304
5305             "\nExamples:\n"
5306             + HelpExampleCli("registeridentity", "jsonidregistration")
5307             + HelpExampleRpc("registeridentity", "jsonidregistration")
5308         );
5309     }
5310
5311     CheckIdentityAPIsValid();
5312
5313     // all names have a parent of the current chain
5314     uint160 parent = ConnectedChains.ThisChain().GetID();
5315
5316     uint256 txid = uint256S(uni_get_str(find_value(params[0], "txid")));
5317     CNameReservation reservation(find_value(params[0], "namereservation"));
5318
5319     CIdentity newID(find_value(params[0], "identity"));
5320     if (!newID.IsValid())
5321     {
5322         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity");
5323     }
5324
5325     CAmount feeOffer;
5326     CAmount minFeeOffer = reservation.referral.IsNull() ? 
5327                           ConnectedChains.ThisChain().IDFullRegistrationAmount() : 
5328                           ConnectedChains.ThisChain().IDReferredRegistrationAmount();
5329
5330     if (params.size() > 1)
5331     {
5332         feeOffer = AmountFromValue(params[1]);
5333     }
5334     else
5335     {
5336         feeOffer = minFeeOffer;
5337     }
5338
5339     if (feeOffer < minFeeOffer)
5340     {
5341         throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee offer must be at least " + ValueFromAmount(minFeeOffer).write());
5342     }
5343
5344     uint160 impliedParent, resParent;
5345     if (txid.IsNull() || 
5346         CleanName(reservation.name, resParent) != CleanName(newID.name, impliedParent) || 
5347         resParent != impliedParent ||
5348         impliedParent != ASSETCHAINS_CHAINID)
5349     {
5350         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid identity description or mismatched reservation.");
5351     }
5352
5353     // lookup commitment to be sure that we can register this identity
5354     LOCK2(cs_main, pwalletMain->cs_wallet);
5355
5356     uint256 hashBlk;
5357     uint32_t commitmentHeight;
5358     CTransaction txOut;
5359     CCommitmentHash ch;
5360     int commitmentOutput;
5361
5362     // must be present and in a mined block
5363     {
5364         LOCK(mempool.cs);
5365         if (!myGetTransaction(txid, txOut, hashBlk) || hashBlk.IsNull())
5366         {
5367             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment transaction id");
5368         }
5369
5370         auto indexIt = mapBlockIndex.find(hashBlk);
5371         if (indexIt == mapBlockIndex.end() || indexIt->second->GetHeight() > chainActive.Height() || chainActive[indexIt->second->GetHeight()]->GetBlockHash() != indexIt->second->GetBlockHash())
5372         {
5373             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unconfirmed commitment");
5374         }
5375
5376         commitmentHeight = indexIt->second->GetHeight();
5377
5378         for (int i = 0; i < txOut.vout.size(); i++)
5379         {
5380             COptCCParams p;
5381             if (txOut.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_COMMITMENT && p.vData.size())
5382             {
5383                 commitmentOutput = i;
5384                 ::FromVector(p.vData[0], ch);
5385                 break;
5386             }
5387         }
5388         if (ch.hash.IsNull())
5389         {
5390             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment hash");
5391         }
5392     }
5393
5394     if (ch.hash != reservation.GetCommitment().hash)
5395     {
5396         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid commitment salt or referral ID");
5397     }
5398
5399     // when creating an ID, the parent is always the current chains, and it is invalid to specify a parent
5400     if (newID.parent != parent)
5401     {
5402         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid to specify alternate parent when creating an identity. Parent is determined by the current blockchain.");
5403     }
5404
5405     newID.parent = parent;
5406
5407     CIdentity dupID = newID.LookupIdentity(newID.GetID());
5408     if (dupID.IsValid())
5409     {
5410         throw JSONRPCError(RPC_VERIFY_ALREADY_IN_CHAIN, "Identity already exists.");
5411     }
5412
5413     // make sure we have a revocation and recovery authority defined
5414     CIdentity revocationAuth = (newID.revocationAuthority.IsNull() || newID.revocationAuthority == newID.GetID()) ? newID : newID.LookupIdentity(newID.revocationAuthority);
5415     CIdentity recoveryAuth = (newID.recoveryAuthority.IsNull() || newID.recoveryAuthority == newID.GetID()) ? newID : newID.LookupIdentity(newID.recoveryAuthority);
5416
5417     if (!recoveryAuth.IsValidUnrevoked() || !revocationAuth.IsValidUnrevoked())
5418     {
5419         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked recovery, or revocation identity.");
5420     }
5421
5422     // now we have a new and valid primary, revocation, and recovery identity, as well as a valid reservation
5423     newID.revocationAuthority = revocationAuth.GetID();
5424     newID.recoveryAuthority = recoveryAuth.GetID();
5425
5426     // create the identity definition transaction & reservation key output
5427     CConditionObj<CNameReservation> condObj(EVAL_IDENTITY_RESERVATION, std::vector<CTxDestination>({CIdentityID(newID.GetID())}), 1, &reservation);
5428     CTxDestination resIndexDest = CKeyID(CCrossChainRPCData::GetConditionID(newID.GetID(), EVAL_IDENTITY_RESERVATION));
5429     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5430
5431     // add referrals, Verus supports referrals
5432     if ((ConnectedChains.ThisChain().IDReferrals() || IsVerusActive()) && !reservation.referral.IsNull())
5433     {
5434         uint32_t referralHeight;
5435         CTxIn referralTxIn;
5436         CTransaction referralIdTx;
5437         auto referralIdentity =  newID.LookupIdentity(reservation.referral, commitmentHeight - 1);
5438         if (referralIdentity.IsValidUnrevoked())
5439         {
5440             if (!newID.LookupFirstIdentity(reservation.referral, &referralHeight, &referralTxIn, &referralIdTx).IsValid())
5441             {
5442                 throw JSONRPCError(RPC_DATABASE_ERROR, "Database or blockchain data error, \"" + referralIdentity.name + "\" seems valid, but first instance is not found in index");
5443             }
5444
5445             // create outputs for this referral and up to n identities back in the referral chain
5446             outputs.push_back({referralIdentity.TransparentOutput(referralIdentity.GetID()), ConnectedChains.ThisChain().IDReferralAmount(), false});
5447             feeOffer -= ConnectedChains.ThisChain().IDReferralAmount();
5448             int afterId = referralTxIn.prevout.n + 1;
5449             for (int i = afterId; i < referralIdTx.vout.size() && (i - afterId) < (ConnectedChains.ThisChain().idReferralLevels - 1); i++)
5450             {
5451                 CTxDestination nextID;
5452                 COptCCParams p, master;
5453
5454                 if (referralIdTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && 
5455                     p.IsValid() && 
5456                     p.evalCode == EVAL_NONE && 
5457                     p.vKeys.size() == 1 && 
5458                     (p.vData.size() == 1 ||
5459                     (p.vData.size() == 2 && 
5460                     p.vKeys[0].which() == COptCCParams::ADDRTYPE_ID &&
5461                     (master = COptCCParams(p.vData[1])).IsValid() &&
5462                     master.evalCode == EVAL_NONE)))
5463                 {
5464                     outputs.push_back({newID.TransparentOutput(CIdentityID(GetDestinationID(p.vKeys[0]))), ConnectedChains.ThisChain().IDReferralAmount(), false});
5465                     feeOffer -= ConnectedChains.ThisChain().IDReferralAmount();
5466                 }
5467                 else
5468                 {
5469                     break;
5470                 }
5471             }
5472         }
5473         else
5474         {
5475             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or revoked referral identity at time of commitment");
5476         }
5477     }
5478
5479     CScript reservationOutScript = MakeMofNCCScript(condObj, &resIndexDest);
5480     outputs.push_back({reservationOutScript, CNameReservation::DEFAULT_OUTPUT_AMOUNT, false});
5481
5482     // make one dummy output, which CreateTransaction will leave as last, and we will remove to add its output to the fee
5483     // this serves to keep the change output after our real reservation output
5484     outputs.push_back({reservationOutScript, feeOffer, false});
5485
5486     CWalletTx wtx;
5487
5488     CReserveKey reserveKey(pwalletMain);
5489     CAmount fee;
5490     int nChangePos;
5491     string failReason;
5492
5493     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5494     {
5495         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to create identity transaction: " + failReason);
5496     }
5497
5498     // add commitment output
5499     CMutableTransaction mtx(wtx);
5500     mtx.vin.push_back(CTxIn(txid, commitmentOutput));
5501
5502     // remove the fee offer output
5503     mtx.vout.pop_back();
5504
5505     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5506
5507     // now sign
5508     CCoinsViewCache view(pcoinsTip);
5509     for (int i = 0; i < wtx.vin.size(); i++)
5510     {
5511         bool signSuccess;
5512         SignatureData sigdata;
5513
5514         CCoins coins;
5515         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5516         {
5517             break;
5518         }
5519
5520         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5521
5522         signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5523
5524         if (!signSuccess)
5525         {
5526             LogPrintf("%s: failure to sign identity registration tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5527             printf("%s: failure to sign identity registration tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5528             throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5529         } else {
5530             UpdateTransaction(mtx, i, sigdata);
5531         }
5532     }
5533     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5534
5535     if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5536     {
5537         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5538     }
5539
5540     // including definitions and claims thread
5541     return UniValue(wtx.GetHash().GetHex());
5542 }
5543
5544 UniValue updateidentity(const UniValue& params, bool fHelp)
5545 {
5546     if (fHelp || params.size() < 1 || params.size() > 2)
5547     {
5548         throw runtime_error(
5549             "updateidentity \"jsonidentity\" (returntx)\n"
5550             "\n\n"
5551
5552             "\nArguments\n"
5553             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
5554
5555             "\nResult:\n"
5556
5557             "\nExamples:\n"
5558             + HelpExampleCli("updateidentity", "\'{\"name\" : \"myname\"}\'")
5559             + HelpExampleRpc("updateidentity", "\'{\"name\" : \"myname\"}\'")
5560         );
5561     }
5562
5563     CheckIdentityAPIsValid();
5564
5565     // get identity
5566     bool returnTx = false;
5567     CIdentity newID(params[0]);
5568
5569     if (!newID.IsValid())
5570     {
5571         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
5572     }
5573
5574     if (params.size() > 1)
5575     {
5576         returnTx = uni_get_bool(params[1], false);
5577     }
5578
5579     CTxIn idTxIn;
5580     CIdentity oldID;
5581     uint32_t idHeight;
5582
5583     LOCK2(cs_main, pwalletMain->cs_wallet);
5584
5585     if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
5586     {
5587         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
5588     }
5589
5590     // create the identity definition transaction
5591     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5592     CWalletTx wtx;
5593
5594     CReserveKey reserveKey(pwalletMain);
5595     CAmount fee;
5596     int nChangePos;
5597     string failReason;
5598
5599     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5600     {
5601         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
5602     }
5603
5604     CMutableTransaction mtx(wtx);
5605     mtx.vin.push_back(idTxIn);
5606     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5607
5608     // now sign
5609     CCoinsViewCache view(pcoinsTip);
5610     for (int i = 0; i < wtx.vin.size(); i++)
5611     {
5612         bool signSuccess;
5613         SignatureData sigdata;
5614
5615         CCoins coins;
5616         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5617         {
5618             break;
5619         }
5620
5621         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5622
5623         signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5624
5625         if (!signSuccess)
5626         {
5627             LogPrintf("%s: failure to sign identity update tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5628             printf("%s: failure to sign identity update tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5629             throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5630         } else {
5631             UpdateTransaction(mtx, i, sigdata);
5632         }
5633     }
5634     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5635
5636     if (returnTx)
5637     {
5638         return EncodeHexTx(wtx);
5639     }
5640     else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5641     {
5642         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5643     }
5644     return wtx.GetHash().GetHex();
5645 }
5646
5647 UniValue revokeidentity(const UniValue& params, bool fHelp)
5648 {
5649     if (fHelp || params.size() < 1 || params.size() > 2)
5650     {
5651         throw runtime_error(
5652             "revokeidentity \"nameorID\" (returntx)\n"
5653             "\n\n"
5654
5655             "\nArguments\n"
5656             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
5657
5658             "\nResult:\n"
5659
5660             "\nExamples:\n"
5661             + HelpExampleCli("revokeidentity", "\"nameorID\"")
5662             + HelpExampleRpc("revokeidentity", "\"nameorID\"")
5663         );
5664     }
5665
5666     CheckIdentityAPIsValid();
5667
5668     // get identity
5669     bool returnTx = false;
5670     CTxDestination idDest = DecodeDestination(uni_get_str(params[0]));
5671
5672     if (idDest.which() != COptCCParams::ADDRTYPE_ID)
5673     {
5674         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
5675     }
5676
5677     CIdentityID idID(GetDestinationID(idDest));
5678
5679     if (params.size() > 1)
5680     {
5681         returnTx = uni_get_bool(params[1], false);
5682     }
5683
5684     CTxIn idTxIn;
5685     CIdentity oldID;
5686     uint32_t idHeight;
5687
5688     LOCK2(cs_main, pwalletMain->cs_wallet);
5689
5690     if (!(oldID = CIdentity::LookupIdentity(idID, 0, &idHeight, &idTxIn)).IsValid())
5691     {
5692         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + EncodeDestination(idID));
5693     }
5694
5695     CIdentity newID(oldID);
5696     newID.Revoke();
5697
5698     // create the identity definition transaction
5699     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5700     CWalletTx wtx;
5701
5702     CReserveKey reserveKey(pwalletMain);
5703     CAmount fee;
5704     int nChangePos;
5705     string failReason;
5706
5707     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5708     {
5709         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
5710     }
5711     CMutableTransaction mtx(wtx);
5712
5713     // add the spend of the last ID transaction output
5714     mtx.vin.push_back(idTxIn);
5715
5716     // all of the reservation output is actually the fee offer, so zero the output
5717     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5718
5719     // now sign
5720     CCoinsViewCache view(pcoinsTip);
5721     for (int i = 0; i < wtx.vin.size(); i++)
5722     {
5723         bool signSuccess;
5724         SignatureData sigdata;
5725
5726         CCoins coins;
5727         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5728         {
5729             break;
5730         }
5731
5732         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5733
5734         signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5735
5736         if (!signSuccess)
5737         {
5738             LogPrintf("%s: failure to sign identity revocation tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5739             printf("%s: failure to sign identity revocation tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5740             throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5741         } else {
5742             UpdateTransaction(mtx, i, sigdata);
5743         }
5744     }
5745     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5746
5747     if (returnTx)
5748     {
5749         return EncodeHexTx(wtx);
5750     }
5751     else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5752     {
5753         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5754     }
5755     return wtx.GetHash().GetHex();
5756 }
5757
5758 UniValue recoveridentity(const UniValue& params, bool fHelp)
5759 {
5760     if (fHelp || params.size() < 1 || params.size() > 2)
5761     {
5762         throw runtime_error(
5763             "recoveridentity \"jsonidentity\" (returntx)\n"
5764             "\n\n"
5765
5766             "\nArguments\n"
5767             "       \"returntx\"                        (bool,   optional) defaults to false and transaction is sent, if true, transaction is signed by this wallet and returned\n"
5768
5769             "\nResult:\n"
5770
5771             "\nExamples:\n"
5772             + HelpExampleCli("recoveridentity", "\'{\"name\" : \"myname\"}\'")
5773             + HelpExampleRpc("recoveridentity", "\'{\"name\" : \"myname\"}\'")
5774         );
5775     }
5776     CheckIdentityAPIsValid();
5777
5778     // get identity
5779     bool returnTx = false;
5780     CIdentity newID(params[0]);
5781
5782     if (!newID.IsValid())
5783     {
5784         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid JSON ID parameter");
5785     }
5786
5787     if (params.size() > 1)
5788     {
5789         returnTx = uni_get_bool(params[1], false);
5790     }
5791
5792     CTxIn idTxIn;
5793     CIdentity oldID;
5794     uint32_t idHeight;
5795
5796     LOCK2(cs_main, pwalletMain->cs_wallet);
5797
5798     if (!(oldID = CIdentity::LookupIdentity(newID.GetID(), 0, &idHeight, &idTxIn)).IsValid())
5799     {
5800         throw JSONRPCError(RPC_INVALID_PARAMETER, "ID not found " + newID.ToUniValue().write());
5801     }
5802
5803     if (!oldID.IsRevoked())
5804     {
5805         throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity must be revoked in order to recover : " + newID.name);
5806     }
5807
5808     newID.flags &= ~CIdentity::FLAG_REVOKED;
5809
5810     // create the identity definition transaction
5811     std::vector<CRecipient> outputs = std::vector<CRecipient>({{newID.IdentityUpdateOutputScript(), 0, false}});
5812     CWalletTx wtx;
5813
5814     CReserveKey reserveKey(pwalletMain);
5815     CAmount fee;
5816     int nChangePos;
5817     string failReason;
5818
5819     if (!pwalletMain->CreateTransaction(outputs, wtx, reserveKey, fee, nChangePos, failReason, nullptr, false))
5820     {
5821         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Unable to create update transaction: " + failReason);
5822     }
5823     CMutableTransaction mtx(wtx);
5824
5825     // add the spend of the last ID transaction output
5826     mtx.vin.push_back(idTxIn);
5827
5828     // all of the reservation output is actually the fee offer, so zero the output
5829     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5830
5831     // now sign
5832     CCoinsViewCache view(pcoinsTip);
5833     for (int i = 0; i < wtx.vin.size(); i++)
5834     {
5835         bool signSuccess;
5836         SignatureData sigdata;
5837
5838         CCoins coins;
5839         if (!(view.GetCoins(wtx.vin[i].prevout.hash, coins) && coins.IsAvailable(wtx.vin[i].prevout.n)))
5840         {
5841             break;
5842         }
5843
5844         CAmount value = coins.vout[wtx.vin[i].prevout.n].nValue;
5845
5846         signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &wtx, i, value, coins.vout[wtx.vin[i].prevout.n].scriptPubKey), coins.vout[wtx.vin[i].prevout.n].scriptPubKey, sigdata, CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()));
5847
5848         if (!signSuccess)
5849         {
5850             LogPrintf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5851             printf("%s: failure to sign identity recovery tx for input %d from output %d of %s\n", __func__, i, wtx.vin[i].prevout.n, wtx.vin[i].prevout.hash.GetHex().c_str());
5852             throw JSONRPCError(RPC_TRANSACTION_ERROR, "Failed to sign transaction");
5853         } else {
5854             UpdateTransaction(mtx, i, sigdata);
5855         }
5856     }
5857     *static_cast<CTransaction*>(&wtx) = CTransaction(mtx);
5858
5859     if (returnTx)
5860     {
5861         return EncodeHexTx(wtx);
5862     }
5863     else if (!pwalletMain->CommitTransaction(wtx, reserveKey))
5864     {
5865         throw JSONRPCError(RPC_TRANSACTION_ERROR, "Could not commit transaction " + wtx.GetHash().GetHex());
5866     }
5867     return wtx.GetHash().GetHex();
5868 }
5869
5870 UniValue getidentity(const UniValue& params, bool fHelp)
5871 {
5872     if (fHelp || params.size() != 1)
5873     {
5874         throw runtime_error(
5875             "getidentity \"name\"\n"
5876             "\n\n"
5877
5878             "\nArguments\n"
5879
5880             "\nResult:\n"
5881
5882             "\nExamples:\n"
5883             + HelpExampleCli("getidentity", "\"name@\"")
5884             + HelpExampleRpc("getidentity", "\"name@\"")
5885         );
5886     }
5887
5888     CheckIdentityAPIsValid();
5889
5890     CTxDestination idID = DecodeDestination(uni_get_str(params[0]));
5891     if (idID.which() != COptCCParams::ADDRTYPE_ID)
5892     {
5893         throw JSONRPCError(RPC_INVALID_PARAMETER, "Identity parameter must be valid friendly name or identity address: \"" + uni_get_str(params[0]) + "\"");
5894     }
5895
5896     CTxIn idTxIn;
5897     uint32_t height;
5898
5899     CIdentity identity;
5900     bool canSign = false, canSpend = false;
5901
5902     if (pwalletMain)
5903     {
5904         LOCK(pwalletMain->cs_wallet);
5905         uint256 txID;
5906         std::pair<CIdentityMapKey, CIdentityMapValue> keyAndIdentity;
5907         if (pwalletMain->GetIdentity(GetDestinationID(idID), keyAndIdentity))
5908         {
5909             canSign = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SIGN;
5910             canSpend = keyAndIdentity.first.flags & keyAndIdentity.first.CAN_SPEND;
5911             identity = static_cast<CIdentity>(keyAndIdentity.second);
5912         }
5913     }
5914
5915     LOCK(cs_main);
5916
5917     uint160 identityID = GetDestinationID(idID);
5918     identity = CIdentity::LookupIdentity(CIdentityID(identityID), 0, &height, &idTxIn);
5919
5920     if (!identity.IsValid() && identityID == VERUS_CHAINID)
5921     {
5922         std::vector<CTxDestination> primary({CTxDestination(CKeyID(uint160()))});
5923         std::vector<std::pair<uint160, uint256>> contentmap;
5924         identity = CIdentity(CIdentity::VERSION_PBAAS, 
5925                              CIdentity::FLAG_ACTIVECURRENCY,
5926                              primary, 
5927                              1, 
5928                              ConnectedChains.ThisChain().parent,
5929                              VERUS_CHAINNAME,
5930                              contentmap,
5931                              ConnectedChains.ThisChain().GetID(),
5932                              ConnectedChains.ThisChain().GetID(),
5933                              std::vector<libzcash::SaplingPaymentAddress>());
5934     }
5935
5936     UniValue ret(UniValue::VOBJ);
5937
5938     uint160 parent;
5939     if (identity.IsValid() && identity.name == CleanName(identity.name, parent, true))
5940     {
5941         ret.push_back(Pair("identity", identity.ToUniValue()));
5942         ret.push_back(Pair("status", identity.IsRevoked() ? "revoked" : "active"));
5943         ret.push_back(Pair("canspendfor", canSpend));
5944         ret.push_back(Pair("cansignfor", canSign));
5945         ret.push_back(Pair("blockheight", (int64_t)height));
5946         ret.push_back(Pair("txid", idTxIn.prevout.hash.GetHex()));
5947         ret.push_back(Pair("vout", (int32_t)idTxIn.prevout.n));
5948         return ret;
5949     }
5950     else
5951     {
5952         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Identity not found");
5953     }
5954 }
5955
5956 UniValue IdentityPairToUni(const std::pair<CIdentityMapKey, CIdentityMapValue> &identity)
5957 {
5958     UniValue oneID(UniValue::VOBJ);
5959
5960     if (identity.first.IsValid() && identity.second.IsValid())
5961     {
5962         oneID.push_back(Pair("identity", identity.second.ToUniValue()));
5963         oneID.push_back(Pair("blockheight", (int64_t)identity.first.blockHeight));
5964         oneID.push_back(Pair("txid", identity.second.txid.GetHex()));
5965         if (identity.second.IsRevoked())
5966         {
5967             oneID.push_back(Pair("status", "revoked"));
5968             oneID.push_back(Pair("canspendfor", bool(0)));
5969             oneID.push_back(Pair("cansignfor", bool(0)));
5970         }
5971         else
5972         {
5973             oneID.push_back(Pair("status", "active"));
5974             oneID.push_back(Pair("canspendfor", bool(identity.first.flags & identity.first.CAN_SPEND)));
5975             oneID.push_back(Pair("cansignfor", bool(identity.first.flags & identity.first.CAN_SIGN)));
5976         }
5977     }
5978     return oneID;
5979 }
5980
5981 UniValue listidentities(const UniValue& params, bool fHelp)
5982 {
5983     if (fHelp || params.size() > 3)
5984     {
5985         throw runtime_error(
5986             "listidentities (includecansign) (includewatchonly)\n"
5987             "\n\n"
5988
5989             "\nArguments\n"
5990             "    \"includecanspend\"    (bool, optional, default=true)    Include identities for which we can spend/authorize\n"
5991             "    \"includecansign\"     (bool, optional, default=true)    Include identities that we can only sign for but not spend\n"
5992             "    \"includewatchonly\"   (bool, optional, default=false)   Include identities that we can neither sign nor spend, but are either watched or are co-signers with us\n"
5993
5994             "\nResult:\n"
5995
5996             "\nExamples:\n"
5997             + HelpExampleCli("listidentities", "\'{\"name\" : \"myname\"}\'")
5998             + HelpExampleRpc("listidentities", "\'{\"name\" : \"myname\"}\'")
5999         );
6000     }
6001
6002     CheckIdentityAPIsValid();
6003
6004     std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> mine, imsigner, notmine;
6005
6006     bool includeCanSpend = params.size() > 0 ? uni_get_bool(params[0], true) : true;
6007     bool includeCanSign = params.size() > 1 ? uni_get_bool(params[1], true) : true;
6008     bool includeWatchOnly = params.size() > 2 ? uni_get_bool(params[2], false) : false;
6009
6010     if (pwalletMain->GetIdentities(mine, imsigner, notmine))
6011     {
6012         UniValue ret(UniValue::VARR);
6013         if (includeCanSpend)
6014         {
6015             for (auto identity : mine)
6016             {
6017                 uint160 parent;
6018                 if (identity.second.IsValid() && identity.second.name == CleanName(identity.second.name, parent, true))
6019                 {
6020                     ret.push_back(IdentityPairToUni(identity));
6021                 }
6022             }
6023         }
6024         if (includeCanSign)
6025         {
6026             for (auto identity : imsigner)
6027             {
6028                 uint160 parent;
6029                 if (identity.second.IsValid() && identity.second.name == CleanName(identity.second.name, parent, true))
6030                 {
6031                     ret.push_back(IdentityPairToUni(identity));
6032                 }
6033             }
6034         }
6035         if (includeWatchOnly)
6036         {
6037             for (auto identity : notmine)
6038             {
6039                 uint160 parent;
6040                 if (identity.second.IsValid() && identity.second.name == CleanName(identity.second.name, parent, true))
6041                 {
6042                     ret.push_back(IdentityPairToUni(identity));
6043                 }
6044             }
6045         }
6046         return ret;
6047     }
6048     else
6049     {
6050         return NullUniValue;
6051     }
6052 }
6053
6054 UniValue addmergedblock(const UniValue& params, bool fHelp)
6055 {
6056     if (fHelp || params.size() != 5)
6057     {
6058         throw runtime_error(
6059             "addmergedblock \"hexdata\" ( \"jsonparametersobject\" )\n"
6060             "\nAdds a fully prepared block and its header to the current merge mining queue of this daemon.\n"
6061             "Parameters determine the action to take if adding this block would exceed the available merge mining slots.\n"
6062             "Default action to take if adding would exceed available space is to replace the choice with the least ROI if this block provides more.\n"
6063
6064             "\nArguments\n"
6065             "1. \"hexdata\"                     (string, required) the hex-encoded, complete, unsolved block data to add. nTime, and nSolution are replaced.\n"
6066             "2. \"name\"                        (string, required) chain name symbol\n"
6067             "3. \"rpchost\"                     (string, required) host address for RPC connection\n"
6068             "4. \"rpcport\"                     (int,    required) port address for RPC connection\n"
6069             "5. \"userpass\"                    (string, required) credentials for login to RPC\n"
6070
6071             "\nResult:\n"
6072             "\"deserialize-invalid\" - block could not be deserialized and was rejected as invalid\n"
6073             "\"blocksfull\"          - block did not exceed others in estimated ROI, and there was no room for an additional merge mined block\n"
6074
6075             "\nExamples:\n"
6076             + HelpExampleCli("addmergedblock", "\"hexdata\" \'{\"currencyid\" : \"hexstring\", \"rpchost\" : \"127.0.0.1\", \"rpcport\" : portnum}\'")
6077             + HelpExampleRpc("addmergedblock", "\"hexdata\" \'{\"currencyid\" : \"hexstring\", \"rpchost\" : \"127.0.0.1\", \"rpcport\" : portnum, \"estimatedroi\" : (verusreward/hashrate)}\'")
6078         );
6079     }
6080
6081     CheckPBaaSAPIsValid();
6082
6083     // check to see if we should replace any existing block or add a new one. if so, add this to the merge mine vector
6084     string name = params[1].get_str();
6085     if (name == "")
6086     {
6087         throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide chain name to merge mine");
6088     }
6089
6090     string rpchost = params[2].get_str();
6091     int32_t rpcport = params[3].get_int();
6092     string rpcuserpass = params[4].get_str();
6093
6094     if (rpchost == "" || rpcport == 0 || rpcuserpass == "")
6095     {
6096         throw JSONRPCError(RPC_INVALID_PARAMETER, "must provide valid RPC connection parameters to merge mine");
6097     }
6098
6099     uint160 chainID = CCrossChainRPCData::GetID(name);
6100
6101     // confirm data from blockchain
6102     CRPCChainData chainData;
6103     CCurrencyDefinition chainDef;
6104     if (ConnectedChains.GetChainInfo(chainID, chainData))
6105     {
6106         chainDef = chainData.chainDefinition;
6107     }
6108
6109     if (!chainDef.IsValid() && !GetCurrencyDefinition(name, chainDef))
6110     {
6111         throw JSONRPCError(RPC_INVALID_PARAMETER, "chain not found");
6112     }
6113
6114     CBlock blk;
6115
6116     if (!DecodeHexBlk(blk, params[0].get_str()))
6117         return "deserialize-invalid";
6118
6119     CPBaaSMergeMinedChainData blkData = CPBaaSMergeMinedChainData(chainDef, rpchost, rpcport, rpcuserpass, blk);
6120
6121     return ConnectedChains.AddMergedBlock(blkData) ? NullUniValue : "blocksfull";
6122 }
6123
6124 UniValue submitmergedblock(const UniValue& params, bool fHelp)
6125 {
6126     if (fHelp || params.size() < 1 || params.size() > 2)
6127         throw runtime_error(
6128             "submitmergedblock \"hexdata\" ( \"jsonparametersobject\" )\n"
6129             "\nAttempts to submit one more more new blocks to one or more networks.\n"
6130             "Each merged block submission may be valid for Verus and/or up to 8 merge mined chains.\n"
6131             "The submitted block consists of a valid block for this chain, along with embedded headers of up to 8 other chains.\n"
6132             "If the hash for this header meets targets of other chains that have been added with 'addmergedblock', this API will\n"
6133             "submit those blocks to the specified URL endpoints with an RPC 'submitblock' request."
6134             "\nAttempts to submit one more more new blocks to one or more networks.\n"
6135             "The 'jsonparametersobject' parameter is currently ignored.\n"
6136             "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
6137
6138             "\nArguments\n"
6139             "1. \"hexdata\"    (string, required) the hex-encoded block data to submit\n"
6140             "2. \"jsonparametersobject\"     (string, optional) object of optional parameters\n"
6141             "    {\n"
6142             "      \"workid\" : \"id\"    (string, optional) if the server provided a workid, it MUST be included with submissions\n"
6143             "    }\n"
6144             "\nResult:\n"
6145             "\"duplicate\" - node already has valid copy of block\n"
6146             "\"duplicate-invalid\" - node already has block, but it is invalid\n"
6147             "\"duplicate-inconclusive\" - node already has block but has not validated it\n"
6148             "\"inconclusive\" - node has not validated the block, it may not be on the node's current best chain\n"
6149             "\"rejected\" - block was rejected as invalid\n"
6150             "For more information on submitblock parameters and results, see: https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki#block-submission\n"
6151             "\nExamples:\n"
6152             + HelpExampleCli("submitblock", "\"mydata\"")
6153             + HelpExampleRpc("submitblock", "\"mydata\"")
6154         );
6155
6156     CheckPBaaSAPIsValid();
6157
6158     CBlock block;
6159     //LogPrintStr("Hex block submission: " + params[0].get_str());
6160     if (!DecodeHexBlk(block, params[0].get_str()))
6161         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
6162
6163     uint256 hash = block.GetHash();
6164     bool fBlockPresent = false;
6165     {
6166         LOCK(cs_main);
6167         BlockMap::iterator mi = mapBlockIndex.find(hash);
6168         if (mi != mapBlockIndex.end()) {
6169             CBlockIndex *pindex = mi->second;
6170             if (pindex)
6171             {
6172                 if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
6173                     return "duplicate";
6174                 if (pindex->nStatus & BLOCK_FAILED_MASK)
6175                     return "duplicate-invalid";
6176                 // Otherwise, we might only have the header - process the block before returning
6177                 fBlockPresent = true;
6178             }
6179         }
6180     }
6181
6182     CValidationState state;
6183     submitblock_StateCatcher sc(block.GetHash());
6184     RegisterValidationInterface(&sc);
6185     //printf("submitblock, height=%d, coinbase sequence: %d, scriptSig: %s\n", chainActive.LastTip()->GetHeight()+1, block.vtx[0].vin[0].nSequence, block.vtx[0].vin[0].scriptSig.ToString().c_str());
6186     bool fAccepted = ProcessNewBlock(1, chainActive.LastTip()->GetHeight()+1, state, Params(), NULL, &block, true, NULL);
6187     UnregisterValidationInterface(&sc);
6188     if (fBlockPresent)
6189     {
6190         if (fAccepted && !sc.found)
6191             return "duplicate-inconclusive";
6192         return "duplicate";
6193     }
6194     if (fAccepted)
6195     {
6196         if (!sc.found)
6197             return "inconclusive";
6198         state = sc.state;
6199     }
6200     return BIP22ValidationResult(state);
6201 }
6202
6203 UniValue getmergedblocktemplate(const UniValue& params, bool fHelp)
6204 {
6205     if (fHelp || params.size() > 1)
6206         throw runtime_error(
6207             "getblocktemplate ( \"jsonrequestobject\" )\n"
6208             "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
6209             "It returns data needed to construct a block to work on.\n"
6210             "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
6211
6212             "\nArguments:\n"
6213             "1. \"jsonrequestobject\"       (string, optional) A json object in the following spec\n"
6214             "     {\n"
6215             "       \"mode\":\"template\"    (string, optional) This must be set to \"template\" or omitted\n"
6216             "       \"capabilities\":[       (array, optional) A list of strings\n"
6217             "           \"support\"           (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n"
6218             "           ,...\n"
6219             "         ]\n"
6220             "     }\n"
6221             "\n"
6222
6223             "\nResult:\n"
6224             "{\n"
6225             "  \"version\" : n,                     (numeric) The block version\n"
6226             "  \"previousblockhash\" : \"xxxx\",    (string) The hash of current highest block\n"
6227             "  \"finalsaplingroothash\" : \"xxxx\", (string) The hash of the final sapling root\n"
6228             "  \"transactions\" : [                 (array) contents of non-coinbase transactions that should be included in the next block\n"
6229             "      {\n"
6230             "         \"data\" : \"xxxx\",          (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
6231             "         \"hash\" : \"xxxx\",          (string) hash/id encoded in little-endian hexadecimal\n"
6232             "         \"depends\" : [              (array) array of numbers \n"
6233             "             n                        (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n"
6234             "             ,...\n"
6235             "         ],\n"
6236             "         \"fee\": n,                   (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n"
6237             "         \"sigops\" : n,               (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n"
6238             "         \"required\" : true|false     (boolean) if provided and true, this transaction must be in the final block\n"
6239             "      }\n"
6240             "      ,...\n"
6241             "  ],\n"
6242 //            "  \"coinbaseaux\" : {                  (json object) data that should be included in the coinbase's scriptSig content\n"
6243 //            "      \"flags\" : \"flags\"            (string) \n"
6244 //            "  },\n"
6245 //            "  \"coinbasevalue\" : n,               (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n"
6246             "  \"coinbasetxn\" : { ... },           (json object) information for coinbase transaction\n"
6247             "  \"target\" : \"xxxx\",               (string) The hash target\n"
6248             "  \"mintime\" : xxx,                   (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n"
6249             "  \"mutable\" : [                      (array of string) list of ways the block template may be changed \n"
6250             "     \"value\"                         (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n"
6251             "     ,...\n"
6252             "  ],\n"
6253             "  \"noncerange\" : \"00000000ffffffff\",   (string) A range of valid nonces\n"
6254             "  \"sigoplimit\" : n,                 (numeric) limit of sigops in blocks\n"
6255             "  \"sizelimit\" : n,                  (numeric) limit of block size\n"
6256             "  \"curtime\" : ttt,                  (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n"
6257             "  \"bits\" : \"xxx\",                 (string) compressed target of next block\n"
6258             "  \"height\" : n                      (numeric) The height of the next block\n"
6259             "}\n"
6260
6261             "\nExamples:\n"
6262             + HelpExampleCli("getblocktemplate", "")
6263             + HelpExampleRpc("getblocktemplate", "")
6264          );
6265
6266     CheckPBaaSAPIsValid();
6267
6268     LOCK(cs_main);
6269
6270     // Wallet or miner address is required because we support coinbasetxn
6271     if (GetArg("-mineraddress", "").empty()) {
6272 #ifdef ENABLE_WALLET
6273         if (!pwalletMain) {
6274             throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Wallet disabled and -mineraddress not set");
6275         }
6276 #else
6277         throw JSONRPCError(RPC_METHOD_NOT_FOUND, "verusd compiled without wallet and -mineraddress not set");
6278 #endif
6279     }
6280
6281     std::string strMode = "template";
6282     UniValue lpval = NullUniValue;
6283     // TODO: Re-enable coinbasevalue once a specification has been written
6284     bool coinbasetxn = true;
6285     if (params.size() > 0)
6286     {
6287         const UniValue& oparam = params[0].get_obj();
6288         const UniValue& modeval = find_value(oparam, "mode");
6289         if (modeval.isStr())
6290             strMode = modeval.get_str();
6291         else if (modeval.isNull())
6292         {
6293             /* Do nothing */
6294         }
6295         else
6296             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
6297         lpval = find_value(oparam, "longpollid");
6298
6299         if (strMode == "proposal")
6300         {
6301             const UniValue& dataval = find_value(oparam, "data");
6302             if (!dataval.isStr())
6303                 throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
6304
6305             CBlock block;
6306             if (!DecodeHexBlk(block, dataval.get_str()))
6307                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
6308
6309             uint256 hash = block.GetHash();
6310             BlockMap::iterator mi = mapBlockIndex.find(hash);
6311             if (mi != mapBlockIndex.end()) {
6312                 CBlockIndex *pindex = mi->second;
6313                 if (pindex)
6314                 {
6315                     if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
6316                         return "duplicate";
6317                     if (pindex->nStatus & BLOCK_FAILED_MASK)
6318                         return "duplicate-invalid";
6319                 }
6320                 return "duplicate-inconclusive";
6321             }
6322
6323             CBlockIndex* const pindexPrev = chainActive.LastTip();
6324             // TestBlockValidity only supports blocks built on the current Tip
6325             if (block.hashPrevBlock != pindexPrev->GetBlockHash())
6326                 return "inconclusive-not-best-prevblk";
6327             CValidationState state;
6328             TestBlockValidity(state, Params(), block, pindexPrev, false, true);
6329             return BIP22ValidationResult(state);
6330         }
6331     }
6332
6333     if (strMode != "template")
6334         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
6335
6336     bool fvNodesEmpty;
6337     {
6338         LOCK(cs_vNodes);
6339         fvNodesEmpty = vNodes.empty();
6340     }
6341     if (Params().MiningRequiresPeers() && (IsNotInSync() || fvNodesEmpty))
6342     {
6343         throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Cannot get a block template while no peers are connected or chain not in sync!");
6344     }
6345
6346     //if (IsInitialBlockDownload())
6347      //   throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks...");
6348
6349     static unsigned int nTransactionsUpdatedLast;
6350
6351     if (!lpval.isNull())
6352     {
6353         // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
6354         uint256 hashWatchedChain;
6355         boost::system_time checktxtime;
6356         unsigned int nTransactionsUpdatedLastLP;
6357
6358         if (lpval.isStr())
6359         {
6360             // Format: <hashBestChain><nTransactionsUpdatedLast>
6361             std::string lpstr = lpval.get_str();
6362
6363             hashWatchedChain.SetHex(lpstr.substr(0, 64));
6364             nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
6365         }
6366         else
6367         {
6368             // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
6369             hashWatchedChain = chainActive.LastTip()->GetBlockHash();
6370             nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
6371         }
6372
6373         // Release the wallet and main lock while waiting
6374         LEAVE_CRITICAL_SECTION(cs_main);
6375         {
6376             checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);
6377
6378             boost::unique_lock<boost::mutex> lock(csBestBlock);
6379             while (chainActive.LastTip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
6380             {
6381                 if (!cvBlockChange.timed_wait(lock, checktxtime))
6382                 {
6383                     // Timeout: Check transactions for update
6384                     if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
6385                         break;
6386                     checktxtime += boost::posix_time::seconds(10);
6387                 }
6388             }
6389         }
6390         ENTER_CRITICAL_SECTION(cs_main);
6391
6392         if (!IsRPCRunning())
6393             throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
6394         // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
6395     }
6396
6397     // Update block
6398     static CBlockIndex* pindexPrev;
6399     static int64_t nStart;
6400     static CBlockTemplate* pblocktemplate;
6401     if (pindexPrev != chainActive.LastTip() ||
6402         (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
6403     {
6404         // Clear pindexPrev so future calls make a new block, despite any failures from here on
6405         pindexPrev = NULL;
6406
6407         // Store the pindexBest used before CreateNewBlockWithKey, to avoid races
6408         nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
6409         CBlockIndex* pindexPrevNew = chainActive.LastTip();
6410         nStart = GetTime();
6411
6412         // Create new block
6413         if(pblocktemplate)
6414         {
6415             delete pblocktemplate;
6416             pblocktemplate = NULL;
6417         }
6418 #ifdef ENABLE_WALLET
6419         CReserveKey reservekey(pwalletMain);
6420         pblocktemplate = CreateNewBlockWithKey(reservekey,chainActive.LastTip()->GetHeight()+1,KOMODO_MAXGPUCOUNT);
6421 #else
6422         pblocktemplate = CreateNewBlockWithKey();
6423 #endif
6424         if (!pblocktemplate)
6425             throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory or no available utxo for staking");
6426
6427         // Need to update only after we know CreateNewBlockWithKey succeeded
6428         pindexPrev = pindexPrevNew;
6429     }
6430     CBlock* pblock = &pblocktemplate->block; // pointer for convenience
6431
6432     // Update nTime
6433     UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
6434     pblock->nNonce = uint256();
6435
6436     UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
6437
6438     UniValue txCoinbase = NullUniValue;
6439     UniValue transactions(UniValue::VARR);
6440     map<uint256, int64_t> setTxIndex;
6441     int i = 0;
6442     BOOST_FOREACH (const CTransaction& tx, pblock->vtx) {
6443         uint256 txHash = tx.GetHash();
6444         setTxIndex[txHash] = i++;
6445
6446         if (tx.IsCoinBase() && !coinbasetxn)
6447             continue;
6448
6449         UniValue entry(UniValue::VOBJ);
6450
6451         entry.push_back(Pair("data", EncodeHexTx(tx)));
6452
6453         entry.push_back(Pair("hash", txHash.GetHex()));
6454
6455         UniValue deps(UniValue::VARR);
6456         BOOST_FOREACH (const CTxIn &in, tx.vin)
6457         {
6458             if (setTxIndex.count(in.prevout.hash))
6459                 deps.push_back(setTxIndex[in.prevout.hash]);
6460         }
6461         entry.push_back(Pair("depends", deps));
6462
6463         int index_in_template = i - 1;
6464         entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
6465         entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
6466
6467         if (tx.IsCoinBase()) {
6468             // Show founders' reward if it is required
6469             //if (pblock->vtx[0].vout.size() > 1) {
6470                 // Correct this if GetBlockTemplate changes the order
6471             //    entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue));
6472             //}
6473             CAmount nReward = GetBlockSubsidy(chainActive.LastTip()->GetHeight()+1, Params().GetConsensus());
6474             entry.push_back(Pair("coinbasevalue", nReward));
6475             entry.push_back(Pair("required", true));
6476             txCoinbase = entry;
6477         } else
6478             transactions.push_back(entry);
6479     }
6480
6481     UniValue aux(UniValue::VOBJ);
6482     aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
6483
6484     arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
6485
6486     static UniValue aMutable(UniValue::VARR);
6487     if (aMutable.empty())
6488     {
6489         aMutable.push_back("time");
6490         aMutable.push_back("transactions");
6491         aMutable.push_back("prevblock");
6492     }
6493
6494     UniValue result(UniValue::VOBJ);
6495     result.push_back(Pair("capabilities", aCaps));
6496     result.push_back(Pair("version", pblock->nVersion));
6497     result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
6498     result.push_back(Pair("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex()));
6499     result.push_back(Pair("transactions", transactions));
6500     if (coinbasetxn) {
6501         assert(txCoinbase.isObject());
6502         result.push_back(Pair("coinbasetxn", txCoinbase));
6503     } else {
6504         result.push_back(Pair("coinbaseaux", aux));
6505         result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
6506     }
6507     result.push_back(Pair("longpollid", chainActive.LastTip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
6508     if ( ASSETCHAINS_STAKED != 0 )
6509     {
6510         arith_uint256 POWtarget; int32_t PoSperc;
6511         POWtarget = komodo_PoWtarget(&PoSperc,hashTarget,(int32_t)(pindexPrev->GetHeight()+1),ASSETCHAINS_STAKED);
6512         result.push_back(Pair("target", POWtarget.GetHex()));
6513         result.push_back(Pair("PoSperc", (int64_t)PoSperc));
6514         result.push_back(Pair("ac_staked", (int64_t)ASSETCHAINS_STAKED));
6515         result.push_back(Pair("origtarget", hashTarget.GetHex()));
6516     } else result.push_back(Pair("target", hashTarget.GetHex()));
6517     result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));
6518     result.push_back(Pair("mutable", aMutable));
6519     result.push_back(Pair("noncerange", "00000000ffffffff"));
6520     result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
6521     result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
6522     result.push_back(Pair("curtime", pblock->GetBlockTime()));
6523     result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
6524     result.push_back(Pair("height", (int64_t)(pindexPrev->GetHeight()+1)));
6525
6526     //fprintf(stderr,"return complete template\n");
6527     return result;
6528 }
6529
6530 static const CRPCCommand commands[] =
6531 { //  category              name                      actor (function)         okSafeMode
6532   //  --------------------- ------------------------  -----------------------  ----------
6533     { "identity",     "registernamecommitment",       &registernamecommitment, true  },
6534     { "identity",     "registeridentity",             &registeridentity,       true  },
6535     { "identity",     "updateidentity",               &updateidentity,         true  },
6536     { "identity",     "revokeidentity",               &revokeidentity,         true  },
6537     { "identity",     "recoveridentity",              &recoveridentity,        true  },
6538     { "identity",     "getidentity",                  &getidentity,            true  },
6539     { "identity",     "listidentities",               &listidentities,         true  },
6540     { "multichain",   "definecurrency",               &definecurrency,         true  },
6541     { "multichain",   "listcurrencies",               &listcurrencies,         true  },
6542     { "multichain",   "getcurrency",                  &getcurrency,            true  },
6543     { "multichain",   "getnotarizationdata",          &getnotarizationdata,    true  },
6544     { "multichain",   "getcrossnotarization",         &getcrossnotarization,   true  },
6545     { "multichain",   "submitacceptednotarization",   &submitacceptednotarization, true },
6546     { "multichain",   "paynotarizationrewards",       &paynotarizationrewards, true  },
6547     { "multichain",   "getinitialcurrencystate",      &getinitialcurrencystate, true  },
6548     { "multichain",   "getcurrencystate",             &getcurrencystate,       true  },
6549     { "multichain",   "getsaplingtree",               &getsaplingtree,         true  },
6550     { "multichain",   "sendcurrency",                 &sendcurrency,           true  },
6551     { "multichain",   "getpendingtransfers",          &getpendingtransfers,    true  },
6552     { "multichain",   "getexports",                   &getexports,             true  },
6553     { "multichain",   "getimports",                   &getimports,             true  },
6554     { "multichain",   "reserveexchange",              &reserveexchange,        true  },
6555     { "multichain",   "getlatestimportsout",          &getlatestimportsout,    true  },
6556     { "multichain",   "getlastimportin",              &getlastimportin,        true  },
6557     { "multichain",   "refundfailedlaunch",           &refundfailedlaunch,     true  },
6558     { "multichain",   "refundfailedlaunch",           &refundfailedlaunch,     true  },
6559     { "multichain",   "getmergedblocktemplate",       &getmergedblocktemplate, true  },
6560     { "multichain",   "addmergedblock",               &addmergedblock,         true  }
6561 };
6562
6563 void RegisterPBaaSRPCCommands(CRPCTable &tableRPC)
6564 {
6565     for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
6566         tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
6567 }
This page took 0.405243 seconds and 4 git commands to generate.