From 2f416b1763e23e7b83c2cd7ffecf4df6152ea3e5 Mon Sep 17 00:00:00 2001 From: miketout Date: Sat, 11 Apr 2020 22:08:43 -0700 Subject: [PATCH] Fix initial chain definition at PBaaS activation --- src/miner.cpp | 5 + src/pbaas/crosschainrpc.cpp | 67 +++++++------ src/pbaas/crosschainrpc.h | 19 ++-- src/pbaas/identity.h | 3 +- src/pbaas/pbaas.cpp | 4 +- src/pbaas/reserves.cpp | 44 ++++++--- src/pbaas/reserves.h | 11 +-- src/rpc/pbaasrpc.cpp | 185 +++++++++++++++++++++--------------- src/script/script.cpp | 19 ++++ src/script/script.h | 3 + src/wallet/wallet.cpp | 35 +++++-- 11 files changed, 250 insertions(+), 145 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index e1390ee8e..297c8ceb9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -313,6 +313,11 @@ CPubKey GetScriptPublicKey(const CScript &scriptPubKey) 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 diff --git a/src/pbaas/crosschainrpc.cpp b/src/pbaas/crosschainrpc.cpp index 55a62d41c..a4a2bc4de 100644 --- a/src/pbaas/crosschainrpc.cpp +++ b/src/pbaas/crosschainrpc.cpp @@ -348,6 +348,31 @@ UniValue CNodeData::ToUniValue() const 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) { @@ -355,14 +380,9 @@ 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); @@ -373,6 +393,8 @@ CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj) } } + name = CleanName(name, parent); + options = (uint32_t)uni_get_int64(find_value(obj, "options")); idRegistrationAmount = AmountFromValueNoErr(find_value(obj, "idregistrationprice")); @@ -405,20 +427,8 @@ CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj) 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"); @@ -542,8 +552,7 @@ CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj) 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()) { @@ -611,7 +620,6 @@ CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj) } } - launchFee = AmountFromValueNoErr(find_value(obj, "launchfee")); preAllocationRatio = AmountFromValueNoErr(find_value(obj, "preallocationratio")); UniValue preallocationArr = find_value(obj, "preallocation"); @@ -624,23 +632,24 @@ CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj) 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; } @@ -834,8 +843,6 @@ UniValue CCurrencyDefinition::ToUniValue() const obj.push_back(Pair("maxpreconversion", maxPreconvertArr)); } - obj.push_back(Pair("launchfee", launchFee)); - if (preAllocationRatio) { obj.push_back(Pair("preallocationratio", ValueFromAmount(preAllocationRatio))); @@ -850,7 +857,7 @@ UniValue CCurrencyDefinition::ToUniValue() const 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()) diff --git a/src/pbaas/crosschainrpc.h b/src/pbaas/crosschainrpc.h index fa74d7726..069394c4f 100644 --- a/src/pbaas/crosschainrpc.h +++ b/src/pbaas/crosschainrpc.h @@ -156,6 +156,8 @@ public: 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 @@ -163,7 +165,6 @@ public: 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 }; @@ -206,8 +207,8 @@ public: // 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 @@ -231,7 +232,6 @@ public: std::vector minPreconvert; // can be used for Kickstarter-like launch and return all non-network fees upon failure to meet minimum std::vector 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> preAllocation; // pre-allocation recipients, from pre-allocation/premine, emitted after reserve weights are set std::vector contributions; // initial contributions @@ -259,7 +259,7 @@ public: ENotarizationProtocol NotarizationProtocol, EProofProtocol ProofProtocol, int64_t IDRegistrationAmount, int32_t IDReferralLevels, const std::vector &Notaries, int32_t MinNotariesConfirm, int32_t BillingPeriod, int64_t NotaryReward, int32_t StartBlock, int32_t EndBlock, std::vector Currencies, std::vector Weights, - std::vector Conversions, std::vector MinPreconvert, std::vector MaxPreconvert, int32_t LaunchFee, + std::vector Conversions, std::vector MinPreconvert, std::vector MaxPreconvert, int32_t PreAllocationRatio, std::vector> PreAllocation, std::vector Contributions, std::vector Preconverted, const std::vector &chainRewards, const std::vector &chainRewardsDecay, const std::vector &chainHalving, const std::vector &chainEraEnd) : @@ -284,7 +284,6 @@ public: conversions(Conversions), minPreconvert(MinPreconvert), maxPreconvert(MaxPreconvert), - launchFee(LaunchFee), preAllocationRatio(PreAllocationRatio), preAllocation(PreAllocation), contributions(Contributions), @@ -305,12 +304,13 @@ public: template 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); @@ -324,7 +324,6 @@ public: READWRITE(conversions); READWRITE(minPreconvert); READWRITE(maxPreconvert); - READWRITE(VARINT(launchFee)); READWRITE(VARINT(preAllocationRatio)); READWRITE(preAllocation); READWRITE(contributions); @@ -382,7 +381,7 @@ public: bool IsToken() const { - return !(ChainOptions() & OPTION_TOKEN); + return ChainOptions() & OPTION_TOKEN; } void SetToken(bool isToken) diff --git a/src/pbaas/identity.h b/src/pbaas/identity.h index 6063e0b0e..c5a9964f0 100644 --- a/src/pbaas/identity.h +++ b/src/pbaas/identity.h @@ -452,7 +452,8 @@ public: 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) { diff --git a/src/pbaas/pbaas.cpp b/src/pbaas/pbaas.cpp index b26a0daa0..5f12a2413 100644 --- a/src/pbaas/pbaas.cpp +++ b/src/pbaas/pbaas.cpp @@ -412,10 +412,10 @@ CCurrencyDefinition::CCurrencyDefinition(const CTransaction &tx) { 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) { diff --git a/src/pbaas/reserves.cpp b/src/pbaas/reserves.cpp index a8f02f522..e8e0e5085 100644 --- a/src/pbaas/reserves.cpp +++ b/src/pbaas/reserves.cpp @@ -384,7 +384,7 @@ UniValue CTokenOutput::ToUniValue() const { 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; } @@ -425,9 +425,7 @@ UniValue CReserveTransfer::ToUniValue() const 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; @@ -437,7 +435,7 @@ CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, 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 @@ -449,6 +447,12 @@ CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, 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 @@ -456,9 +460,7 @@ CAmount CReserveTransfer::CalculateTransferFee() const { 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) @@ -1542,6 +1544,7 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction CNameReservation nameReservation; CIdentity identity; + CCurrencyDefinition newCurrencyDef; flags |= IS_VALID; @@ -1676,7 +1679,7 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction // 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)) @@ -1715,6 +1718,17 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction 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 { @@ -1746,6 +1760,12 @@ CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction flags |= IS_EXPORT; } break; + + case EVAL_CURRENCY_DEFINITION: + { + + } + break; } } } @@ -1877,8 +1897,8 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const uint16 } // 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()); @@ -1987,7 +2007,7 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const uint16 // 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, diff --git a/src/pbaas/reserves.h b/src/pbaas/reserves.h index bb4be46e4..767101a92 100644 --- a/src/pbaas/reserves.h +++ b/src/pbaas/reserves.h @@ -176,7 +176,7 @@ public: 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 &asVector) @@ -207,9 +207,9 @@ public: 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; @@ -336,7 +336,7 @@ public: 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; @@ -391,7 +391,6 @@ public: return nVersion > VERSION_INVALID && nVersion <= VERSION_LAST && !systemID.IsNull() && - totalAmounts.valueMap.size() && totalAmounts.valueMap.size() == totalFees.valueMap.size(); } diff --git a/src/rpc/pbaasrpc.cpp b/src/rpc/pbaasrpc.cpp index 1da36901a..0aa7d6c5a 100644 --- a/src/rpc/pbaasrpc.cpp +++ b/src/rpc/pbaasrpc.cpp @@ -816,7 +816,7 @@ uint160 ValidateCurrencyName(std::string currencyStr, CCurrencyDefinition *pCurr 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; } @@ -866,7 +866,6 @@ UniValue getcurrencydefinition(const UniValue& params, bool fHelp) " \"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" @@ -1264,7 +1263,6 @@ UniValue getdefinedchains(const UniValue& params, bool fHelp) " \"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" @@ -3320,6 +3318,8 @@ UniValue getlatestimportsout(const UniValue& params, bool fHelp) ); } + 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(); @@ -3330,7 +3330,7 @@ UniValue getlatestimportsout(const UniValue& params, bool fHelp) 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 @@ -3339,7 +3339,7 @@ UniValue getlatestimportsout(const UniValue& params, bool fHelp) 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")); @@ -3365,7 +3365,7 @@ UniValue getlatestimportsout(const UniValue& params, bool fHelp) chainObjs[0]->objectType == CHAINOBJ_CROSSCHAINPROOF)))) { DeleteOpRetObjects(chainObjs); - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid last import tx"); + return ret; } DeleteOpRetObjects(chainObjs); @@ -3373,9 +3373,8 @@ UniValue getlatestimportsout(const UniValue& params, bool fHelp) std::vector 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) { @@ -3602,7 +3601,7 @@ bool RefundFailedLaunch(uint160 currencyID, CTransaction &lastImportTx, std::vec { CReserveTransfer &rt = ((CChainObject *)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); @@ -3622,7 +3621,7 @@ bool RefundFailedLaunch(uint160 currencyID, CTransaction &lastImportTx, std::vec } 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; @@ -3997,7 +3996,7 @@ UniValue definecurrency(const UniValue& params, bool fHelp) " \"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" @@ -4015,7 +4014,6 @@ UniValue definecurrency(const UniValue& params, bool fHelp) " \"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" @@ -4174,66 +4172,68 @@ UniValue definecurrency(const UniValue& params, bool fHelp) " 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 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 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: @@ -4382,12 +4382,14 @@ UniValue definecurrency(const UniValue& params, bool fHelp) dests = std::vector({CPubKey(ParseHex(CC.CChexstr))}); indexDests = std::vector({CKeyID(CCrossChainRPCData::GetConditionID(newChainID, EVAL_CROSSCHAIN_EXPORT))}); CCrossChainExport ccx = CCrossChainExport(newChainID, 0, CCurrencyValueMap(), CCurrencyValueMap()); - vOutputs.push_back({MakeMofNCCScript(CConditionObj(EVAL_CROSSCHAIN_IMPORT, dests, 1, &ccx), &indexDests), 0, false}); + vOutputs.push_back({MakeMofNCCScript(CConditionObj(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests), 0, false}); + std::set idExportSet; if (!newChain.IsToken()) { - // create identity exports for launch identity, notaries, and preallocation recipients - std::set idExportSet = {newChainID}; + idExportSet = std::set({newChainID}); + + // create chain transfer exports for launch identity, notaries, and preallocation recipients for (auto ¬ary : newChain.notaries) { idExportSet.insert(notary); @@ -4397,7 +4399,7 @@ UniValue definecurrency(const UniValue& params, bool fHelp) 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); @@ -4405,19 +4407,54 @@ UniValue definecurrency(const UniValue& params, bool fHelp) { 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({CKeyID(newChain.GetConditionID(EVAL_IDENTITY_EXPORT))}); - dests = std::vector({pk}); - - vOutputs.push_back({MakeMofNCCScript(CConditionObj(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 dests = std::vector({pk.GetID()}); + indexDests = std::vector({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(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 dests = std::vector({pk.GetID()}); + indexDests = std::vector({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(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()) diff --git a/src/script/script.cpp b/src/script/script.cpp index 603542ac9..23adb6cb3 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -234,6 +234,25 @@ CTransferDestination DestinationToTransferDestination(const CTxDestination &dest 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 TransferDestinationsToDestinations(const std::vector &transferDests) { std::vector retDests; diff --git a/src/script/script.h b/src/script/script.h index 78b7d2374..edfcb9043 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -29,6 +29,7 @@ #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 >= @@ -419,6 +420,8 @@ typedef boost::variant TransferDestinationsToDestinations(const std::vector &transferDests); std::vector DestinationsToTransferDestinations(const std::vector &dests); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f2a611bda..617dbff64 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4750,7 +4750,8 @@ void CWallet::AvailableReserveCoins(vector& vCoins, bool fOnlyConfirmed 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))) { @@ -4787,7 +4788,8 @@ void CWallet::AvailableReserveCoins(vector& vCoins, bool fOnlyConfirmed // 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)); } @@ -5359,7 +5361,8 @@ bool CWallet::SelectReserveCoins(const CCurrencyValueMap& targetReserveValues, ++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; @@ -5792,12 +5795,7 @@ bool CWallet::CreateReserveTransaction(const vector& vecSend, CWalle // 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()) { @@ -5815,9 +5813,21 @@ bool CWallet::CreateReserveTransaction(const vector& vecSend, CWalle 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 @@ -5831,6 +5841,7 @@ bool CWallet::CreateReserveTransaction(const vector& vecSend, CWalle strFailReason = _("Transaction output amounts must not be negative"); return false; } + totalNativeOutput += recipient.nAmount; totalReserveOutput += values; @@ -5853,6 +5864,11 @@ bool CWallet::CreateReserveTransaction(const vector& vecSend, CWalle } } + if (newCurrency.IsValid()) + { + totalReserveOutput.valueMap.erase(newCurrency.GetID()); + } + wtxNew.fTimeReceivedIsTxTime = true; wtxNew.BindWallet(this); int nextBlockHeight = chainActive.Height() + 1; @@ -5990,7 +6006,6 @@ bool CWallet::CreateReserveTransaction(const vector& vecSend, CWalle 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 -- 2.42.0