]> Git Repo - VerusCoin.git/commitdiff
Create import transactions with all outputs
authorMichael Toutonghi <[email protected]>
Sun, 4 Aug 2019 00:47:51 +0000 (17:47 -0700)
committerMichael Toutonghi <[email protected]>
Sun, 4 Aug 2019 00:47:51 +0000 (17:47 -0700)
src/miner.cpp
src/pbaas/pbaas.cpp
src/pbaas/pbaas.h
src/pbaas/reserves.cpp
src/pbaas/reserves.h
src/rpc/pbaasrpc.cpp

index 923edf14afc5690a2899864d1624419a0933811b..b39f8e5789aa9d6815e28b11683ad284f93d7be1 100644 (file)
@@ -248,26 +248,26 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
     std::vector<pair<int, CScript>> minerOutputs({make_pair((int)1, scriptPubKeyIn)});
 
     // TODO: when we accept a parameter of the minerOutputs vector, remove this comment but not the check
-    int64_t shareCheck = 0;
-    for (auto output : minerOutputs)
-    {
-        shareCheck += output.first;
-    }
-    if (shareCheck < 0 || shareCheck > INT_MAX)
+    CTxDestination firstDestination;
+    if (!ConnectedChains.SetLatestMiningOutputs(minerOutputs, firstDestination))
     {
-        fprintf(stderr,"Invalid miner outputs share specifications\n");
+        fprintf(stderr,"%s: Must have valid miner outputs, including script with valid PK or PKH destination.\n");
         return NULL;
     }
 
-    CPubKey pk = CPubKey();
-    std::vector<std::vector<unsigned char>> vAddrs;
-    txnouttype txT;
-    if (minerOutputs.size() && Solver(minerOutputs[0].second, txT, vAddrs))
+    int64_t shareCheck = 0;
+    for (auto output : minerOutputs)
     {
-        if (txT == TX_PUBKEY)
-            pk = CPubKey(vAddrs[0]);
+        shareCheck += output.first;
+        if (shareCheck < 0 || shareCheck > INT_MAX)
+        {
+            fprintf(stderr,"Invalid miner outputs share specifications\n");
+            return NULL;
+        }
     }
 
+    CPubKey pk = boost::apply_visitor<GetPubKeyForPubKey>(GetPubKeyForPubKey(), firstDestination);
+
     uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
     //fprintf(stderr,"create new block\n");
     // Create new block
@@ -460,190 +460,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             }
         }
 
-        // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
-        {
-            multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
-
-            CKeyID exportKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetChainID(), EVAL_CROSSCHAIN_EXPORT));
-
-            // get all available transfer outputs to aggregate into export transactions
-            if (GetUnspentChainTransfers(transferOutputs))
-            {
-                std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
-                std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
-
-                // we need unspent export outputs to export
-                if (GetUnspentChainExports(exportOutputs))
-                {
-                    uint160 lastChain;
-
-                    // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
-                    // export of that type, or there are MIN_INPUTS or more outputs to aggregate
-                    for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
-                    {
-                        // get chain target and see if it is the same
-                        if (lastChain.IsNull() || it->first == lastChain)
-                        {
-                            txInputs.push_back(it->second);
-                            lastChain = it->first;
-                        }
-                        else
-                        {
-                            auto recentExportIt = exportOutputs.find(lastChain);
-
-                            if (recentExportIt != exportOutputs.end() &&
-                                ((nHeight - recentExportIt->second.first) >= CCrossChainExport::MIN_BLOCKS) ||
-                                (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
-                            {
-                                // make one or more transactions that spends the last export and all possible cross chain transfers
-                                while (txInputs.size())
-                                {
-                                    TransactionBuilder tb(consensusParams, nHeight);
-                                    boost::optional<CTransaction> oneExport;
-
-                                    int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
-
-                                    int inputsLeft = txInputs.size() - numInputs;
-
-                                    if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
-                                    {
-                                        inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
-                                        numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
-                                        assert(numInputs > 0);
-                                    }
-
-                                    // each time through, we make one export transaction with the remainder or a subset of the
-                                    // reserve transfer inputs. inputs can be:
-                                    // 1. transfers of reserve for fractional reserve chains
-                                    // 2. pre-conversions for pre-launch participation in the premine
-                                    // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
-                                    //
-                                    // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
-                                    // or reserve token inputs.
-                                    //
-                                    // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
-                                    // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
-                                    // as part of the import process
-                                    // 
-                                    // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain. 
-                                    // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the 
-                                    // remaining Verus reserve coin considered burned.
-                                    //
-                                    CAmount totalTxFees = 0;
-                                    CAmount totalAmount = 0;
-                                    std::vector<CBaseChainObject *> chainObjects;
-
-                                    // first, we must add the export output from the current export thread to this chain
-                                    if (oneExport.has_value())
-                                    {
-                                        // spend the last export transaction output
-                                        CTransaction &tx = oneExport.get();
-                                        COptCCParams p;
-                                        int j;
-                                        for (j = 0; j < tx.vout.size(); j++)
-                                        {
-                                            if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
-                                            {
-                                                break;
-                                            }
-                                        }
-
-                                        // had to be found and valid if we made the tx
-                                        assert(j < tx.vout.size() && p.IsValid());
-
-                                        tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
-                                    }
-                                    else
-                                    {
-                                        // spend the recentExportIt output
-                                        tb.AddTransparentInput(recentExportIt->second.second.txIn.prevout, 
-                                                               recentExportIt->second.second.scriptPubKey, 
-                                                               recentExportIt->second.second.nValue);
-                                    }
-
-                                    for (int j = 0; j < numInputs; j++)
-                                    {
-                                        tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
-                                        totalTxFees += txInputs[j].second.nFees;
-                                        totalAmount += txInputs[j].second.nValue;
-                                        chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
-                                    }
-
-                                    CScript opRet = StoreOpRetArray(chainObjects);
-                                    DeleteOpRetObjects(chainObjects);
-
-                                    CCcontract_info CC;
-                                    CCcontract_info *cp;
-                                    cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
-
-                                    CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
-
-                                    // send zero to a cross chain export output of the specific chain
-                                    std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
-
-                                    CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
-                                    CAmount exportFees = ccx.CalculateExportFee();
-
-                                    CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, 
-                                                                      0,
-                                                                      pk,
-                                                                      dests,
-                                                                      ccx);
-
-                                    tb.AddTransparentOutput(exportOut.scriptPubKey, 0);
-
-                                    // if we are on Verus chain, send all native funds, less fees to reserve deposit CC, which is equivalent to the reserve account
-                                    // on a PBaaS reserve chain, input is burned
-                                    if (isVerusActive)
-                                    {
-                                        cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
-                                        pk = CPubKey(ParseHex(CC.CChexstr));
-
-                                        // send the entire amount, less fees taken on this chain only, to a reserve transfer output of the specific chain
-                                        dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
-
-                                        CReserveOutput ro(CReserveOutput::VALID, totalAmount - ccx.CalculateExportFee());
-
-                                        CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT, 
-                                                                             ro.nValue,
-                                                                             pk,
-                                                                             dests,
-                                                                             ro);
-
-                                        tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
-                                    }
-
-                                    tb.AddOpRet(opRet);
-
-                                    boost::optional<CTransaction> newExport = tb.Build();
-
-                                    if (newExport.has_value())
-                                    {
-                                        // replace the last one only if we have a valid new one
-                                        oneExport = newExport;
-                                        CTransaction &tx = oneExport.get();
-
-                                        LOCK2(cs_main, mempool.cs);
-
-                                        // don't remove conflicts for now
-                                        //std::list<CTransaction> removed;
-                                        //mempool.removeConflicts(tx, removed);
-
-                                        // add to mem pool and relay
-                                        if (myAddtomempool(tx))
-                                        {
-                                            RelayTransaction(tx);
-                                        }
-                                    }
-                                    // erase the inputs we've attempted to spend
-                                    txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
+        ConnectedChains.AggregateChainTransfers(firstDestination, nHeight);
 
         //
         // Now start solving the block
@@ -776,7 +593,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         CCcontract_info CC;
         CCcontract_info *cp;
         vector<CTxDestination> vKeys;
-        CPubKey pk;
+        CPubKey pkCC;
 
         // Create coinbase tx and set up the null input with height
         CMutableTransaction coinbaseTx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
@@ -885,10 +702,10 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 cp = CCinit(&CC, EVAL_PBAASDEFINITION);
 
                 // send this to EVAL_PBAASDEFINITION address as a destination, locked by the default pubkey
-                pk = CPubKey(ParseHex(CC.CChexstr));
+                pkCC = CPubKey(ParseHex(CC.CChexstr));
                 vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_PBAASDEFINITION)));
                 thisChain.preconverted = currencyState.ReserveIn;   // update known, preconverted amount
-                chainDefinitionOut = MakeCC1of1Vout(EVAL_PBAASDEFINITION, 0, pk, vKeys, thisChain);
+                chainDefinitionOut = MakeCC1of1Vout(EVAL_PBAASDEFINITION, 0, pkCC, vKeys, thisChain);
                 coinbaseTx.vout.push_back(chainDefinitionOut);
 
                 // import - only spendable for reserve currency or currency with preconversion to allow import of conversions, this output will include
@@ -898,22 +715,25 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 vKeys.clear();
                 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
 
-                pk = CPubKey(ParseHex(CC.CChexstr));
-                vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_CROSSCHAIN_IMPORT)));
+                pkCC = CPubKey(ParseHex(CC.CChexstr));
+
+                // import thread is specific to the chain importing from
+                vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.notaryChain.GetChainID(), EVAL_CROSSCHAIN_IMPORT)));
 
                 importThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, 
-                                                 currencyState.ReserveToNative(thisChain.preconverted, thisChain.conversion), pk, vKeys, 
+                                                 currencyState.ReserveToNative(thisChain.preconverted, thisChain.conversion), pkCC, vKeys, 
                                                  CCrossChainImport(ConnectedChains.NotaryChain().GetChainID(), 0));
+
                 coinbaseTx.vout.push_back(importThreadOut);
 
                 // export - currently only spendable for reserve currency, but added for future capabilities
                 vKeys.clear();
                 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
 
-                pk = CPubKey(ParseHex(CC.CChexstr));
+                pkCC = CPubKey(ParseHex(CC.CChexstr));
                 vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_CROSSCHAIN_EXPORT)));
 
-                exportThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, 0, pk, vKeys, 
+                exportThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, 0, pkCC, vKeys, 
                                                  CCrossChainExport(ConnectedChains.NotaryChain().GetChainID(), 0, 0, 0));
                 coinbaseTx.vout.push_back(exportThreadOut);
             }
@@ -1015,7 +835,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                     cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
 
                     // send this to EVAL_EARNEDNOTARIZATION address as a destination, locked by the default pubkey
-                    CPubKey pk(ParseHex(cp->CChexstr));
+                    pkCC = CPubKey(ParseHex(cp->CChexstr));
                     vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(VERUS_CHAINID, EVAL_EARNEDNOTARIZATION))));
 
                     int64_t needed = nHeight == 1 ? PBAAS_MINNOTARIZATIONOUTPUT << 1 : PBAAS_MINNOTARIZATIONOUTPUT;
@@ -1023,10 +843,14 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                     // output duplicate notarization as coinbase output for instant spend to notarization
                     // the output amount is considered part of the total value of this coinbase
                     CPBaaSNotarization pbn(newNotarizationTx);
-                    notarizationOut = MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, needed, pk, vKeys, pbn);
+                    notarizationOut = MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, needed, pkCC, vKeys, pbn);
                     coinbaseTx.vout.push_back(notarizationOut);
 
-                    // TODO: if we have confirmed our notary chain, we need to create and send any newly confirmed
+                    // we need to create and send any newly confirmed
+                    if (confirmedInput != -1)
+                    {
+
+                    }
                     // exports as import transactions to the notary chain - may want to set a flag and do that on submissionthread
                     //
                 }
index 39276f296be2e76cabf7167bfc201c4e0259ba35..46150dd89e6ce1165d1622a2709ff905e349a3b7 100644 (file)
@@ -17,6 +17,7 @@
 #include "base58.h"
 #include "timedata.h"
 #include "main.h"
+#include "transaction_builder.h"
 
 using namespace std;
 
@@ -423,16 +424,15 @@ CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
 
 CCrossChainImport::CCrossChainImport(const CTransaction &tx)
 {
-    for (auto out : tx.vout)
+    if (tx.vout.size())
     {
         COptCCParams p;
-        if (IsPayToCryptoCondition(out.scriptPubKey, p))
+        if (IsPayToCryptoCondition(tx.vout[0].scriptPubKey, p))
         {
             // always take the first for now
             if (p.evalCode == EVAL_CROSSCHAIN_IMPORT)
             {
                 FromVector(p.vData[0], *this);
-                break;
             }
         }
     }
@@ -462,7 +462,7 @@ uint160 CPBaaSChainDefinition::GetChainID(std::string name)
     return Hash160(chainHash.begin(), chainHash.end());
 }
 
-uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition)
+uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
 {
     return CCrossChainRPCData::GetConditionID(name, condition);
 }
@@ -1051,6 +1051,249 @@ CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
     return currencyState;
 }
 
+bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> minerOutputs, CTxDestination &firstDestinationOut)
+{
+    LOCK(cs_mergemining);
+    {
+        txnouttype outType;
+        std::vector<std::vector<unsigned char>> vSolutions;
+
+        if (!minerOutputs.size() || !Solver(minerOutputs[0].second, outType, vSolutions))
+        {
+            return false;
+        }
+
+        if (outType == TX_PUBKEY)
+        {
+            CPubKey pubKey(vSolutions[0]);
+            if (!pubKey.IsValid())
+            {
+                return false;
+            }
+            firstDestinationOut = CTxDestination(pubKey);
+        }
+        else if (outType == TX_PUBKEYHASH)
+        {
+            firstDestinationOut = CTxDestination(CKeyID(uint160(vSolutions[0])));
+        }
+        else
+        {
+            return false;
+        }
+    }
+    latestMiningOutputs = minerOutputs;
+    latestDestination = firstDestinationOut;
+}
+
+void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
+{
+    // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
+    {
+        if (!nHeight)
+        {
+            return;
+        }
+
+        multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
+
+        CKeyID exportKeyID(CCrossChainRPCData::GetConditionID(ThisChain().GetChainID(), EVAL_CROSSCHAIN_EXPORT));
+
+        // get all available transfer outputs to aggregate into export transactions
+        if (GetUnspentChainTransfers(transferOutputs))
+        {
+            std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
+            std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
+
+            // we need unspent export outputs to export
+            if (GetUnspentChainExports(exportOutputs))
+            {
+                uint160 lastChain;
+
+                // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
+                // export of that type, or there are MIN_INPUTS or more outputs to aggregate
+                for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
+                {
+                    // get chain target and see if it is the same
+                    if (lastChain.IsNull() || it->first == lastChain)
+                    {
+                        txInputs.push_back(it->second);
+                        lastChain = it->first;
+                    }
+                    else
+                    {
+                        auto recentExportIt = exportOutputs.find(lastChain);
+                        // TODO: we cannot have a duplicate here, check for it
+
+                        if (recentExportIt != exportOutputs.end() &&
+                            ((nHeight - recentExportIt->second.first) >= CCrossChainExport::MIN_BLOCKS) ||
+                            (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
+                        {
+                            boost::optional<CTransaction> oneExport;
+
+                            // make one or more transactions that spends the last export and all possible cross chain transfers
+                            while (txInputs.size())
+                            {
+                                TransactionBuilder tb(Params().GetConsensus(), nHeight);
+
+                                int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
+
+                                int inputsLeft = txInputs.size() - numInputs;
+
+                                if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
+                                {
+                                    inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
+                                    numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
+                                    assert(numInputs > 0);
+                                }
+
+                                // each time through, we make one export transaction with the remainder or a subset of the
+                                // reserve transfer inputs. inputs can be:
+                                // 1. transfers of reserve for fractional reserve chains
+                                // 2. pre-conversions for pre-launch participation in the premine
+                                // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
+                                //
+                                // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
+                                // or reserve token inputs.
+                                //
+                                // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
+                                // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
+                                // as part of the import process
+                                // 
+                                // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain. 
+                                // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the 
+                                // remaining Verus reserve coin considered burned.
+                                //
+                                CAmount totalTxFees = 0;
+                                CAmount totalAmount = 0;
+                                std::vector<CBaseChainObject *> chainObjects;
+
+                                // first, we must add the export output from the current export thread to this chain
+                                if (oneExport.has_value())
+                                {
+                                    // spend the last export transaction output
+                                    CTransaction &tx = oneExport.get();
+                                    COptCCParams p;
+                                    int j;
+                                    for (j = 0; j < tx.vout.size(); j++)
+                                    {
+                                        if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
+                                        {
+                                            break;
+                                        }
+                                    }
+
+                                    // had to be found and valid if we made the tx
+                                    assert(j < tx.vout.size() && p.IsValid());
+
+                                    tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
+                                }
+                                else
+                                {
+                                    // spend the recentExportIt output
+                                    tb.AddTransparentInput(recentExportIt->second.second.txIn.prevout, 
+                                                            recentExportIt->second.second.scriptPubKey, 
+                                                            recentExportIt->second.second.nValue);
+                                }
+
+                                for (int j = 0; j < numInputs; j++)
+                                {
+                                    tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
+                                    totalTxFees += txInputs[j].second.nFees;
+                                    totalAmount += txInputs[j].second.nValue;
+                                    chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
+                                }
+
+                                CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
+                                CAmount exportFees = ccx.CalculateExportFee();
+                                CReserveTransfer feeOut = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT,
+                                                                            exportFees, 0, GetDestinationID(feeOutput));
+                                chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
+
+                                CScript opRet = StoreOpRetArray(chainObjects);
+                                DeleteOpRetObjects(chainObjects);
+
+                                CCcontract_info CC;
+                                CCcontract_info *cp;
+                                cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
+
+                                CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
+
+                                // send zero to a cross chain export output of the specific chain
+                                std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
+
+                                CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, 
+                                                                    0,
+                                                                    pk,
+                                                                    dests,
+                                                                    ccx);
+
+                                tb.AddTransparentOutput(exportOut.scriptPubKey, 0);
+
+                                // if we are on Verus chain, send all native funds, less fees to reserve deposit CC, which is equivalent to the reserve account
+                                // on a PBaaS reserve chain, input is burned
+                                if (IsVerusActive())
+                                {
+                                    cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
+                                    pk = CPubKey(ParseHex(CC.CChexstr));
+
+                                    // send the entire amount to a reserve transfer output of the specific chain
+                                    // we receive our fee on the other chain or when it comes back
+                                    dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
+
+                                    CReserveOutput ro(CReserveOutput::VALID, totalAmount - ccx.CalculateExportFee());
+
+                                    CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT, 
+                                                                            ro.nValue,
+                                                                            pk,
+                                                                            dests,
+                                                                            ro);
+
+                                    tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
+                                }
+
+                                tb.AddOpRet(opRet);
+
+                                boost::optional<CTransaction> newExport = tb.Build();
+
+                                if (newExport.has_value())
+                                {
+                                    // replace the last one only if we have a valid new one
+                                    oneExport = newExport;
+                                    CTransaction &tx = oneExport.get();
+
+                                    LOCK2(cs_main, mempool.cs);
+                                    static int lastHeight = 0;
+                                    // remove conflicts, so that we are the now
+                                    std::list<CTransaction> removed;
+                                    mempool.removeConflicts(tx, removed);
+
+                                    // add to mem pool and relay
+                                    if (myAddtomempool(tx))
+                                    {
+                                        RelayTransaction(tx);
+                                    }
+                                }
+                                // erase the inputs we've attempted to spend
+                                txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+// send new imports from this chain to the specified chain, which generally will be the notary chain
+void CConnectedChains::SendNewImports(const uint160 &chainID, 
+                                      const CPBaaSNotarization &notarization, 
+                                      const uint256 &lastExportTx, 
+                                      const CTransaction &lastCrossImport, 
+                                      const CTransaction &lastExport)
+{
+    // currently only support sending imports to  
+}
+
 void CConnectedChains::SubmissionThread()
 {
     try
@@ -1060,11 +1303,13 @@ void CConnectedChains::SubmissionThread()
         // wait for something to check on, then submit blocks that should be submitted
         while (true)
         {
+            boost::this_thread::interruption_point();
+
             if (IsVerusActive())
             {
                 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
                 //printf("SubmissionThread: pruning\n");
-                ConnectedChains.PruneOldChains(GetAdjustedTime() - 300);
+                PruneOldChains(GetAdjustedTime() - 300);
                 bool submit = false;
                 {
                     LOCK(cs_mergemining);
index 0f77474ae4f06766b85425f8706e8da5bf45b124..74f6d8ed1a88c2ded8a907892e449d4902fa723b 100644 (file)
@@ -26,6 +26,8 @@
 
 #include <boost/algorithm/string.hpp>
 
+class CPBaaSNotarization;
+
 // these are output cryptoconditions for the Verus reserve liquidity system
 // VRSC can be proxied to other PBaaS chains and sent back for use with this system
 // The assumption that Verus is either the proxy on the PBaaS chain or the native
@@ -393,6 +395,7 @@ public:
     CKeyID address;                         // non-purchased/converted premine and fee recipient address
     int64_t premine;                        // initial supply that is distributed to the premine output address, but not purchased
     int64_t initialcontribution;            // optional initial contribution by this transaction. this serves to ensure the option to participate by whoever defines the chain
+    int64_t minpreconvert;                  // zero for now, later can be used for Kickstarter-like launch and return all non-network fees upon failure to meet minimum
     int64_t maxpreconvert;                  // maximum amount of reserve that can be pre-converted
     int64_t preconverted;                   // actual converted amount if known
     int64_t conversion;                     // initial reserve ratio, also value in Verus if premine is 0, otherwise value is conversion * maxpreconvert/(maxpreconvert + premine)
@@ -422,7 +425,7 @@ public:
 
     CPBaaSChainDefinition(const CTransaction &tx, bool validate = false);
 
-    CPBaaSChainDefinition(std::string Name, std::string Address, int64_t Premine, int64_t Conversion, int64_t maxPreConvert, int64_t preConverted, int64_t LaunchFee,
+    CPBaaSChainDefinition(std::string Name, std::string Address, int64_t Premine, int64_t Conversion, int64_t minPreConvert, int64_t maxPreConvert, int64_t preConverted, int64_t LaunchFee,
                           int32_t StartBlock, int32_t EndBlock, int32_t chainEras,
                           const std::vector<int64_t> &chainRewards, const std::vector<int64_t> &chainRewardsDecay,
                           const std::vector<int32_t> &chainHalving, const std::vector<int32_t> &chainEraEnd, std::vector<int32_t> &chainCurrencyOptions,
@@ -431,6 +434,7 @@ public:
                             name(Name),
                             premine(Premine),
                             conversion(Conversion),
+                            minpreconvert(minPreConvert),
                             maxpreconvert(maxPreConvert),
                             preconverted(preConverted),
                             launchFee(LaunchFee),
@@ -464,6 +468,7 @@ public:
         READWRITE(address);        
         READWRITE(VARINT(premine));
         READWRITE(VARINT(conversion));
+        READWRITE(VARINT(minpreconvert));
         READWRITE(VARINT(maxpreconvert));
         READWRITE(VARINT(preconverted));
         READWRITE(VARINT(launchFee));
@@ -480,26 +485,26 @@ public:
         READWRITE(nodes);
     }
 
-    std::vector<unsigned char> AsVector()
+    std::vector<unsigned char> AsVector() const
     {
         return ::AsVector(*this);
     }
 
     static uint160 GetChainID(std::string name);
 
-    uint160 GetChainID()
+    uint160 GetChainID() const
     {
         return GetChainID(name);
     }
 
-    uint160 GetConditionID(int32_t condition);
+    uint160 GetConditionID(int32_t condition) const;
 
-    bool IsValid()
+    bool IsValid() const
     {
         return (nVersion != PBAAS_VERSION_INVALID) && (name.size() && eras > 0) && (eras <= ASSETCHAINS_MAX_ERAS);
     }
 
-    int32_t ChainOptions()
+    int32_t ChainOptions() const
     {
         return eraOptions.size() ? eraOptions[0] : 0;
     }
@@ -852,6 +857,9 @@ public:
     CRPCChainData notaryChain;                  // notary chain information
 
     CPBaaSChainDefinition thisChain;
+    std::vector<std::pair<int, CScript>> latestMiningOutputs; // accessible from all merge miners - can be invalid
+    CTxDestination  latestDestination;          // latest destination from miner output 0 - can be invalid
+    int64_t lastAggregation = 0;                // adjusted time of last aggregation
 
     int32_t earnedNotarizationHeight;           // zero or the height of one or more potential submissions
     CBlock earnedNotarizationBlock;
@@ -891,6 +899,32 @@ public:
     uint32_t PruneOldChains(uint32_t pruneBefore);
     uint32_t CombineBlocks(CBlockHeader &bh);
 
+    // returns false if destinations are empty or first is not either pubkey or pubkeyhash
+    bool SetLatestMiningOutputs(const std::vector<std::pair<int, CScript>> minerOutputs, CTxDestination &firstDestinationOut);
+    void AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight);
+
+    // send new imports from this chain to the specified chain, which generally will be the notary chain
+    void SendNewImports(const uint160 &chainID, 
+                        const CPBaaSNotarization &notarization, 
+                        const uint256 &lastExportTx, 
+                        const CTransaction &lastCrossImport, 
+                        const CTransaction &lastExport);
+
+    bool GetLastImport(const uint160 &chainID, 
+                       CTransaction &lastImport, 
+                       CTransaction &crossChainExport, 
+                       CCrossChainImport &ccImport, 
+                       CCrossChainExport &ccCrossExport);
+
+    // returns newly created import transactions to the specified chain from exports on this chain specified chain
+    void CreateLatestImports(const CPBaaSChainDefinition &chainDef, 
+                             const CTransaction &lastCrossChainImport, 
+                             const CTransaction &lastExport,
+                             const CTransaction &importTxTemplate,
+                             uint32_t confirmedHeight,
+                             std::vector<CTransaction> &newImports);
+
+
     CRPCChainData &NotaryChain()
     {
         return notaryChain;
index ee985dee7c2685da2738b948bae43aca208f5883..1fdf2f96106dbded6f641ed410a09363720d334e 100644 (file)
@@ -463,7 +463,7 @@ CMutableTransaction &CReserveTransactionDescriptor::AddConversionOutputs(CMutabl
 
     CCurrencyState dummy;
     CCurrencyState &currencyState = pCurrencyState ? *pCurrencyState : dummy;
-    // if no exchange rate is specified, it is unity
+    // if no exchange rate is specified, first from the currency if present, then it is unity
     if (!exchangeRate)
     {
         int64_t price;
@@ -517,28 +517,24 @@ CMutableTransaction &CReserveTransactionDescriptor::AddConversionOutputs(CMutabl
 
             conversionTx.vout.push_back(MakeCC1of1Vout(EVAL_RESERVE_TRANSFER, 0, pk, dests, rt));
         }
-        else
+        else if (indexRex.second.flags & indexRex.second.TO_RESERVE)
         {
-            // check reserve or normal output
-            if (indexRex.second.flags & indexRex.second.TO_RESERVE)
-            {
-                // convert amount to reserve from native
-                amount = currencyState.NativeToReserve(amount, exchangeRate);
+            // convert amount to reserve from native
+            amount = currencyState.NativeToReserve(amount, exchangeRate);
 
-                // send the net amount to the indicated destination
-                std::vector<CTxDestination> dests = std::vector<CTxDestination>({p.vKeys[0]});
+            // send the net amount to the indicated destination
+            std::vector<CTxDestination> dests = std::vector<CTxDestination>({p.vKeys[0]});
 
-                // create the output with the unconverted amount less fees
-                CReserveTransfer rt(CReserveTransfer::VALID, amount, CReserveTransfer::DEFAULT_PER_STEP_FEE << 1, GetDestinationID(p.vKeys[0]));
+            // create the output with the unconverted amount less fees
+            CReserveOutput ro(CReserveOutput::VALID, amount);
 
-                conversionTx.vout.push_back(MakeCC0of0Vout(EVAL_RESERVE_TRANSFER, 0, dests, rt));
-            }
-            else
-            {
-                // convert amount to native from reserve
-                amount = currencyState.ReserveToNative(amount, exchangeRate);
-                conversionTx.vout.push_back(CTxOut(amount, GetScriptForDestination(p.vKeys[0])));
-            }
+            conversionTx.vout.push_back(MakeCC0of0Vout(EVAL_RESERVE_OUTPUT, 0, dests, ro));
+        }
+        else
+        {
+            // convert amount to native from reserve and send as normal output
+            amount = currencyState.ReserveToNative(amount, exchangeRate);
+            conversionTx.vout.push_back(CTxOut(amount, GetScriptForDestination(p.vKeys[0])));
         }
     }
     return conversionTx;
@@ -897,7 +893,7 @@ CAmount CCurrencyState::ReserveToNative(CAmount reserveAmount) const
     return bigAmount.GetLow64();
 }
 
-CAmount CCurrencyState::ReserveToNative(CAmount reserveAmount, CAmount exchangeRate) const
+CAmount CCurrencyState::ReserveToNative(CAmount reserveAmount, CAmount exchangeRate)
 {
     arith_uint256 bigAmount(reserveAmount);
 
index c12423daf2be3efe284ed5d7a471d4ccb197a825..42f2844229335e98edff7c4b4373af8e9b4bcc66 100644 (file)
@@ -69,11 +69,13 @@ class CReserveTransfer : public CReserveOutput
 public:
     static const uint32_t CONVERT = 2;
     static const uint32_t PRECONVERT = 4;
+    static const uint32_t FEE_OUTPUT = 8;               // one per import, amount must match total percentage of fees for exporter, no pre-convert allowed
+    static const uint32_t SEND_BACK = 0x10;             // fee is sent back immediately to destination on exporting chain
 
-    static const CAmount DEFAULT_PER_STEP_FEE = 10000; // default fee for each step of each transfer (initial mining, transfer, mining on new chain)
+    static const CAmount DEFAULT_PER_STEP_FEE = 10000;  // default fee for each step of each transfer (initial mining, transfer, mining on new chain)
 
-    CAmount nFees;                  // cross-chain network fees only, separated out to enable market conversions, conversion fees are additional
-    CKeyID destination;             // transparent address to send funds to on the target chain
+    CAmount nFees;                                      // cross-chain network fees only, separated out to enable market conversions, conversion fees are additional
+    CKeyID destination;                                 // transparent address to send funds to on the target chain
 
     CReserveTransfer(const std::vector<unsigned char> &asVector)
     {
@@ -368,7 +370,7 @@ public:
     CAmount ReserveFeeToNative(CAmount inputAmount, CAmount outputAmount) const;
 
     CAmount ReserveToNative(CAmount reserveAmount) const;
-    CAmount ReserveToNative(CAmount reserveAmount, CAmount exchangeRate) const;
+    static CAmount ReserveToNative(CAmount reserveAmount, CAmount exchangeRate);
 
     CAmount NativeToReserve(CAmount nativeAmount) const
     {
index 7ce1a8e1cca1afeb1f62852fa104746cdad04688..3f39f15ba5bf657af5258c4c44b58501caa3f379 100644 (file)
@@ -175,6 +175,288 @@ void GetDefinedChains(vector<CPBaaSChainDefinition> &chains, bool includeExpired
     }
 }
 
+bool CConnectedChains::GetLastImport(const uint160 &chainID, 
+                                     CTransaction &lastImport, 
+                                     CTransaction &crossChainExport, 
+                                     CCrossChainImport &ccImport, 
+                                     CCrossChainExport &ccCrossExport)
+{
+    CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_IMPORT);
+
+    std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
+
+    LOCK2(cs_main, mempool.cs);
+
+    // get last import from the specified chain
+    if (!GetAddressUnspent(keyID, 1, unspentOutputs))
+    {
+        return false;
+    }
+    
+    // make sure it isn't just a burned transaction to that address, drop out on first match
+    const std::pair<CAddressUnspentKey, CAddressUnspentValue> *pOutput = NULL;
+    COptCCParams p;
+    for (const auto &output : unspentOutputs)
+    {
+        if (output.second.script.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
+        {
+            pOutput = &output;
+            break;
+        }
+    }
+    if (!pOutput)
+    {
+        return false;
+    }
+    uint256 hashBlk;
+    if (!myGetTransaction(pOutput->first.txhash, lastImport, hashBlk) || !(lastImport.vout.size() && lastImport.vout.back().scriptPubKey.IsOpReturn()))
+    {
+        return false;
+    }
+    ccImport = CCrossChainImport(p.vData[0]);
+    auto opRetArr = RetrieveOpRetArray(lastImport.vout.back().scriptPubKey);
+    if (!opRetArr.size() || opRetArr[0]->objectType != CHAINOBJ_TRANSACTION)
+    {
+        DeleteOpRetObjects(opRetArr);
+    }
+    else
+    {
+        crossChainExport = ((CChainObject<CTransaction> *)opRetArr[0])->object;
+        if (!(ccCrossExport = CCrossChainExport(crossChainExport)).IsValid())
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+// returns newly created import transactions to the specified chain from exports on this chain specified chain
+// nHeight is the height for which we have an MMR that the chainID chain considers confirmed for this chain. it will
+// accept proofs of any transactions with that MMR and height.
+// Parameters shuold be validated before this call.
+void CConnectedChains::CreateLatestImports(const CPBaaSChainDefinition &chainDef, 
+                                           const CTransaction &lastCrossChainImport, 
+                                           const CTransaction &lastExport,
+                                           const CTransaction &importTxTemplate,
+                                           uint32_t confirmedHeight,
+                                           std::vector<CTransaction> &newImports)
+{
+    uint160 chainID = chainDef.GetChainID();
+
+    // we are passed the latest import transaction from the chain specified, and
+    // we continue from there, creating import transactions from all of the confirmed exports after the one passed
+    uint256 lastExportHash = lastExport.GetHash();
+
+    // which transaction are we in this block?
+    std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
+    std::set<uint256> countedTxes;                  // don't count twice
+
+    CCrossChainImport lastCCI(lastCrossChainImport);
+    if (!lastCCI.IsValid())
+    {
+        LogPrintf("%s: Invalid lastCrossChainImport transaction\n", __func__);
+        printf("%s: Invalid lastCrossChainImport transaction\n", __func__);
+        return;
+    }
+
+    // look for the exports
+    CKeyID keyID = CCrossChainRPCData::GetConditionID(chainID, EVAL_CROSSCHAIN_EXPORT);
+
+    LOCK2(cs_main, mempool.cs);
+    CTransaction lastExportTx;
+    uint256 blkHash;
+    CBlockIndex *pIndex;
+    BlockMap::iterator blkMapIt;
+
+    // get all export transactions including and since this one up to the confirmed height
+    if (myGetTransaction(lastExportHash, lastExportTx, blkHash) && 
+                         (blkMapIt = mapBlockIndex.find(blkHash)) != mapBlockIndex.end() && 
+                         chainActive.Contains(blkMapIt->second) &&
+                         blkMapIt->second->GetHeight() <= confirmedHeight &&
+                         GetAddressIndex(keyID, 1, addressIndex, blkMapIt->second->GetHeight(), confirmedHeight))
+    {
+        // find this export, then check the next one that spends it and use it if still valid
+        bool found = false;
+        uint256 lastHash = lastExportHash;
+
+        // indexed by input hash
+        std::map<uint256, std::pair<CAddressIndexKey, CTransaction>> validExports;
+
+        // validate, order, and relate them with their inputs
+        for (auto utxo : addressIndex)
+        {
+            // if current tx spends lastHash, then we have our next valid transaction to create an import with
+            CTransaction tx, inputtx;
+            uint256 blkHash1, blkHash2;
+            BlockMap::iterator blkIt;
+            CCrossChainExport ccx;
+            if (myGetTransaction(utxo.first.txhash, tx, blkHash1) &&
+                (ccx = CCrossChainExport(tx)).IsValid() &&
+                (tx.IsCoinBase() && (blkIt = mapBlockIndex.find(blkHash1)) != mapBlockIndex.end() && blkIt->second->GetHeight() == 1) || 
+                (!tx.IsCoinBase() && tx.vin.size() && myGetTransaction(tx.vin[0].prevout.hash, inputtx, blkHash2)))
+            {
+                // either this is the first import as a chain definition from the coinbase of block 1, or it must spend a valid import
+                if (!tx.IsCoinBase())
+                {
+                    COptCCParams p;
+                    // validate the input as a chain export input
+                    if (!(inputtx.vout[tx.vin[0].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_EXPORT))
+                    {
+                        printf("%s: invalid export: input tx %s is not in valid export thread\n", __func__, inputtx.GetHash().GetHex().c_str());
+                        continue;
+                    }
+                    validExports.insert(make_pair(tx.vin[0].prevout.hash, make_pair(utxo.first, tx)));
+                }
+            }
+            else
+            {
+                printf("%s: cannot retrieve transaction %s or transaction is an invalid export\n", __func__, utxo.first.txhash.GetHex().c_str());
+                continue;
+            }
+        }
+
+        CTransaction lastImport(lastCrossChainImport);
+        for (auto aixIt = validExports.find(lastExportHash); 
+             aixIt != validExports.end(); 
+             aixIt = validExports.find(lastExportHash))
+        {
+            // One pass - create an import transaction that spends the last import transaction from a confirmed export transaction that spends the last one of those
+            // 1. Creates preconvert outputs that spend from the initial supply, which comes from the import transaction thread without fees
+            // 2. Creates reserve outputs for unconverted imports
+            // 3. Creates reserveExchange transactions requesting conversion at market for convert transfers
+            // 4. Creates reserveTransfer outputs for outputs with the SEND_BACK flag set, unless they are under 5x the normal network fee
+            // 5. Creates a pass-through EVAL_CROSSCHAIN_IMPORT output with the remainder of the non-preconverted coins
+            // then signs the transaction considers it the latest import and the new export the latest export and
+            // loops until there are no more confirmed, consecutive, valid export transactions to export
+
+            // 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
+            assert(aixIt->second.second.vout.back().scriptPubKey.IsOpReturn());
+
+            std::vector<CBaseChainObject *> exportOutputs = RetrieveOpRetArray(aixIt->second.second.vout.back().scriptPubKey);
+
+            CMutableTransaction newImportTx(importTxTemplate);
+            newImportTx.vin.clear();
+            newImportTx.vin.push_back(CTxIn(lastImport.GetHash(), 0));          // must spend output 0
+            newImportTx.vout.clear();
+            newImportTx.vout.push_back(CTxOut()); // placeholder for the first output
+
+            // emit a reserve exchange output
+            // we will send using a reserve output, fee will be paid by converting from reserve
+            CCcontract_info CC;
+            CCcontract_info *cp;
+
+            CAmount totalPreconvert = 0;
+            CAmount totalImport = 0;
+
+            for (auto pRT : exportOutputs)
+            {
+                if (pRT->objectType != CHAINOBJ_RESERVETRANSFER)
+                {
+                    LogPrintf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
+                    printf("%s: POSSIBLE CORRUPTION bad export opret in transaction %s\n", __func__, aixIt->second.second.GetHash().GetHex().c_str());
+                    break;
+                }
+                CReserveTransfer &curTransfer = ((CChainObject<CReserveTransfer> *)pRT)->object;
+                if (curTransfer.IsValid())
+                {
+                    CTxOut newOut;
+
+                    totalImport += curTransfer.nFees + curTransfer.nValue;
+
+                    if (curTransfer.flags & curTransfer.CONVERT)
+                    {
+                        // emit a reserve exchange output
+                        // we will send using a reserve output, fee will be paid by converting from reserve
+                        cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
+
+                        CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
+
+                        std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
+                        CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, curTransfer.nValue);
+
+                        newOut = MakeCC1of1Vout(EVAL_RESERVE_EXCHANGE, 0, pk, dests, rex);
+                    }
+                    else if (curTransfer.flags & curTransfer.PRECONVERT)
+                    {
+                        // calculate the amount and generate a normal output that spends the input of the import
+                        CAmount nativeConverted = CCurrencyState::ReserveToNative(curTransfer.nValue, chainDef.conversion);
+                        totalPreconvert += nativeConverted;
+                        newOut = CTxOut(nativeConverted, GetScriptForDestination(curTransfer.destination));
+                    }
+                    else if (curTransfer.flags & curTransfer.SEND_BACK && curTransfer.nValue > (curTransfer.DEFAULT_PER_STEP_FEE << 2))
+                    {
+                        // generate a reserve transfer back to the source chain if we have at least double the fee, otherwise leave it on
+                        // this chain to be claimed
+                        cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
+
+                        std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
+                        CAmount fees = curTransfer.DEFAULT_PER_STEP_FEE << 1;
+                        CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID, curTransfer.nValue - fees, fees, curTransfer.destination);
+
+                        newOut = MakeCC0of0Vout(EVAL_RESERVE_TRANSFER, 0, dests, rt);
+                    }
+                    else
+                    {
+                        // generate a reserve output of the amount indicated, less fees
+                        // emit a reserve exchange output
+                        // we will send using a reserve output, fee will be paid by converting from reserve
+                        cp = CCinit(&CC, EVAL_RESERVE_OUTPUT);
+
+                        std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(curTransfer.destination)});
+                        CReserveOutput ro = CReserveOutput(CReserveExchange::VALID, curTransfer.nValue);
+
+                        newOut = MakeCC0of0Vout(EVAL_RESERVE_OUTPUT, 0, dests, ro);
+                    }
+                    newImportTx.vout.push_back(newOut);
+                }
+            }
+
+            // emit a reserve exchange output
+            // we will send using a reserve output, fee will be paid by converting from reserve
+            cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
+
+            CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
+            CKeyID dest();
+
+            std::vector<CTxDestination> dests = std::vector<CTxDestination>({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(chainDef.GetChainID(), EVAL_CROSSCHAIN_IMPORT)))});
+            CCrossChainImport cci = CCrossChainImport(ConnectedChains.ThisChain().GetChainID(), totalImport);
+
+            newImportTx.vout[0] = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, lastCCI.nValue - totalPreconvert, pk, dests, cci);
+
+            //
+            // sign the transaction and addto our vector
+
+            CTransaction ntx(newImportTx);
+
+            uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
+
+            bool signSuccess;
+            SignatureData sigdata;
+            CAmount value;
+            const CScript *pScriptPubKey;
+
+            const CScript virtualCC;
+            CTxOut virtualCCOut;
+
+            pScriptPubKey = &lastImport.vout[0].scriptPubKey;
+            value = lastCCI.nValue;
+
+            signSuccess = ProduceSignature(
+                TransactionSignatureCreator(pwalletMain, &ntx, 0, lastCCI.nValue, SIGHASH_ALL), lastImport.vout[0].scriptPubKey, sigdata, consensusBranchId);
+
+            if (signSuccess)
+            {
+                UpdateTransaction(newImportTx, 0, sigdata);
+            }
+
+            // we now have a signed Import transaction for the chainID chain, it is the latest, and the export we used is now the latest as well
+            newImports.push_back(newImportTx);
+            lastImport = newImportTx;
+            lastExportHash = aixIt->second.first.txhash;
+        }
+    }
+}
+
 void CheckPBaaSAPIsValid()
 {
     if (!chainActive.LastTip() ||
@@ -1427,7 +1709,7 @@ UniValue getcrossnotarization(const UniValue& params, bool fHelp)
             else
             {
                 // get index in the block as our transaction index for proofs
-                txIndex = addressIndex[txIndex].first.txindex;
+                txIndex = addressIndex[txIndex].first.index;
             }
 
             // if bock headers are merge mined, keep header refs, not headers
@@ -1731,6 +2013,37 @@ UniValue listreservetransactions(const UniValue& params, bool fHelp)
     // lists all transactions in a wallet that are 
 }
 
+UniValue reserveexchange(const UniValue& params, bool fHelp)
+{
+    if (fHelp || params.size() != 1)
+    {
+        throw runtime_error(
+            "reserveexchange '[{\"toreserve\": 1, \"recipient\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'\n"
+            "\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"
+            "\nFunds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n"
+
+            "\nArguments\n"
+            "       {\n"
+            "           \"toreserve\"      : \"bool\",  (bool,   optional) if present, conversion is to the underlying reserve (Verus), if false, from Verus\n"
+            "           \"recipient\"      : \"Rxxx\",  (string, required) recipient of converted funds or funds that failed to convert\n"
+            "           \"amount\"         : \"n\",     (int64,  required) amount of source coins that will be converted, depending on the toreserve flag, the rest is change\n"
+            "           \"limit\"          : \"n\",     (int64,  optional) price in reserve limit, below which for buys and above which for sells, execution will occur\n"
+            "           \"validbefore\"    : \"n\",     (int,    optional) block before which this can execute as a conversion, otherwise, it executes as a send with normal network fee\n"
+            "           \"subtractfee\"    : \"bool\",  (bool,   optional) if true, reduce amount to destination by the fee amount, otherwise, add from inputs to cover fee"
+            "       }\n"
+
+            "\nResult:\n"
+            "       \"txid\" : \"transactionid\" (string) The transaction id.\n"
+
+            "\nExamples:\n"
+            + HelpExampleCli("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
+            + HelpExampleRpc("reserveexchange", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'")
+        );
+    }
+
+    CheckPBaaSAPIsValid();
+}
+
 UniValue sendreserve(const UniValue& params, bool fHelp)
 {
     if (fHelp || params.size() != 1)
@@ -1742,12 +2055,12 @@ UniValue sendreserve(const UniValue& params, bool fHelp)
 
             "\nArguments\n"
             "       {\n"
-            "           \"chain\"          : \"xxxx\",  (string, optional) Verus ecosystem-wide name/symbol of chain to send to, if absent, current chain is assumed\n"
+            "           \"name\"           : \"xxxx\",  (string, optional) Verus ecosystem-wide name/symbol of chain to send to, if absent, current chain is assumed\n"
             "           \"paymentaddress\" : \"Rxxx\",  (string, required) premine and launch fee recipient\n"
             "           \"refundaddress\"  : \"Rxxx\",  (string, required) if a pre-convert is not mined in time, funds can be spent by the owner of this address\n"
             "           \"amount\"         : \"n\",     (int64,  required) amount of coins that will be moved and sent to address on PBaaS chain, network and conversion fees additional\n"
             "           \"convert\"        : \"false\", (bool,   optional) auto-convert to PBaaS currency at market price\n"
-            "           \"launchonly\"     : \"false\", (bool,   optional) auto-convert to PBaaS currency at market price, fail if order cannot be placed before launch\n"
+            "           \"preconvert\"     : \"false\", (bool,   optional) auto-convert to PBaaS currency at market price, fail if order cannot be placed before launch\n"
             "           \"subtractfee\"    : \"bool\",  (bool,   optional) if true, reduce amount to destination by the fee amount, otherwise, add from inputs to cover fee"
             "       }\n"
 
@@ -1789,6 +2102,7 @@ UniValue sendreserve(const UniValue& params, bool fHelp)
     string refundAddr = uni_get_str(find_value(params[0], "refundaddress"), paymentAddr);
     CAmount amount = uni_get_int64(find_value(params[0], "amount"), -1);
     bool convert = uni_get_int(find_value(params[0], "convert"), false);
+    bool preconvert = uni_get_int(find_value(params[0], "preconvert"), false);
     bool subtractFee = uni_get_int(find_value(params[0], "subtractfee"), false);
     uint32_t flags = CReserveOutput::VALID;
 
@@ -1839,8 +2153,6 @@ UniValue sendreserve(const UniValue& params, bool fHelp)
     int32_t height = chainActive.Height();
     bool beforeStart = chainDef.startBlock > height;
 
-    bool launchOnly = uni_get_int(find_value(params[0], "launchonly"), beforeStart);
-
     if (isVerusActive)
     {
         if (chainID == thisChainID)
@@ -1849,7 +2161,7 @@ UniValue sendreserve(const UniValue& params, bool fHelp)
         }
         else // ensure the PBaaS chain is a fractional reserve or that it's convertible and this is a conversion
         {
-            if (convert)
+            if (convert || preconvert)
             {
                 // if chain hasn't started yet, we must use the conversion as a ratio over satoshis to participate in the pre-mine
                 // up to a maximum
@@ -1861,9 +2173,9 @@ UniValue sendreserve(const UniValue& params, bool fHelp)
                     }
 
                     flags |= CReserveTransfer::PRECONVERT;
-                } else if (!isReserve || launchOnly)
+                } else if (!isReserve || preconvert)
                 {
-                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert " + std::string(ASSETCHAINS_SYMBOL) + " after chain launch");
+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot preconvert " + std::string(ASSETCHAINS_SYMBOL) + " after chain launch");
                 }
                 else
                 {
This page took 0.069331 seconds and 4 git commands to generate.