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
}
}
- // 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
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);
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
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);
}
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;
// 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
//
}
#include "base58.h"
#include "timedata.h"
#include "main.h"
+#include "transaction_builder.h"
using namespace std;
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;
}
}
}
return Hash160(chainHash.begin(), chainHash.end());
}
-uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition)
+uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
{
return CCrossChainRPCData::GetConditionID(name, condition);
}
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 ¬arization,
+ const uint256 &lastExportTx,
+ const CTransaction &lastCrossImport,
+ const CTransaction &lastExport)
+{
+ // currently only support sending imports to
+}
+
void CConnectedChains::SubmissionThread()
{
try
// 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);
#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
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)
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,
name(Name),
premine(Premine),
conversion(Conversion),
+ minpreconvert(minPreConvert),
maxpreconvert(maxPreConvert),
preconverted(preConverted),
launchFee(LaunchFee),
READWRITE(address);
READWRITE(VARINT(premine));
READWRITE(VARINT(conversion));
+ READWRITE(VARINT(minpreconvert));
READWRITE(VARINT(maxpreconvert));
READWRITE(VARINT(preconverted));
READWRITE(VARINT(launchFee));
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;
}
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;
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 ¬arization,
+ 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;
CCurrencyState dummy;
CCurrencyState ¤cyState = 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;
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;
return bigAmount.GetLow64();
}
-CAmount CCurrencyState::ReserveToNative(CAmount reserveAmount, CAmount exchangeRate) const
+CAmount CCurrencyState::ReserveToNative(CAmount reserveAmount, CAmount exchangeRate)
{
arith_uint256 bigAmount(reserveAmount);
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)
{
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
{
}
}
+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() ||
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
// 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)
"\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"
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;
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)
}
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
}
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
{