void ProcessNewImports(const uint160 &sourceChainID, const CTransaction &lastConfirmed, int32_t nHeight)
{
+ if (CConstVerusSolutionVector::GetVersionByHeight(nHeight) < CActivationHeight::ACTIVATE_PBAAS ||
+ CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight))
+ {
+ return;
+ }
uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
// get any pending imports from the source chain. if the source chain is this chain, we don't need notarization
return obj;
}
+uint160 CurrencyNameToChainID(std::string currencyStr)
+{
+ std::string extraName;
+ uint160 retVal;
+ currencyStr = TrimSpaces(currencyStr);
+ if (!currencyStr.size())
+ {
+ return retVal;
+ }
+ ParseSubNames(currencyStr, extraName, true);
+ if (currencyStr.back() == '@' || extraName != "")
+ {
+ return retVal;
+ }
+ CTxDestination currencyDest = DecodeDestination(currencyStr);
+ if (currencyDest.which() == COptCCParams::ADDRTYPE_INVALID)
+ {
+ currencyDest = DecodeDestination(currencyStr + "@");
+ }
+ if (currencyDest.which() != COptCCParams::ADDRTYPE_INVALID)
+ {
+ retVal = GetDestinationID(currencyDest);
+ }
+ return retVal;
+}
CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj)
{
{
nVersion = PBAAS_VERSION;
name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
- name = CleanName(name, parent);
std::string parentStr = uni_get_str(find_value(obj, "parent"));
- if (parentStr == "")
- {
- parent = parent;
- }
- else
+ if (parentStr != "")
{
CTxDestination parentDest = DecodeDestination(parentStr);
parent = GetDestinationID(parentDest);
}
}
+ name = CleanName(name, parent);
+
options = (uint32_t)uni_get_int64(find_value(obj, "options"));
idRegistrationAmount = AmountFromValueNoErr(find_value(obj, "idregistrationprice"));
startBlock = uni_get_int(find_value(obj, "startblock"));
endBlock = uni_get_int(find_value(obj, "endblock"));
- proofProtocol = (EProofProtocol)uni_get_int(find_value(obj, "proofprotocol"));
- notarizationProtocol = (ENotarizationProtocol)uni_get_int(find_value(obj, "notarizationprotocol"));
- if (proofProtocol == PROOF_INVALID)
- {
- // default to standard PBaaS for a blockchain and ID for a token
- proofProtocol = options & OPTION_TOKEN ? PROOF_CHAINID : PROOF_PBAASMMR;
- }
-
- if (notarizationProtocol == NOTARIZATION_INVALID)
- {
- // default to standard PBaaS for a blockchain and ID for a token
- notarizationProtocol = options & OPTION_TOKEN ? NOTARIZATION_NOTARY_CHAINID : NOTARIZATION_AUTO;
- }
-
+ proofProtocol = (EProofProtocol)uni_get_int(find_value(obj, "proofprotocol"), (int32_t)PROOF_PBAASMMR);
+ notarizationProtocol = (ENotarizationProtocol)uni_get_int(find_value(obj, "notarizationprotocol"), (int32_t)NOTARIZATION_AUTO);
int32_t totalReserveWeight = AmountFromValueNoErr(find_value(obj, "reserveratio"));
UniValue currencyArr = find_value(obj, "currencies");
UniValue weightArr = find_value(obj, "weights");
for (int i = 0; nVersion != PBAAS_VERSION_INVALID && i < currencyArr.size(); i++)
{
- uint160 currencyID;
- currencyID = GetDestinationID(DecodeDestination(uni_get_str(currencyArr[i])));
+ uint160 currencyID = CurrencyNameToChainID(uni_get_str(currencyArr[i]));
// if we have a destination, but it is invalid, the json for this definition cannot be valid
if (currencyID.IsNull())
{
}
}
- launchFee = AmountFromValueNoErr(find_value(obj, "launchfee"));
preAllocationRatio = AmountFromValueNoErr(find_value(obj, "preallocationratio"));
UniValue preallocationArr = find_value(obj, "preallocation");
if (preallocationKey.size() != 1 || preallocationValue.size() != 1)
{
LogPrintf("%s: each preallocation entry must contain one destination identity and one amount\n", __func__);
+ printf("%s: each preallocation entry must contain one destination identity and one amount\n", __func__);
nVersion = PBAAS_VERSION_INVALID;
break;
}
CTxDestination preallocDest = DecodeDestination(preallocationKey[0]);
- if (preallocDest.which() != COptCCParams::ADDRTYPE_INVALID && preallocDest.which() != COptCCParams::ADDRTYPE_ID)
+ if (preallocDest.which() != COptCCParams::ADDRTYPE_ID && preallocDest.which() != COptCCParams::ADDRTYPE_INVALID)
{
- LogPrintf("%s: preallocation must be allocated to IDs\n", __func__);
+ LogPrintf("%s: preallocation destination must be an identity\n", __func__);
nVersion = PBAAS_VERSION_INVALID;
break;
}
CAmount preAllocAmount = AmountFromValueNoErr(preallocationValue[0]);
- if (preAllocAmount < 0)
+ if (preAllocAmount <= 0)
{
- LogPrintf("%s: preallocation must be greater than zero\n", __func__);
+ LogPrintf("%s: preallocation values must be greater than zero\n", __func__);
nVersion = PBAAS_VERSION_INVALID;
break;
}
obj.push_back(Pair("maxpreconversion", maxPreconvertArr));
}
- obj.push_back(Pair("launchfee", launchFee));
-
if (preAllocationRatio)
{
obj.push_back(Pair("preallocationratio", ValueFromAmount(preAllocationRatio)));
onePreAlloc.push_back(Pair(EncodeDestination(CIdentityID(onePreAllocation.first)), ValueFromAmount(onePreAllocation.second)));
preAllocationArr.push_back(onePreAlloc);
}
- obj.push_back(Pair("preallocations", preAllocationArr));
+ obj.push_back(Pair("preallocation", preAllocationArr));
}
if (contributions.size())
class CCurrencyDefinition
{
public:
+ static const int64_t DEFAULT_ID_REGISTRATION_AMOUNT = 10000000000;
+
enum ELimitsDefaults
{
MIN_PER_BLOCK_NOTARIZATION = 1000000, // 0.01 VRSC per block notarization minimum
MIN_BILLING_PERIOD = 960, // 16 hour minimum billing period for notarization, typically expect days/weeks/months
MIN_CURRENCY_LIFE = 480, // 8 hour minimum lifetime, which gives 8 hours of minimum billing to notarize conclusion
DEFAULT_OUTPUT_VALUE = 0, // 0 VRSC default output value
- DEFAULT_ID_REGISTRATION_AMOUNT = 10000000000,
DEFAULT_ID_REFERRAL_LEVELS = 3
};
// the interface to the currency controller. systemID refers to the controlling blockchain or currency that serves as a gateway
uint160 systemID; // native system of currency home, for BTC.VRSC: BTC, for VQUAD.VRSC: QUAD.VRSC, for QUAD.VRSC, QUAD.VRSC
CTransferDestination nativeCurrencyID; // ID of the currency in its native system
- ENotarizationProtocol notarizationProtocol; // method of notarization
- EProofProtocol proofProtocol; // method of proving imports and other elements
+ int32_t notarizationProtocol; // method of notarization
+ int32_t proofProtocol; // method of proving imports and other elements
int64_t idRegistrationAmount; // normal cost of ID registration
int32_t idReferralLevels; // number of referral levels to divide among
std::vector<int64_t> minPreconvert; // can be used for Kickstarter-like launch and return all non-network fees upon failure to meet minimum
std::vector<int64_t> maxPreconvert; // maximum amount of each reserve that can be pre-converted
- int32_t launchFee; // fee in ratio deducted from purchase before conversion. always 100000000 (100%) for non-reserve currencies
int32_t preAllocationRatio; // if non-zero, a ratio of the initial supply instead of a fixed number is used to calculate total preallocation
std::vector<std::pair<uint160, int64_t>> preAllocation; // pre-allocation recipients, from pre-allocation/premine, emitted after reserve weights are set
std::vector<int64_t> contributions; // initial contributions
ENotarizationProtocol NotarizationProtocol, EProofProtocol ProofProtocol, int64_t IDRegistrationAmount, int32_t IDReferralLevels,
const std::vector<uint160> &Notaries, int32_t MinNotariesConfirm, int32_t BillingPeriod, int64_t NotaryReward,
int32_t StartBlock, int32_t EndBlock, std::vector<uint160> Currencies, std::vector<int32_t> Weights,
- std::vector<int64_t> Conversions, std::vector<int64_t> MinPreconvert, std::vector<int64_t> MaxPreconvert, int32_t LaunchFee,
+ std::vector<int64_t> Conversions, std::vector<int64_t> MinPreconvert, std::vector<int64_t> MaxPreconvert,
int32_t PreAllocationRatio, std::vector<std::pair<uint160, int64_t>> PreAllocation, std::vector<int64_t> Contributions,
std::vector<int64_t> Preconverted, const std::vector<int64_t> &chainRewards, const std::vector<int64_t> &chainRewardsDecay,
const std::vector<int32_t> &chainHalving, const std::vector<int32_t> &chainEraEnd) :
conversions(Conversions),
minPreconvert(MinPreconvert),
maxPreconvert(MaxPreconvert),
- launchFee(LaunchFee),
preAllocationRatio(PreAllocationRatio),
preAllocation(PreAllocation),
contributions(Contributions),
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nVersion);
+ READWRITE(options);
READWRITE(parent);
READWRITE(name);
READWRITE(systemID);
READWRITE(nativeCurrencyID);
- READWRITE(VARINT((int32_t)notarizationProtocol));
- READWRITE(VARINT((int32_t)proofProtocol));
+ READWRITE(notarizationProtocol);
+ READWRITE(proofProtocol);
READWRITE(VARINT(idRegistrationAmount));
READWRITE(VARINT(idReferralLevels));
READWRITE(notaries);
READWRITE(conversions);
READWRITE(minPreconvert);
READWRITE(maxPreconvert);
- READWRITE(VARINT(launchFee));
READWRITE(VARINT(preAllocationRatio));
READWRITE(preAllocation);
READWRITE(contributions);
bool IsToken() const
{
- return !(ChainOptions() & OPTION_TOKEN);
+ return ChainOptions() & OPTION_TOKEN;
}
void SetToken(bool isToken)
if (parent != newIdentity.parent ||
name != newIdentity.name ||
(newIdentity.flags & ~(FLAG_REVOKED) != 0 && newIdentity.nVersion == VERSION_FIRSTVALID) ||
- (newIdentity.flags & ~(FLAG_REVOKED + FLAG_ACTIVECURRENCY) != 0 && newIdentity.nVersion == VERSION_PBAAS) ||
+ (newIdentity.flags & ~(FLAG_REVOKED + FLAG_ACTIVECURRENCY) != 0 && newIdentity.nVersion >= VERSION_PBAAS) ||
+ (flags & FLAG_ACTIVECURRENCY != 0 && newIdentity.flags & FLAG_ACTIVECURRENCY == 0) ||
newIdentity.nVersion < VERSION_FIRSTVALID ||
newIdentity.nVersion > VERSION_LASTVALID)
{
{
bool definitionFound = false;
nVersion = PBAAS_VERSION_INVALID;
- for (auto out : tx.vout)
+ for (auto &out : tx.vout)
{
COptCCParams p;
- if (IsPayToCryptoCondition(out.scriptPubKey, p))
+ if (out.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
{
if (p.evalCode == EVAL_CURRENCY_DEFINITION)
{
{
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("version", (int64_t)nVersion));
- ret.push_back(Pair("currencyid", EncodeDestination(CKeyID(currencyID))));
+ ret.push_back(Pair("currencyid", EncodeDestination(CIdentityID(currencyID))));
ret.push_back(Pair("value", ValueFromAmount(nValue)));
return ret;
}
return ret;
}
-CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags,
- const uint160 &nativeSource,
- CAmount transferTotal) const
+CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, CAmount transferTotal) const
{
CCurrencyValueMap feeMap;
return feeMap;
}
- feeMap.valueMap[nativeSource] = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 +
+ feeMap.valueMap[currencyID] = CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 +
((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
// add conversion fees in source currency for preconvert
return feeMap;
}
+CAmount CReserveTransfer::CalculateTransferFee(const CTransferDestination &destination)
+{
+ return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 +
+ ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
+}
+
CAmount CReserveTransfer::CalculateTransferFee() const
{
// determine fee for this send
{
return 0;
}
-
- return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 +
- ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
+ return CalculateTransferFee(destination);
}
CReserveExchange::CReserveExchange(const UniValue &uni) : CTokenOutput(uni)
CNameReservation nameReservation;
CIdentity identity;
+ CCurrencyDefinition newCurrencyDef;
flags |= IS_VALID;
// either a fully valid import with an export or the first import either in block 1 or the chain definition
// on the Verus chain
if ((nHeight == 1 && tx.IsCoinBase() && !IsVerusActive()) ||
- (CCurrencyDefinition(tx).IsValid() && IsVerusActive()) ||
+ ((newCurrencyDef = CCurrencyDefinition(tx)).IsValid() && IsVerusActive()) ||
(tx.vout.back().scriptPubKey.IsOpReturn() &&
(chainObjs = RetrieveOpRetArray(tx.vout.back().scriptPubKey)).size() == 1 &&
chainObjs[0]->objectType == CHAINOBJ_CROSSCHAINPROOF))
flags |= IS_REJECT;
}
}
+ // if this is a new currency definition, the currency def is the source of the pre-allocated output
+ // that will be "transferred" to the chain and materialize when the chain starts, assuming it met any
+ // minimums, etc.
+ if (newCurrencyDef.IsValid() && newCurrencyDef.IsToken() && newCurrencyDef.preAllocation.size())
+ {
+ // if we have pre-allocation, consider the output of pre-allocation the source of input to this transaction
+ for (auto &onePreAlloc : newCurrencyDef.preAllocation)
+ {
+ AddReserveInput(newCurrencyDef.GetID(), onePreAlloc.second);
+ }
+ }
}
else
{
flags |= IS_EXPORT;
}
break;
+
+ case EVAL_CURRENCY_DEFINITION:
+ {
+
+ }
+ break;
}
}
}
}
// includes all conversion fees of other currencies as well
- CCurrencyValueMap thisExpectedFees = curTransfer.CalculateFee(curTransfer.flags, currencySourceID, curTransfer.nValue);
- if (curTransfer.nFees < thisExpectedFees.valueMap[currencySourceID])
+ CCurrencyValueMap thisExpectedFees = curTransfer.CalculateFee(curTransfer.flags, curTransfer.nValue);
+ if (curTransfer.nFees < thisExpectedFees.valueMap[curTransfer.currencyID])
{
printf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
LogPrintf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
// naturally compress a full ID to an ID destination, since it is going back where it came from
CTxDestination sendBackAddr = TransferDestinationToDestination(curTransfer.destination);
- CAmount fees = curTransfer.CalculateFee(curTransfer.flags, currencySourceID, curTransfer.nValue).valueMap.begin()->second;
+ CAmount fees = curTransfer.CalculateFee(curTransfer.flags, curTransfer.nValue).valueMap.begin()->second;
CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID,
curTransfer.currencyID,
uint32_t flags; // type of transfer and options
CAmount nFees; // cross-chain network fees only, separated out to enable market conversions, conversion fees are additional
- uint160 destCurrencyID; // system to export to, which may represent a PBaaS chain or external bridge
+ uint160 destCurrencyID; // system to export to, which may represent a PBaaS chain or external bridge
CTransferDestination destination; // system specific address to send funds to on the target chain
CReserveTransfer(const std::vector<unsigned char> &asVector)
UniValue ToUniValue() const;
- CCurrencyValueMap CalculateFee(uint32_t flags,
- const uint160 &nativeSource,
- CAmount transferTotal) const;
+ CCurrencyValueMap CalculateFee(uint32_t flags, CAmount transferTotal) const;
+
+ static CAmount CalculateTransferFee(const CTransferDestination &destination);
CAmount CalculateTransferFee() const;
bool IsValid() const
{
- return nVersion > VERSION_INVALID && nVersion <= VERSION_LAST && !systemID.IsNull() && importValue.valueMap.size() != 0;
+ return nVersion > VERSION_INVALID && nVersion <= VERSION_LAST && !systemID.IsNull();
}
UniValue ToUniValue() const;
return nVersion > VERSION_INVALID &&
nVersion <= VERSION_LAST &&
!systemID.IsNull() &&
- totalAmounts.valueMap.size() &&
totalAmounts.valueMap.size() == totalFees.valueMap.size();
}
return retVal;
}
ParseSubNames(currencyStr, extraName, true);
- if (currencyStr.back() == '@' || extraName != "")
+ if (currencyStr.back() == '@' || (extraName != "" && boost::to_lower_copy(extraName) != boost::to_lower_copy(VERUS_CHAINNAME)))
{
return retVal;
}
" \"chainid\" : \"hex-string\", (string) 40 char string that represents the chain ID, calculated from the name\n"
" \"premine\" : \"n\", (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
" \"convertible\" : \"xxxx\" (bool) if this currency is a fractional reserve currency of Verus\n"
- " \"launchfee\" : \"n\", (int) (launchfee * total converted) / 100000000 sent directly to premine address\n"
" \"startblock\" : \"n\", (int) block # on this chain, which must be notarized into block one of the chain\n"
" \"endblock\" : \"n\", (int) block # after which, this chain's useful life is considered to be over\n"
" \"eras\" : \"[obj, ...]\", (objarray) different chain phases of rewards and convertibility\n"
" \"chainid\" : \"hex-string\", (string) 40 char string that represents the chain ID, calculated from the name\n"
" \"premine\" : \"n\", (int) amount of currency paid out to the premine address in block #1, may be smart distribution\n"
" \"convertible\" : \"xxxx\" (bool) if this currency is a fractional reserve currency of Verus\n"
- " \"launchfee\" : \"n\", (int) (launchfee * total converted) / 100000000 sent directly to premine address\n"
" \"startblock\" : \"n\", (int) block # on this chain, which must be notarized into block one of the chain\n"
" \"endblock\" : \"n\", (int) block # after which, this chain's useful life is considered to be over\n"
" \"eras\" : \"[obj, ...]\", (objarray) different chain phases of rewards and convertibility\n"
);
}
+ UniValue ret(UniValue::VARR);
+
// starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
// import transactions using the importtxtemplate as a template
CheckPBaaSAPIsValid();
if (chainID.IsNull())
{
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid chain name or chain ID");
+ return ret;
}
// starting from that last transaction id, see if we have any newer to export for the indicated chain, and if so, return them as
if (!GetCurrencyDefinition(chainID, chainDef))
{
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain definition not found");
+ return ret;
}
std::string lastImportHex = uni_get_str(find_value(params[0], "lastimporttx"));
chainObjs[0]->objectType == CHAINOBJ_CROSSCHAINPROOF))))
{
DeleteOpRetObjects(chainObjs);
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid last import tx");
+ return ret;
}
DeleteOpRetObjects(chainObjs);
std::vector<CTransaction> newImports;
if (!ConnectedChains.CreateLatestImports(chainDef, lastImportTx, templateTx, lastConfirmedNotarization, tokenImportAvailable, nativeImportAvailable, newImports))
{
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Failure creating new imports");
+ return ret;
}
- UniValue ret(UniValue::VARR);
for (auto import : newImports)
{
{
CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
- // convert full ID destinations to normal ID outputs, full ID is on this chain already
+ // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
if (rt.destination.type == CTransferDestination::DEST_FULLID)
{
CIdentity(rt.destination.destination);
}
else
{
- CCurrencyValueMap fees = rt.CalculateFee(rt.flags, ASSETCHAINS_CHAINID, rt.nValue + rt.nFees);
+ CCurrencyValueMap fees = rt.CalculateFee(rt.flags, rt.nValue + rt.nFees);
if (fees.valueMap.size() == 1)
{
ccx.totalFees.valueMap[rt.currencyID] += rt.nFees;
" \"options\" : \"n\", (int, optional) bits:\n"
" 1 = FRACTIONAL, 2 = IDRESTRICTED, 4 = IDSTAKING, 8 = IDREFERRALS\n"
" 0x10 = IDREFERRALSREQUIRED, 0x20 = TOKEN, 0x40 = CANBERESERVE\n"
- " \"identityname\" : \"xxxx\", (string, required) existing identity with no active or pending blockchain as the name\n"
+ " \"name\" : \"xxxx\", (string, required) name of existing identity with no active or pending blockchain\n"
" \"idregistrationprice\" : \"xx.xx\", (value, required) price of an identity in native currency\n"
" \"idreferrallevels\" : \"n\", (int, required) how many levels ID referrals go back in reward\n"
" \"minpreconvert\" : \"[\"xx.xx\",..]\", (list, optional) must be same size as currencies. minimum in each currency to launch\n"
" \"maxpreconvert\" : \"[\"xx.xx\",..]\", (list, optional) maximum in each currency allowed\n"
- " \"launchfee\" : \"xx.xx\", (value, optional) % fee for conversion at pre-launch\n"
" \"preallocationratio\" : \"xx.xx\", (value, optional) if non-0, pre-allocation is a percentage after initial supply is determined\n"
" \"preallocation\" : \"[{\"identity\":xx.xx}..]\", (list, optional) amount or % of from pre-allocation, depending on preallocationratio\n"
" \"initialcontribution\" : \"[\"xx.xx\",..]\", (list, optional) initial contribution in each currency\n"
" blocks after startblock (" + to_string(newChain.startBlock) + ")\n");
}
- if (newChain.billingPeriod < CCurrencyDefinition::MIN_BILLING_PERIOD || (newChain.notarizationReward / newChain.billingPeriod) < CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION)
+ if (!newChain.IsToken())
{
- throw JSONRPCError(RPC_INVALID_PARAMS, "Billing period of at least " +
- to_string(CCurrencyDefinition::MIN_BILLING_PERIOD) +
- " blocks and per-block notary rewards of >= " + to_string(CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION) +
- " are required to define an active currency\n");
- }
+ if (newChain.billingPeriod < CCurrencyDefinition::MIN_BILLING_PERIOD || (newChain.notarizationReward / newChain.billingPeriod) < CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION)
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMS, "Billing period of at least " +
+ to_string(CCurrencyDefinition::MIN_BILLING_PERIOD) +
+ " blocks and per-block notary rewards of >= " + to_string(CCurrencyDefinition::MIN_PER_BLOCK_NOTARIZATION) +
+ " are required to define an active currency\n");
+ }
- // TODO: check to see if rewards obviously lead to an unstable currency
- //for (int i = 0; i < newChain.rewards.size(); i++)
- //{
- //}
+ // TODO: check to see if rewards obviously lead to an unstable currency
+ //for (int i = 0; i < newChain.rewards.size(); i++)
+ //{
+ //}
- // if we have no emission parameters, this is not a PBaaS blockchain, it is a controlled or bridged token.
- // controlled tokens can be centrally or algorithmically controlled.
- if (newChain.rewards.empty() && !newChain.IsToken())
- {
- throw JSONRPCError(RPC_INVALID_PARAMS, "A currency must either be based on a token protocol or must specify blockchain rewards, even if 0\n");
- }
+ // if we have no emission parameters, this is not a PBaaS blockchain, it is a controlled or bridged token.
+ // controlled tokens can be centrally or algorithmically controlled.
+ if (newChain.rewards.empty())
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMS, "A currency must either be based on a token protocol or must specify blockchain rewards, even if 0\n");
+ }
- if ((newChain.rewards.empty() || newChain.currencies.empty()) && newChain.IsReserve())
- {
- throw JSONRPCError(RPC_INVALID_PARAMS, "Fractional reserve currencies must specify blockchain rewards, even if 0 and at least one reserve currency\n");
- }
+ if (newChain.currencies.empty() && newChain.IsReserve())
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMS, "Fractional reserve currencies must specify blockchain rewards, even if 0 and at least one reserve currency\n");
+ }
- // if this is a fractional reserve currency, ensure that all reserves are currently active
- // with at least as long of a life as this currency and that at least one of the currencies
- // is VRSC or VRSCTEST.
- std::vector<CCurrencyDefinition> reserveCurrencies;
- bool hasCoreReserve = false;
- if (newChain.IsReserve())
- {
- for (auto ¤cy : newChain.currencies)
+ // if this is a fractional reserve currency, ensure that all reserves are currently active
+ // with at least as long of a life as this currency and that at least one of the currencies
+ // is VRSC or VRSCTEST.
+ std::vector<CCurrencyDefinition> reserveCurrencies;
+ bool hasCoreReserve = false;
+ if (newChain.IsReserve())
{
- reserveCurrencies.emplace_back();
- if (!GetCurrencyDefinition(currency, reserveCurrencies.back()))
+ for (auto ¤cy : newChain.currencies)
{
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot find reserve currency " + EncodeDestination(CKeyID(currency)));
- }
+ reserveCurrencies.emplace_back();
+ if (!GetCurrencyDefinition(currency, reserveCurrencies.back()))
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot find reserve currency " + EncodeDestination(CKeyID(currency)));
+ }
- if (reserveCurrencies.back().endBlock && (!newChain.endBlock || reserveCurrencies.back().endBlock < newChain.endBlock))
- {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Reserve currency " + EncodeDestination(CKeyID(currency)) + " ends its life before the fractional currency's endblock");
- }
+ if (reserveCurrencies.back().endBlock && (!newChain.endBlock || reserveCurrencies.back().endBlock < newChain.endBlock))
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Reserve currency " + EncodeDestination(CKeyID(currency)) + " ends its life before the fractional currency's endblock");
+ }
- if (!reserveCurrencies.back().CanBeReserve())
- {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Currency " + EncodeDestination(CKeyID(currency)) + " may not be used as a reserve");
- }
+ if (!reserveCurrencies.back().CanBeReserve())
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Currency " + EncodeDestination(CKeyID(currency)) + " may not be used as a reserve");
+ }
- if (currency == VERUS_CHAINID)
- {
- hasCoreReserve = true;
+ if (currency == VERUS_CHAINID)
+ {
+ hasCoreReserve = true;
+ }
}
}
- }
-
- if (!hasCoreReserve)
- {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Fractional currency requires a reserve of " + VERUS_CHAINNAME + " in addition to any other reserves");
+ if (!hasCoreReserve)
+ {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Fractional currency requires a reserve of " + VERUS_CHAINNAME + " in addition to any other reserves");
+ }
}
// now, create the outputs:
dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_EXPORT))});
CCrossChainExport ccx = CCrossChainExport(newChainID, 0, CCurrencyValueMap(), CCurrencyValueMap());
- vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &ccx), &indexDests), 0, false});
+ vOutputs.push_back({MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests), 0, false});
+ std::set<CIdentityID> idExportSet;
if (!newChain.IsToken())
{
- // create identity exports for launch identity, notaries, and preallocation recipients
- std::set<CIdentityID> idExportSet = {newChainID};
+ idExportSet = std::set<CIdentityID>({newChainID});
+
+ // create chain transfer exports for launch identity, notaries, and preallocation recipients
for (auto ¬ary : newChain.notaries)
{
idExportSet.insert(notary);
idExportSet.insert(oneAlloc.first);
}
- // now, look them all up and create the exports
+ // now, look them all up and create exports for zero value to move the IDs
for (auto &oneID : idExportSet)
{
CIdentity oneIdentity = (oneID == newChainID) ? launchIdentity : CIdentity::LookupIdentity(oneID);
{
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid specified identity: " + EncodeDestination(CIdentityID(oneID)));
}
- CIdentityExport cie = CIdentityExport(oneID, ::GetHash(oneIdentity), thisChainID, CIdentityExport::DEFAULT_EXPORT_FEE, newChainID);
- cp = CCinit(&CC, EVAL_IDENTITY_EXPORT);
- CPubKey pk(ParseHex(CC.CChexstr));
-
- indexDests = std::vector<CTxDestination>({CKeyID(newChain.GetConditionID(EVAL_IDENTITY_EXPORT))});
- dests = std::vector<CTxDestination>({pk});
-
- vOutputs.push_back({MakeMofNCCScript(CConditionObj<CIdentityExport>(EVAL_IDENTITY_EXPORT, dests, 1, &cie), &indexDests),
- CIdentityExport::DEFAULT_EXPORT_FEE,
+ // emit a reserve exchange output
+ cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
+ CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
+
+ // send a zero output to this ID and pass the full ID to do so
+ std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
+ indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChain.systemID, EVAL_RESERVE_TRANSFER))});
+
+ CTransferDestination transferDest = IdentityToTransferDestination(oneIdentity);
+ CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID,
+ ASSETCHAINS_CHAINID,
+ 0,
+ CReserveTransfer::CalculateTransferFee(transferDest),
+ newChain.systemID,
+ transferDest);
+
+ vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests),
+ rt.nFees,
false});
}
- }
+ }
+ else
+ {
+ // output preallocation
+ for (auto &oneAlloc : newChain.preAllocation)
+ {
+ // emit a reserve exchange output
+ cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
+ CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
+
+ // send a zero output to this ID and pass the full ID to do so
+ std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
+ indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(newChain.systemID, EVAL_RESERVE_TRANSFER))});
+
+ CTransferDestination transferDest = DestinationToTransferDestination(CIdentityID(oneAlloc.first));
+ CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID,
+ newChain.GetID(),
+ oneAlloc.second,
+ CReserveTransfer::CalculateTransferFee(transferDest),
+ ASSETCHAINS_CHAINID,
+ transferDest);
+
+ vOutputs.push_back({MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), &indexDests),
+ rt.nFees,
+ false});
+ }
+ }
// make the outputs for initial contributions
if (newChain.contributions.size() && newChain.contributions.size() == newChain.currencies.size())
return retDest;
}
+CTransferDestination IdentityToTransferDestination(const CIdentity &identity)
+{
+ return CTransferDestination(CTransferDestination::DEST_FULLID, ::AsVector(identity));
+}
+
+CIdentity TransferDestinationToIdentity(const CTransferDestination &dest)
+{
+ CIdentity retIdentity;
+ switch (dest.type)
+ {
+ case CTransferDestination::DEST_FULLID:
+ {
+ ::FromVector(dest.destination, retIdentity);
+ break;
+ }
+ }
+ return retIdentity;
+}
+
std::vector<CTxDestination> TransferDestinationsToDestinations(const std::vector<CTransferDestination> &transferDests)
{
std::vector<CTxDestination> retDests;
#define OPRETTYPE_STAKEPARAMS2 6
class CCurrencyStateNew;
+class CIdentity;
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE_V2 = 1024;
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE_IDENTITY = 3073; // fulfillment maximum size + 1, MAKE SURE TO KEEP MAX_BINARY_CC_SIZE IN SYNC WITH THIS-1, BUF_SIZE in crypto conditions, should be >=
CTxDestination TransferDestinationToDestination(const CTransferDestination &trasnferDest);
CTransferDestination DestinationToTransferDestination(const CTxDestination &dest);
+CTransferDestination IdentityToTransferDestination(const CIdentity &identity);
+CIdentity TransferDestinationToIdentity(const CTransferDestination &dest);
std::vector<CTxDestination> TransferDestinationsToDestinations(const std::vector<CTransferDestination> &transferDests);
std::vector<CTransferDestination> DestinationsToTransferDestinations(const std::vector<CTxDestination> &dests);
for (int i = 0; i < pcoin->vout.size(); i++)
{
isminetype mine = IsMine(pcoin->vout[i]);
- if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
+ if (!(IsSpent(wtxid, i)) &&
+ mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
{
// don't return zero valued outputs
if (rOut.valueMap.size() || pcoin->vout[i].nValue)
{
- if ((pOnlyTheseCurrencies && pOnlyTheseCurrencies->Intersects(rOut)) || (fIncludeNative && pcoin->vout[i].nValue))
+ if ((rOut.valueMap.size() && (!pOnlyTheseCurrencies || (pOnlyTheseCurrencies && pOnlyTheseCurrencies->Intersects(rOut)))) ||
+ (fIncludeNative && pcoin->vout[i].nValue))
{
vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO));
}
++it;
}
retval = false;
- if ( targetReserveValues <= targetReserveValues.IntersectingValues(valueFromPresetInputs) && targetNativeValue <= nativeValueFromPresets )
+ if ( targetNativeValue <= nativeRet &&
+ targetReserveValues <= targetReserveValues.IntersectingValues(valueFromPresetInputs) && targetNativeValue <= nativeValueFromPresets )
retval = true;
else if ( SelectReserveCoinsMinConf(targetReserveValues, targetNativeValue, 1, 6, vCoins, setCoinsRet, valueRet, nativeRet) != 0 )
retval = true;
// fees can only be deducted from fractional reserve outputs on fractional currency blockchains, otherwise,
// Verus/Verustest must be used to cover fees.
bool isVerusActive = IsVerusActive();
- if (IsVerusActive())
- {
- strFailReason = _("Transactions that accept reserve currency input can only be created on PBaaS blockchains");
- return false;
- }
- else
+ if (!isVerusActive)
{
if (ConnectedChains.ReserveCurrencies().size())
{
return false;
}
+ CCurrencyDefinition newCurrency;
+
// make sure that there are recipients, all recipients expect reserve inputs, and amounts are all non-negative
BOOST_FOREACH (const CRecipient& recipient, vecSend)
{
+ COptCCParams p;
+ if (recipient.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CURRENCY_DEFINITION && p.vData.size() >= 1)
+ {
+ if (newCurrency.IsValid())
+ {
+ strFailReason = _("A normal transaction cannot define define multiple currencies");
+ return false;
+ }
+ newCurrency = CCurrencyDefinition(p.vData[0]);
+ }
CCurrencyValueMap values = recipient.scriptPubKey.ReserveOutValue();
CCurrencyValueMap zeroes = values - values; // zero values of the same currencies
strFailReason = _("Transaction output amounts must not be negative");
return false;
}
+
totalNativeOutput += recipient.nAmount;
totalReserveOutput += values;
}
}
+ if (newCurrency.IsValid())
+ {
+ totalReserveOutput.valueMap.erase(newCurrency.GetID());
+ }
+
wtxNew.fTimeReceivedIsTxTime = true;
wtxNew.BindWallet(this);
int nextBlockHeight = chainActive.Height() + 1;
p.evalCode == EVAL_RESERVE_OUTPUT ||
p.evalCode == EVAL_RESERVE_DEPOSIT ||
p.evalCode == EVAL_RESERVE_EXCHANGE ||
- p.evalCode == EVAL_RESERVE_TRANSFER ||
p.evalCode == EVAL_NONE)
{
// add all values to a native equivalent