diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index e52c75ca1e..78e49b5bba 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -35,6 +35,7 @@ static const std::regex ROOT_NAME_CHARACTERS("^[A-Z0-9._]{3,}$"); static const std::regex SUB_NAME_CHARACTERS("^[A-Z0-9._]+$"); static const std::regex UNIQUE_TAG_CHARACTERS("^[-A-Za-z0-9@$%&*()[\\]{}<>_.;?\\\\:]+$"); static const std::regex CHANNEL_TAG_CHARACTERS("^[A-Z0-9._]+$"); +static const std::regex VOTE_TAG_CHARACTERS("^[A-Z0-9._]+$"); static const std::regex DOUBLE_PUNCTUATION("^.*[._]{2,}.*$"); static const std::regex LEADING_PUNCTUATION("^[._].*$"); @@ -43,12 +44,14 @@ static const std::regex TRAILING_PUNCTUATION("^.*[._]$"); static const std::string SUB_NAME_DELIMITER = "/"; static const std::string UNIQUE_TAG_DELIMITER = "#"; static const std::string CHANNEL_TAG_DELIMITER = "~"; +static const std::string VOTE_TAG_DELIMITER = "^"; -static const std::regex UNIQUE_INDICATOR("^[^#]+#[^#]+$"); -static const std::regex CHANNEL_INDICATOR("^[^~]+~[^~]+$"); -static const std::regex OWNER_INDICATOR("^[^!]+!$"); +static const std::regex UNIQUE_INDICATOR(R"(^[^^~#!]+#[^~#!\/]+$)"); +static const std::regex CHANNEL_INDICATOR(R"(^[^^~#!]+~[^~#!\/]+$)"); +static const std::regex OWNER_INDICATOR(R"(^[^^~#!]+!$)"); +static const std::regex VOTE_INDICATOR(R"(^[^^~#!]+\^[^~#!\/]+$)"); -static const std::regex RAVEN_NAMES("^RVN|^RAVEN|^RAVENCOIN|^RAVENC0IN|^RAVENCO1N|^RAVENC01N"); +static const std::regex RAVEN_NAMES("^RVN$|^RAVEN$|^RAVENCOIN$|^RAVENC0IN$|^RAVENCO1N$|^RAVENC01N$"); bool IsRootNameValid(const std::string& name) { @@ -72,6 +75,11 @@ bool IsUniqueTagValid(const std::string& tag) return std::regex_match(tag, UNIQUE_TAG_CHARACTERS); } +bool IsVoteTagValid(const std::string& tag) +{ + return std::regex_match(tag, VOTE_TAG_CHARACTERS); +} + bool IsChannelTagValid(const std::string& tag) { return std::regex_match(tag, CHANNEL_TAG_CHARACTERS) @@ -138,6 +146,15 @@ bool IsAssetNameValid(const std::string& name, AssetType& assetType) if (!valid) return false; assetType = AssetType::OWNER; return true; + } else if (std::regex_match(name, VOTE_INDICATOR)) + { + if (name.size() > MAX_NAME_LENGTH) return false; + std::vector parts; + boost::split(parts, name, boost::is_any_of(VOTE_TAG_DELIMITER)); + bool valid = IsNameValidBeforeTag(parts.front()) && IsVoteTagValid(parts.back()); + if (!valid) return false; + assetType = AssetType::VOTE; + return true; } else { @@ -160,6 +177,32 @@ bool IsAssetNameAnOwner(const std::string& name) return IsAssetNameValid(name) && std::regex_match(name, OWNER_INDICATOR); } +std::string GetParentName(const std::string& name) +{ + AssetType type; + if (!IsAssetNameValid(name, type)) + return ""; + + auto index = std::string::npos; + if (type == AssetType::SUB) { + index = name.find_last_of(SUB_NAME_DELIMITER); + } else if (type == AssetType::UNIQUE) { + index = name.find_last_of(UNIQUE_TAG_DELIMITER); + } else if (type == AssetType::MSGCHANNEL) { + index = name.find_last_of(CHANNEL_TAG_DELIMITER); + } else if (type == AssetType::VOTE) { + index = name.find_last_of(VOTE_TAG_DELIMITER); + } else if (type == AssetType::ROOT) + return name; + + if (std::string::npos != index) + { + return name.substr(0, index); + } + + return name; +} + bool CNewAsset::IsNull() const { return strName == ""; @@ -195,6 +238,9 @@ bool CNewAsset::IsValid(std::string& strError, CAssetsCache& assetCache, bool fC if (!IsAssetNameValid(std::string(strName))) strError = "Invalid parameter: asset_name must only consist of valid characters and have a size between 3 and 30 characters. See help for more details."; + if (IsAssetNameAnOwner(std::string(strName))) + strError = "Invalid parameters: asset_name can't have a '!' at the end of it. See help for more details."; + if (nAmount <= 0) strError = "Invalid parameter: asset amount can't be equal to or less than zero."; @@ -364,7 +410,8 @@ bool OwnerFromTransaction(const CTransaction& tx, std::string& ownerName, std::s bool TransferAssetFromScript(const CScript& scriptPubKey, CAssetTransfer& assetTransfer, std::string& strAddress) { - if (!IsScriptTransferAsset(scriptPubKey)) + int nStartingIndex = 0; + if (!IsScriptTransferAsset(scriptPubKey, nStartingIndex)) return false; CTxDestination destination; @@ -388,7 +435,8 @@ bool TransferAssetFromScript(const CScript& scriptPubKey, CAssetTransfer& assetT bool AssetFromScript(const CScript& scriptPubKey, CNewAsset& assetNew, std::string& strAddress) { - if (!IsScriptNewAsset(scriptPubKey)) + int nStartingIndex = 0; + if (!IsScriptNewAsset(scriptPubKey, nStartingIndex)) return false; CTxDestination destination; @@ -397,7 +445,7 @@ bool AssetFromScript(const CScript& scriptPubKey, CNewAsset& assetNew, std::stri strAddress = EncodeDestination(destination); std::vector vchNewAsset; - vchNewAsset.insert(vchNewAsset.end(), scriptPubKey.begin() + 31, scriptPubKey.end()); + vchNewAsset.insert(vchNewAsset.end(), scriptPubKey.begin() + nStartingIndex, scriptPubKey.end()); CDataStream ssAsset(vchNewAsset, SER_NETWORK, PROTOCOL_VERSION); try { @@ -412,7 +460,8 @@ bool AssetFromScript(const CScript& scriptPubKey, CNewAsset& assetNew, std::stri bool OwnerAssetFromScript(const CScript& scriptPubKey, std::string& assetName, std::string& strAddress) { - if (!IsScriptOwnerAsset(scriptPubKey)) + int nStartingIndex = 0; + if (!IsScriptOwnerAsset(scriptPubKey, nStartingIndex)) return false; CTxDestination destination; @@ -421,7 +470,7 @@ bool OwnerAssetFromScript(const CScript& scriptPubKey, std::string& assetName, s strAddress = EncodeDestination(destination); std::vector vchOwnerAsset; - vchOwnerAsset.insert(vchOwnerAsset.end(), scriptPubKey.begin() + 31, scriptPubKey.end()); + vchOwnerAsset.insert(vchOwnerAsset.end(), scriptPubKey.begin() + nStartingIndex, scriptPubKey.end()); CDataStream ssOwner(vchOwnerAsset, SER_NETWORK, PROTOCOL_VERSION); try { @@ -436,7 +485,8 @@ bool OwnerAssetFromScript(const CScript& scriptPubKey, std::string& assetName, s bool ReissueAssetFromScript(const CScript& scriptPubKey, CReissueAsset& reissue, std::string& strAddress) { - if (!IsScriptReissueAsset(scriptPubKey)) + int nStartingIndex = 0; + if (!IsScriptReissueAsset(scriptPubKey, nStartingIndex)) return false; CTxDestination destination; @@ -444,9 +494,9 @@ bool ReissueAssetFromScript(const CScript& scriptPubKey, CReissueAsset& reissue, strAddress = EncodeDestination(destination); - std::vector vchTransferAsset; - vchTransferAsset.insert(vchTransferAsset.end(), scriptPubKey.begin() + 31, scriptPubKey.end()); - CDataStream ssReissue(vchTransferAsset, SER_NETWORK, PROTOCOL_VERSION); + std::vector vchReissueAsset; + vchReissueAsset.insert(vchReissueAsset.end(), scriptPubKey.begin() + nStartingIndex, scriptPubKey.end()); + CDataStream ssReissue(vchReissueAsset, SER_NETWORK, PROTOCOL_VERSION); try { ssReissue >> reissue; @@ -460,7 +510,7 @@ bool ReissueAssetFromScript(const CScript& scriptPubKey, CReissueAsset& reissue, bool CTransaction::IsNewAsset() const { - // Reissuing an Asset must contain at least 3 CTxOut( Raven Burn Tx, Any Number of other Outputs ..., Owner Asset Change Tx, Reissue Tx) + // Issuing an Asset must contain at least 3 CTxOut( Raven Burn Tx, Any Number of other Outputs ..., Owner Asset Change Tx, Reissue Tx) if (vout.size() < 3) return false; @@ -472,9 +522,18 @@ bool CTransaction::IsNewAsset() const if (!CheckOwnerDataTx(vout[vout.size() - 2])) return false; + // Get the asset type + CNewAsset asset; + std::string address; + if (!AssetFromScript(vout[vout.size() - 1].scriptPubKey, asset, address)) + return error("%s : Failed to get new asset from transaction: %s", __func__, this->GetHash().GetHex()); + + AssetType assetType; + IsAssetNameValid(asset.strName, assetType); + // Check for the Burn CTxOut in one of the vouts ( This is needed because the change CTxOut is places in a random position in the CWalletTx for (auto out : vout) - if (CheckIssueBurnTx(out)) + if (CheckIssueBurnTx(out, assetType)) return true; return false; @@ -652,7 +711,10 @@ void CAssetsCache::AddToAssetBalance(const std::string& strName, const std::stri mapAssetsAddressAmount.insert(make_pair(pair, 0)); // Add the new amount to the balance - mapAssetsAddressAmount.at(pair) += nAmount; + if (IsAssetNameAnOwner(strName)) + mapAssetsAddressAmount.at(pair) = OWNER_ASSET_AMOUNT; + else + mapAssetsAddressAmount.at(pair) += nAmount; // Add to map of addresses if (!mapAssetsAddresses.count(strName)) { @@ -680,28 +742,35 @@ bool CAssetsCache::TrySpendCoin(const COutPoint& out, const CTxOut& txOut) std::string assetName = ""; CAmount nAmount = -1; - // Get the New Asset or Transfer Asset from the scriptPubKey - if (txOut.scriptPubKey.IsNewAsset()) { - CNewAsset asset; - if (AssetFromScript(txOut.scriptPubKey, asset, address)) { - assetName = asset.strName; - nAmount = asset.nAmount; - } - } else if (txOut.scriptPubKey.IsTransferAsset()) { - CAssetTransfer transfer; - if (TransferAssetFromScript(txOut.scriptPubKey, transfer, address)) { - assetName = transfer.strName; - nAmount = transfer.nAmount; - } - } else if (txOut.scriptPubKey.IsOwnerAsset()) { - if (!OwnerAssetFromScript(txOut.scriptPubKey, assetName, address)) - return error("%s : ERROR Failed to get owner asset from the OutPoint: %s", __func__, out.ToString()); - nAmount = OWNER_ASSET_AMOUNT; - } else if (txOut.scriptPubKey.IsReissueAsset()) { - CReissueAsset reissue; - if (ReissueAssetFromScript(txOut.scriptPubKey, reissue, address)) { - assetName = reissue.strName; - nAmount = reissue.nAmount; + int nType = 0; + bool fIsOwner = false; + if (txOut.scriptPubKey.IsAssetScript(nType, fIsOwner)) { + txnouttype type = (txnouttype)nType; + + // Get the New Asset or Transfer Asset from the scriptPubKey + if (type == TX_NEW_ASSET && !fIsOwner) { + CNewAsset asset; + if (AssetFromScript(txOut.scriptPubKey, asset, address)) { + assetName = asset.strName; + nAmount = asset.nAmount; + } + } else if (type == TX_TRANSFER_ASSET) { + CAssetTransfer transfer; + if (TransferAssetFromScript(txOut.scriptPubKey, transfer, address)) { + assetName = transfer.strName; + nAmount = transfer.nAmount; + } + } else if (type == TX_NEW_ASSET && fIsOwner) { + if (!OwnerAssetFromScript(txOut.scriptPubKey, assetName, address)) + return error("%s : ERROR Failed to get owner asset from the OutPoint: %s", __func__, + out.ToString()); + nAmount = OWNER_ASSET_AMOUNT; + } else if (type == TX_REISSUE_ASSET) { + CReissueAsset reissue; + if (ReissueAssetFromScript(txOut.scriptPubKey, reissue, address)) { + assetName = reissue.strName; + nAmount = reissue.nAmount; + } } } @@ -791,36 +860,49 @@ bool CAssetsCache::UndoAssetCoin(const Coin& coin, const COutPoint& out) CAmount nAmount = 0; // Get the asset tx from the script - if (IsScriptNewAsset(coin.out.scriptPubKey)) { - CNewAsset asset; - if (!AssetFromScript(coin.out.scriptPubKey, asset, strAddress)) { - return error("%s : Failed to get asset from script while trying to undo asset spend. OutPoint : %s", - __func__, - out.ToString()); - } - assetName = asset.strName; + txnouttype type; + int nType = 0; + bool fIsOwner = false; + if(coin.out.scriptPubKey.IsAssetScript(nType, fIsOwner)) { + type = (txnouttype) nType; + + if (type == TX_NEW_ASSET && !fIsOwner) { + CNewAsset asset; + if (!AssetFromScript(coin.out.scriptPubKey, asset, strAddress)) { + return error("%s : Failed to get asset from script while trying to undo asset spend. OutPoint : %s", + __func__, + out.ToString()); + } + assetName = asset.strName; - nAmount = asset.nAmount; - } else if (IsScriptTransferAsset(coin.out.scriptPubKey)) { - CAssetTransfer transfer; - if (!TransferAssetFromScript(coin.out.scriptPubKey, transfer, strAddress)) - return error("%s : Failed to get transfer asset from script while trying to undo asset spend. OutPoint : %s", __func__, - out.ToString()); - - assetName = transfer.strName; - nAmount = transfer.nAmount; - } else if (IsScriptOwnerAsset(coin.out.scriptPubKey)) { - std::string ownerName; - if (!OwnerAssetFromScript(coin.out.scriptPubKey, ownerName, strAddress)) - return error("%s : Failed to get owner asset from script while trying to undo asset spend. OutPoint : %s", __func__, out.ToString()); - assetName = ownerName; - nAmount = OWNER_ASSET_AMOUNT; - } else if (IsScriptReissueAsset(coin.out.scriptPubKey)) { - CReissueAsset reissue; - if (!ReissueAssetFromScript(coin.out.scriptPubKey, reissue, strAddress)) - return error("%s : Failed to get reissue asset from script while trying to undo asset spend. OutPoint : %s", __func__, out.ToString()); - assetName = reissue.strName; - nAmount = reissue.nAmount; + nAmount = asset.nAmount; + } else if (type == TX_TRANSFER_ASSET) { + CAssetTransfer transfer; + if (!TransferAssetFromScript(coin.out.scriptPubKey, transfer, strAddress)) + return error( + "%s : Failed to get transfer asset from script while trying to undo asset spend. OutPoint : %s", + __func__, + out.ToString()); + + assetName = transfer.strName; + nAmount = transfer.nAmount; + } else if (type == TX_NEW_ASSET && fIsOwner) { + std::string ownerName; + if (!OwnerAssetFromScript(coin.out.scriptPubKey, ownerName, strAddress)) + return error( + "%s : Failed to get owner asset from script while trying to undo asset spend. OutPoint : %s", + __func__, out.ToString()); + assetName = ownerName; + nAmount = OWNER_ASSET_AMOUNT; + } else if (type == TX_REISSUE_ASSET) { + CReissueAsset reissue; + if (!ReissueAssetFromScript(coin.out.scriptPubKey, reissue, strAddress)) + return error( + "%s : Failed to get reissue asset from script while trying to undo asset spend. OutPoint : %s", + __func__, out.ToString()); + assetName = reissue.strName; + nAmount = reissue.nAmount; + } } if (assetName == "" || strAddress == "" || nAmount == 0) @@ -1228,15 +1310,18 @@ bool CAssetsCache::Flush(bool fSoftCopy, bool fFlushDB) // Add the new owners to database for (auto ownerAsset : setNewOwnerAssetsToAdd) { - if (!passetsdb->WriteAssetAddressQuantity(ownerAsset.assetName, ownerAsset.address, - OWNER_ASSET_AMOUNT)) { - dirty = true; - message = "_Failed Writing Owner Address Balance to database"; - } + auto pair = std::make_pair(ownerAsset.assetName, ownerAsset.address); + if (mapAssetsAddressAmount.count(pair)) { + if (!passetsdb->WriteAssetAddressQuantity(ownerAsset.assetName, ownerAsset.address, + mapAssetsAddressAmount.at(pair))) { + dirty = true; + message = "_Failed Writing Owner Address Balance to database"; + } - if (dirty) { - return error("%s : %s", __func__, message); - } + if (dirty) { + return error("%s : %s", __func__, message); + } + } } // Undo the transfering by updating the balances in the database @@ -1419,10 +1504,26 @@ bool IsAssetUnitsValid(const CAmount& units) return false; } -bool CheckIssueBurnTx(const CTxOut& txOut) +bool CheckIssueBurnTx(const CTxOut& txOut, const AssetType& type) { - // Check the first transaction is the 500 Burn amount to the burn address - if (txOut.nValue != GetIssueAssetBurnAmount()) + CAmount burnAmount = 0; + std::string burnAddress = ""; + + if (type == AssetType::SUB) { + burnAmount = GetIssueSubAssetBurnAmount(); + burnAddress = Params().IssueSubAssetBurnAddress(); + } else if (type == AssetType::ROOT) { + burnAmount = GetIssueAssetBurnAmount(); + burnAddress = Params().IssueAssetBurnAddress(); + } else if (type == AssetType::UNIQUE) { + burnAmount = GetIssueUniqueAssetBurnAmount(); + burnAddress = Params().IssueUniqueAssetBurnAddress(); + } else { + return false; + } + + // Check the first transaction for the required Burn Amount for the asset type + if (!(txOut.nValue == burnAmount)) return false; // Extract the destination @@ -1435,7 +1536,8 @@ bool CheckIssueBurnTx(const CTxOut& txOut) return false; // Check destination address is the burn address - if (EncodeDestination(destination) != Params().IssueAssetBurnAddress()) + auto strDestination = EncodeDestination(destination); + if (!(strDestination == burnAddress)) return false; return true; @@ -1468,7 +1570,8 @@ bool CheckIssueDataTx(const CTxOut& txOut) // Verify 'rvnq' is in the transaction CScript scriptPubKey = txOut.scriptPubKey; - return IsScriptNewAsset(scriptPubKey); + int nStartingIndex = 0; + return IsScriptNewAsset(scriptPubKey, nStartingIndex); } bool CheckReissueDataTx(const CTxOut& txOut) @@ -1497,10 +1600,16 @@ bool CheckTransferOwnerTx(const CTxOut& txOut) bool IsScriptNewAsset(const CScript& scriptPubKey) { - if (scriptPubKey.size() > 39) { - if (scriptPubKey[25] == OP_RVN_ASSET && scriptPubKey[27] == RVN_R && scriptPubKey[28] == RVN_V && scriptPubKey[29] == RVN_N && scriptPubKey[30] == RVN_Q) { - return true; - } + int index = 0; + return IsScriptNewAsset(scriptPubKey, index); +} + +bool IsScriptNewAsset(const CScript& scriptPubKey, int& nStartingIndex) +{ + int nType = 0; + bool fIsOwner =false; + if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + return (txnouttype)nType == TX_NEW_ASSET && !fIsOwner; } return false; @@ -1508,10 +1617,17 @@ bool IsScriptNewAsset(const CScript& scriptPubKey) bool IsScriptOwnerAsset(const CScript& scriptPubKey) { - if (scriptPubKey.size() > 30) { - if (scriptPubKey[25] == OP_RVN_ASSET && scriptPubKey[27] == RVN_R && scriptPubKey[28] == RVN_V && scriptPubKey[29] == RVN_N && scriptPubKey[30] == RVN_O) { - return true; - } + + int index = 0; + return IsScriptOwnerAsset(scriptPubKey, index); +} + +bool IsScriptOwnerAsset(const CScript& scriptPubKey, int& nStartingIndex) +{ + int nType = 0; + bool fIsOwner =false; + if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + return (txnouttype)nType == TX_NEW_ASSET && fIsOwner; } return false; @@ -1519,10 +1635,16 @@ bool IsScriptOwnerAsset(const CScript& scriptPubKey) bool IsScriptReissueAsset(const CScript& scriptPubKey) { - if (scriptPubKey.size() > 39) { - if (scriptPubKey[25] == OP_RVN_ASSET && scriptPubKey[27] == RVN_R && scriptPubKey[28] == RVN_V && scriptPubKey[29] == RVN_N && scriptPubKey[30] == RVN_R) { - return true; - } + int index = 0; + return IsScriptReissueAsset(scriptPubKey, index); +} + +bool IsScriptReissueAsset(const CScript& scriptPubKey, int& nStartingIndex) +{ + int nType = 0; + bool fIsOwner =false; + if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + return (txnouttype)nType == TX_REISSUE_ASSET; } return false; @@ -1530,10 +1652,16 @@ bool IsScriptReissueAsset(const CScript& scriptPubKey) bool IsScriptTransferAsset(const CScript& scriptPubKey) { - if (scriptPubKey.size() > 30) { - if (scriptPubKey[25] == OP_RVN_ASSET && scriptPubKey[27] == RVN_R && scriptPubKey[28] == RVN_V && scriptPubKey[29] == RVN_N && scriptPubKey[30] == RVN_T) { - return true; - } + int index = 0; + return IsScriptTransferAsset(scriptPubKey, index); +} + +bool IsScriptTransferAsset(const CScript& scriptPubKey, int& nStartingIndex) +{ + int nType = 0; + bool fIsOwner =false; + if (scriptPubKey.IsAssetScript(nType, fIsOwner, nStartingIndex)) { + return (txnouttype)nType == TX_TRANSFER_ASSET; } return false; @@ -1657,11 +1785,16 @@ bool CAssetsCache::GetAssetIfExists(const std::string& name, CNewAsset& asset) bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) { - if (!coin.IsAsset()) + int nType = 0; + bool fIsOwner = false; + if (!coin.out.scriptPubKey.IsAssetScript(nType, fIsOwner)) { return false; + } + + txnouttype type = txnouttype(nType); // Determine the type of asset that the scriptpubkey contains and return the name and amount - if (coin.out.scriptPubKey.IsNewAsset()) { + if (type == TX_NEW_ASSET && !fIsOwner) { CNewAsset asset; std::string address; if (!AssetFromScript(coin.out.scriptPubKey, asset, address)) @@ -1669,7 +1802,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) strName = asset.strName; nAmount = asset.nAmount; return true; - } else if (coin.out.scriptPubKey.IsTransferAsset()) { + } else if (type == TX_TRANSFER_ASSET) { CAssetTransfer asset; std::string address; if (!TransferAssetFromScript(coin.out.scriptPubKey, asset, address)) @@ -1677,7 +1810,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) strName = asset.strName; nAmount = asset.nAmount; return true; - } else if (coin.out.scriptPubKey.IsOwnerAsset()) { + } else if (type == TX_NEW_ASSET && fIsOwner) { std::string name; std::string address; if (!OwnerAssetFromScript(coin.out.scriptPubKey, name, address)) @@ -1685,7 +1818,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) strName = name; nAmount = OWNER_ASSET_AMOUNT; return true; - } else if (coin.out.scriptPubKey.IsReissueAsset()) { + } else if (type == TX_REISSUE_ASSET) { CReissueAsset reissue; std::string address; if (!ReissueAssetFromScript(coin.out.scriptPubKey, reissue, address)) @@ -1704,8 +1837,17 @@ void GetAssetData(const CScript& script, CAssetOutputEntry& data) std::string address = ""; std::string assetName = ""; + + int nType = 0; + bool fIsOwner = false; + if (!script.IsAssetScript(nType, fIsOwner)) { + return; + } + + txnouttype type = txnouttype(nType); + // Get the New Asset or Transfer Asset from the scriptPubKey - if (script.IsNewAsset()) { + if (type == TX_NEW_ASSET && !fIsOwner) { CNewAsset asset; if (AssetFromScript(script, asset, address)) { assetName = asset.strName; @@ -1714,7 +1856,7 @@ void GetAssetData(const CScript& script, CAssetOutputEntry& data) data.destination = DecodeDestination(address); data.assetName = asset.strName; } - } else if (script.IsTransferAsset()) { + } else if (type == TX_TRANSFER_ASSET) { CAssetTransfer transfer; if (TransferAssetFromScript(script, transfer, address)) { assetName = transfer.strName; @@ -1723,14 +1865,14 @@ void GetAssetData(const CScript& script, CAssetOutputEntry& data) data.destination = DecodeDestination(address); data.assetName = transfer.strName; } - } else if (script.IsOwnerAsset()) { + } else if (type == TX_NEW_ASSET && !fIsOwner) { if (OwnerAssetFromScript(script, assetName, address)) { data.type = ASSET_NEW_STRING; data.amount = OWNER_ASSET_AMOUNT; data.destination = DecodeDestination(address); data.assetName = assetName; } - } else if (script.IsReissueAsset()) { + } else if (type == TX_REISSUE_ASSET) { CReissueAsset reissue; if (ReissueAssetFromScript(script, reissue, address)) { assetName = reissue.strName; @@ -1787,6 +1929,50 @@ CAmount GetIssueUniqueAssetBurnAmount() return Params().IssueUniqueAssetBurnAmount(); } +CAmount GetBurnAmount(const AssetType type) +{ + switch (type) { + case AssetType::ROOT: + return GetIssueAssetBurnAmount(); + case AssetType::SUB: + return GetIssueSubAssetBurnAmount(); + case AssetType::MSGCHANNEL: + return 0; + case AssetType::OWNER: + return 0; + case AssetType::UNIQUE: + return GetIssueUniqueAssetBurnAmount(); + case AssetType::VOTE: + return 0; + case AssetType::REISSUE: + return GetReissueAssetBurnAmount(); + default: + return 0; + } +} + +std::string GetBurnAddress(const AssetType type) +{ + switch (type) { + case AssetType::ROOT: + return Params().IssueAssetBurnAddress(); + case AssetType::SUB: + return Params().IssueSubAssetBurnAddress(); + case AssetType::MSGCHANNEL: + return ""; + case AssetType::OWNER: + return ""; + case AssetType::UNIQUE: + return Params().IssueUniqueAssetBurnAddress(); + case AssetType::VOTE: + return ""; + case AssetType::REISSUE: + return Params().ReissueAssetBurnAddress(); + default: + return ""; + } +} + //! This will get the amount that an address for a certain asset contains from the database if they cache doesn't already have it bool GetBestAssetAddressAmount(CAssetsCache& cache, const std::string& assetName, const std::string& address) { @@ -1891,6 +2077,9 @@ std::string EncodeIPFS(std::string decoded){ bool CreateAssetTransaction(CWallet* pwallet, const CNewAsset& asset, const std::string& address, std::pair& error, std::string& rvnChangeAddress, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRequired) { + + std::string change_address = rvnChangeAddress; + // Validate the assets data std::string strError; if (!asset.IsValid(strError, *passets)) { @@ -1898,19 +2087,45 @@ bool CreateAssetTransaction(CWallet* pwallet, const CNewAsset& asset, const std: return false; } - bool haveChangeAddress = false; - if (rvnChangeAddress != "") - haveChangeAddress = true; + if (!change_address.empty()) { + CTxDestination destination = DecodeDestination(change_address); + if (!IsValidDestination(destination)) { + error = std::make_pair(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Raven address: ") + change_address); + return false; + } + } else { + // Create a new address + std::string strAccount; + + if (!pwallet->IsLocked()) { + pwallet->TopUpKeyPool(); + } + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwallet->GetKeyFromPool(newKey)) { + error = std::make_pair(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + return false; + } + CKeyID keyID = newKey.GetID(); + + pwallet->SetAddressBook(keyID, strAccount, "receive"); + + change_address = EncodeDestination(keyID); + } + - if (haveChangeAddress && !IsValidDestinationString(rvnChangeAddress)) { - error = std::make_pair(RPC_INVALID_ADDRESS_OR_KEY, "Change Address isn't a valid RVN address"); + AssetType assetType; + if (!IsAssetNameValid(asset.strName, assetType)) { + error = std::make_pair(RPC_INVALID_PARAMETER, "Asset name not valid"); return false; } - CAmount curBalance = pwallet->GetBalance(); + // Assign the correct burn amount and the correct burn address depending on the type of asset issuance that is happening + CAmount burnAmount = GetBurnAmount(assetType); + CScript scriptPubKey = GetScriptForDestination(DecodeDestination(GetBurnAddress(assetType))); - // Get the current burn amount for issuing an asset - CAmount burnAmount = GetIssueAssetBurnAmount(); + CAmount curBalance = pwallet->GetBalance(); // Check to make sure the wallet has the RVN required by the burnAmount if (curBalance < burnAmount) { @@ -1923,15 +2138,10 @@ bool CreateAssetTransaction(CWallet* pwallet, const CNewAsset& asset, const std: return false; } - // Get the script for the burn address - CScript scriptPubKey = GetScriptForDestination(DecodeDestination(Params().IssueAssetBurnAddress())); - CCoinControl coin_control; - if (haveChangeAddress) { - auto rvnChangeDest = DecodeDestination(rvnChangeAddress); - coin_control.destChange = rvnChangeDest; - } + coin_control.destChange = DecodeDestination(change_address); + LOCK2(cs_main, pwallet->cs_wallet); @@ -1940,9 +2150,32 @@ bool CreateAssetTransaction(CWallet* pwallet, const CNewAsset& asset, const std: std::vector vecSend; int nChangePosRet = -1; bool fSubtractFeeFromAmount = false; + CRecipient recipient = {scriptPubKey, burnAmount, fSubtractFeeFromAmount}; vecSend.push_back(recipient); - if (!pwallet->CreateTransactionWithAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, asset, DecodeDestination(address))) { + + // If the asset is a subasset. We need to send the ownertoken change back to ourselfs + if (assetType == AssetType::SUB) { + // Get the script for the destination address for the assets + CScript scriptTransferOwnerAsset = GetScriptForDestination(DecodeDestination(change_address)); + + std::string parent_name = GetParentName(asset.strName); + CAssetTransfer assetTransfer(parent_name + OWNER_TAG, OWNER_ASSET_AMOUNT); + assetTransfer.ConstructTransaction(scriptTransferOwnerAsset); + CRecipient rec = {scriptTransferOwnerAsset, 0, fSubtractFeeFromAmount}; + vecSend.push_back(rec); + } + + // Get the owner outpoints if this is a subasset + std::set myAssetOutPoints; + if (assetType == AssetType::SUB) { + // Verify that this wallet is the owner for the asset, and get the owner asset outpoint + if (!VerifyAssetOwner(GetParentName(asset.strName), myAssetOutPoints, error)) { + return false; + } + } + + if (!pwallet->CreateTransactionWithAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, asset, DecodeDestination(address), myAssetOutPoints, assetType)) { if (!fSubtractFeeFromAmount && burnAmount + nFeeRequired > curBalance) strTxError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); error = std::make_pair(RPC_WALLET_ERROR, strTxError); @@ -2019,29 +2252,9 @@ bool CreateReissueAssetTransaction(CWallet* pwallet, const CReissueAsset& reissu return false; } - - // Check to make sure this wallet is the owner of the asset - if(!CheckAssetOwner(asset_name)) { - error = std::make_pair(RPC_INVALID_PARAMS, - std::string("This wallet is not the owner of the asset: ") + asset_name); - return false; - } - - // Get the outpoint that belongs to the Owner Asset + // Verify that this wallet is the owner for the asset, and get the owner asset outpoint std::set myAssetOutPoints; - if (!passets->GetAssetsOutPoints(asset_name + OWNER_TAG, myAssetOutPoints)) { - error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet can't find the owner token information for: ") + asset_name); - return false; - } - - // Check to make sure we have the right amount of outpoints - if (myAssetOutPoints.size() == 0) { - error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name + OWNER_TAG); - return false; - } - - if (myAssetOutPoints.size() != 1) { - error = std::make_pair(RPC_INVALID_PARAMS, "Found multiple Owner Assets. Database is out of sync. You might have to run the wallet with -reindex"); + if (!VerifyAssetOwner(asset_name, myAssetOutPoints, error)) { return false; } @@ -2185,5 +2398,34 @@ bool SendAssetTransaction(CWallet* pwallet, CWalletTx& transaction, CReserveKey& return false; } txid = transaction.GetHash().GetHex(); + return true; +} + +bool VerifyAssetOwner(const std::string& asset_name, std::set& myOwnerOutPoints, std::pair& error) +{ + // Check to make sure this wallet is the owner of the asset + if(!CheckAssetOwner(asset_name)) { + error = std::make_pair(RPC_INVALID_PARAMS, + std::string("This wallet is not the owner of the asset: ") + asset_name); + return false; + } + + // Get the outpoint that belongs to the Owner Asset + if (!passets->GetAssetsOutPoints(asset_name + OWNER_TAG, myOwnerOutPoints)) { + error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet can't find the owner token information for: ") + asset_name); + return false; + } + + // Check to make sure we have the right amount of outpoints + if (myOwnerOutPoints.size() == 0) { + error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name + OWNER_TAG); + return false; + } + + if (myOwnerOutPoints.size() != 1) { + error = std::make_pair(RPC_INVALID_PARAMS, "Found multiple Owner Assets. Database is out of sync. You might have to run the wallet with -reindex"); + return false; + } + return true; } \ No newline at end of file diff --git a/src/assets/assets.h b/src/assets/assets.h index 54550fba99..a67ea46839 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -49,17 +49,6 @@ struct CAssetOutputEntry; // 50000 * 82 Bytes == 4.1 Mb #define MAX_CACHE_ASSETS_SIZE 50000 -enum AssetType -{ - ROOT, - OWNER, - SUB, - UNIQUE, - MSGCHANNEL, - VOTE, - INVALID -}; - class CAssets { public: std::map > mapMyUnspentAssets; // Asset Name -> COutPoint @@ -309,10 +298,13 @@ CAmount GetIssueAssetBurnAmount(); CAmount GetReissueAssetBurnAmount(); CAmount GetIssueSubAssetBurnAmount(); CAmount GetIssueUniqueAssetBurnAmount(); +CAmount GetBurnAmount(const AssetType type); +std::string GetBurnAddress(const AssetType type); bool IsAssetNameValid(const std::string& name); bool IsAssetNameValid(const std::string& name, AssetType& assetType); bool IsAssetNameAnOwner(const std::string& name); +std::string GetParentName(const std::string& name); // Gets the parent name of a subasset TEST/TESTSUB would return TEST bool IsAssetNameSizeValid(const std::string& name); @@ -323,11 +315,11 @@ bool OwnerFromTransaction(const CTransaction& tx, std::string& ownerName, std::s bool ReissueAssetFromTransaction(const CTransaction& tx, CReissueAsset& reissue, std::string& strAddress); bool TransferAssetFromScript(const CScript& scriptPubKey, CAssetTransfer& assetTransfer, std::string& strAddress); -bool AssetFromScript(const CScript& scriptPubKey, CNewAsset& assetTransfer, std::string& strAddress); +bool AssetFromScript(const CScript& scriptPubKey, CNewAsset& asset, std::string& strAddress); bool OwnerAssetFromScript(const CScript& scriptPubKey, std::string& assetName, std::string& strAddress); bool ReissueAssetFromScript(const CScript& scriptPubKey, CReissueAsset& reissue, std::string& strAddress); -bool CheckIssueBurnTx(const CTxOut& txOut); +bool CheckIssueBurnTx(const CTxOut& txOut, const AssetType& type); bool CheckReissueBurnTx(const CTxOut& txOut); bool CheckIssueDataTx(const CTxOut& txOut); @@ -335,6 +327,10 @@ bool CheckOwnerDataTx(const CTxOut& txOut); bool CheckReissueDataTx(const CTxOut& txOut); bool CheckTransferOwnerTx(const CTxOut& txOut); +bool IsScriptNewAsset(const CScript& scriptPubKey, int& nStartingIndex); +bool IsScriptOwnerAsset(const CScript& scriptPubKey, int& nStartingIndex); +bool IsScriptReissueAsset(const CScript& scriptPubKey, int& nStartingIndex); +bool IsScriptTransferAsset(const CScript& scriptPubKey, int& nStartingIndex); bool IsScriptNewAsset(const CScript& scriptPubKey); bool IsScriptOwnerAsset(const CScript& scriptPubKey); bool IsScriptReissueAsset(const CScript& scriptPubKey); @@ -360,6 +356,8 @@ bool GetMyAssetBalance(CAssetsCache& cache, const std::string& assetName, CAmoun bool GetMyAssetBalances(CAssetsCache& cache, const std::vector& assetNames, std::map& balances); bool GetMyAssetBalances(CAssetsCache& cache, std::map& balances); +bool VerifyAssetOwner(const std::string& asset_name, std::set& myOwnerOutPoints, std::pair& error); + std::string DecodeIPFS(std::string encoded); std::string EncodeIPFS(std::string decoded); diff --git a/src/assets/assettypes.h b/src/assets/assettypes.h index a3d0a39d7b..f304703cf4 100644 --- a/src/assets/assettypes.h +++ b/src/assets/assettypes.h @@ -14,6 +14,19 @@ class CAssetsCache; + +enum AssetType +{ + ROOT, + OWNER, + SUB, + UNIQUE, + MSGCHANNEL, + VOTE, + INVALID, + REISSUE +}; + class CNewAsset { public: std::string strName; // MAX 31 Bytes diff --git a/src/coins.h b/src/coins.h index 68f8642111..5e94589543 100644 --- a/src/coins.h +++ b/src/coins.h @@ -79,7 +79,7 @@ class Coin } bool IsAsset() const { - return out.scriptPubKey.IsTransferAsset() || out.scriptPubKey.IsNewAsset() || out.scriptPubKey.IsOwnerAsset() || out.scriptPubKey.IsReissueAsset(); + return out.scriptPubKey.IsAssetScript(); } size_t DynamicMemoryUsage() const { diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index e93f5db7d2..14389feead 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -186,7 +186,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, CAssetsCa return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); /** RVN START */ - if (!AreAssetsDeployed() && txout.scriptPubKey.IsAsset() && !fReindex) + if (!AreAssetsDeployed() && txout.scriptPubKey.IsAssetScript() && !fReindex) return state.DoS(100, false, REJECT_INVALID, "bad-txns-is-asset-and-asset-not-active"); // Check for transfers that don't meet the assets units only if the assetCache is not null @@ -388,8 +388,9 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c } // Check the input values and the output values - if (totalOutputs.size() != totalInputs.size()) + if (totalOutputs.size() != totalInputs.size()) { return state.DoS(100, false, REJECT_INVALID, "bad-tx-asset-inputs-size-does-not-match-outputs-size"); + } for (const auto& outValue : totalOutputs) { if (!totalInputs.count(outValue.first)) diff --git a/src/rpc/assets.cpp b/src/rpc/assets.cpp index 14cd42c2f8..ebbf734e4a 100644 --- a/src/rpc/assets.cpp +++ b/src/rpc/assets.cpp @@ -75,7 +75,7 @@ UniValue issue(const JSONRPCRequest& request) "3. \"to_address\" (string), optional, default=\"\"), address asset will be sent to, if it is empty, address will be generated for you\n" "4. \"change_address\" (string), optional, default=\"\"), address the the rvn change will be sent to, if it is empty, change address will be generated for you\n" "5. \"units\" (integer, optional, default=8, min=0, max=8), the number of decimals precision for the asset (0 for whole units (\"1\"), 8 for max precision (\"1.00000000\")\n" - "6. \"reissuable\" (boolean, optional, default=false), whether future reissuance is allowed\n" + "6. \"reissuable\" (boolean, optional, default=true), whether future reissuance is allowed\n" "7. \"has_ipfs\" (boolean, optional, default=false), whether ifps hash is going to be added to the asset\n" "8. \"ipfs_hash\" (string, optional but required if has_ipfs = 1), an ipfs hash\n" @@ -146,7 +146,7 @@ UniValue issue(const JSONRPCRequest& request) int units = 8; if (request.params.size() > 4) units = request.params[4].get_int(); - bool reissuable = false; + bool reissuable = true; if (request.params.size() > 5) reissuable = request.params[5].get_bool(); @@ -179,6 +179,132 @@ UniValue issue(const JSONRPCRequest& request) return result; } +UniValue issuesubasset(const JSONRPCRequest& request) +{ + if (request.fHelp || !AreAssetsDeployed() || request.params.size() < 3 || request.params.size() > 9) + throw std::runtime_error( + "issuesubasset \"parent_asset_name\" \"sub_asset_name\" qty \"( to_address )\" \"( change_address )\" ( units ) ( reissuable ) ( has_ipfs ) \"( ipfs_hash )\"\n" + + AssetActivationWarning() + + "\nIssue a subasset with unique name.\n" + "Unit as the number of decimals precision for the subasset (0 for whole units (\"1\"), 8 for max precision (\"1.00000000\")\n" + "Qty should be whole number.\n" + "Reissuable is true/false for whether additional units can be issued by the original issuer.\n" + + "\nArguments:\n" + "1. \"parent_asset_name\" (string, required) the parent asset name that is owned by this wallet\n" + "2. \"sub_asset_name\" (string, required) a unique name\n" + "3. \"qty\" (integer, required) the number of units to be issued\n" + "4. \"to_address\" (string), optional, default=\"\"), address subasset will be sent to, if it is empty, address will be generated for you\n" + "5. \"change_address\" (string), optional, default=\"\"), address the the rvn change will be sent to, if it is empty, change address will be generated for you\n" + "6. \"units\" (integer, optional, default=8, min=0, max=8), the number of decimals precision for the asset (0 for whole units (\"1\"), 8 for max precision (\"1.00000000\")\n" + "7. \"reissuable\" (boolean, optional, default=true), whether future reissuance is allowed\n" + "8. \"has_ipfs\" (boolean, optional, default=false), whether ifps hash is going to be added to the asset\n" + "9. \"ipfs_hash\" (string, optional but required if has_ipfs = 1), an ipfs hash\n" + + "\nResult:\n" + "\"txid\" (string) The transaction id\n" + + "\nExamples:\n" + + HelpExampleCli("issuesubasset", "\"myassetname\" \"subassetname\" 1000") + + HelpExampleCli("issuesubasset", "\"myassetname\" \"subassetname\" 1000 \"myaddress\"") + + HelpExampleCli("issuesubasset", "\"myassetname\" \"subassetname\" 1000 \"myaddress\" \"changeaddress\" 4") + + HelpExampleCli("issuesubasset", "\"myassetname\" \"subassetname\" 1000 \"myaddress\" \"changeaddress\" 2 true") + ); + + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + ObserveSafeMode(); + LOCK2(cs_main, pwallet->cs_wallet); + + EnsureWalletIsUnlocked(pwallet); + + std::string parent_asset_name = request.params[0].get_str(); + std::string asset_name = request.params[1].get_str(); + CAmount nAmount = AmountFromValue(request.params[2]); + + std::string address = ""; + if (request.params.size() > 3) + address = request.params[3].get_str(); + + if (!address.empty()) { + CTxDestination destination = DecodeDestination(address); + if (!IsValidDestination(destination)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Raven address: ") + address); + } + } else { + // Create a new address + std::string strAccount; + + if (!pwallet->IsLocked()) { + pwallet->TopUpKeyPool(); + } + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwallet->GetKeyFromPool(newKey)) { + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + } + CKeyID keyID = newKey.GetID(); + + pwallet->SetAddressBook(keyID, strAccount, "receive"); + + address = EncodeDestination(keyID); + } + + std::string changeAddress = ""; + if (request.params.size() > 4) + changeAddress = request.params[4].get_str(); + if (!changeAddress.empty()) { + CTxDestination destination = DecodeDestination(changeAddress); + if (!IsValidDestination(destination)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, + std::string("Invalid Change Address: Invalid Raven address: ") + changeAddress); + } + } + + int units = 8; + if (request.params.size() > 5) + units = request.params[5].get_int(); + bool reissuable = true; + if (request.params.size() > 6) + reissuable = request.params[6].get_bool(); + + bool has_ipfs = false; + if (request.params.size() > 7) + has_ipfs = request.params[7].get_bool(); + + std::string ipfs_hash = ""; + if (request.params.size() > 8 && has_ipfs) + ipfs_hash = request.params[8].get_str(); + + std::string strFullSubAssetName = parent_asset_name + "/" + asset_name; + CNewAsset asset(strFullSubAssetName, nAmount, units, reissuable ? 1 : 0, has_ipfs ? 1 : 0, DecodeIPFS(ipfs_hash)); + + CReserveKey reservekey(pwallet); + CWalletTx transaction; + CAmount nRequiredFee; + std::pair error; + + // Create the Transaction + if (!CreateAssetTransaction(pwallet, asset, address, error, changeAddress, transaction, reservekey, nRequiredFee)) + throw JSONRPCError(error.first, error.second); + + // Send the Transaction to the network + std::string txid; + if (!SendAssetTransaction(pwallet, transaction, reservekey, error, txid)) + throw JSONRPCError(error.first, error.second); + + UniValue result(UniValue::VARR); + result.push_back(txid); + return result; +} + + + + UniValue listassetbalancesbyaddress(const JSONRPCRequest& request) { if (request.fHelp || !AreAssetsDeployed() || request.params.size() < 1) @@ -513,8 +639,8 @@ UniValue transfer(const JSONRPCRequest& request) "\nArguments:\n" "1. \"asset_name\" (string, required) name of asset\n" - "3. \"qty\" (number, required) number of assets you want to send to the address\n" - "2. \"to_address\" (string, required) address to send the asset to\n" + "2. \"qty\" (number, required) number of assets you want to send to the address\n" + "3. \"to_address\" (string, required) address to send the asset to\n" "\nResult:\n" "txid" @@ -745,6 +871,7 @@ static const CRPCCommand commands[] = { // category name actor (function) argNames // ----------- ------------------------ ----------------------- ---------- { "assets", "issue", &issue, {"asset_name","qty","to_address","change_address","units","reissuable","has_ipfs","ipfs_hash"} }, + { "assets", "issuesubasset", &issuesubasset, {"parent_asset_name","asset_name","qty","to_address","change_address","units","reissuable","has_ipfs","ipfs_hash"} }, { "assets", "listassetbalancesbyaddress", &listassetbalancesbyaddress, {"address"} }, { "assets", "getassetdata", &getassetdata, {"asset_name"}}, { "assets", "listmyassets", &listmyassets, {"asset", "verbose", "count", "start"}}, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 902da8acf2..6af03f4179 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -33,6 +33,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "issue", 4, "units" }, { "issue", 5, "reissuable" }, { "issue", 6, "has_ipfs" }, + { "issuesubasset", 2, "qty" }, + { "issuesubasset", 5, "units" }, + { "issuesubasset", 6, "reissuable" }, + { "issuesubasset", 7, "has_ipfs" }, { "transfer", 1, "qty"}, { "reissue", 1, "qty"}, { "reissue", 4, "reissuable"}, diff --git a/src/script/script.cpp b/src/script/script.cpp index 9be35ece05..7d8465afe7 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -9,6 +9,7 @@ #include "tinyformat.h" #include "utilstrencodings.h" +#include "standard.h" const char* GetOpName(opcodetype opcode) { @@ -226,61 +227,112 @@ bool CScript::IsPayToScriptHash() const } /** RVN START */ +bool CScript::IsAssetScript() const +{ + int nType = 0; + bool isOwner = false; + int start = 0; + return IsAssetScript(nType, isOwner, start); +} + +bool CScript::IsAssetScript(int& nType, bool& isOwner) const +{ + int start = 0; + return IsAssetScript(nType, isOwner, start); +} + +bool CScript::IsAssetScript(int& nType, bool& fIsOwner, int& nStartingIndex) const +{ + if (this->size() > 30) { + if ((*this)[25] == OP_RVN_ASSET) { // OP_RVN_ASSET is always in the 25 index of the script if it exists + int index = -1; + if ((*this)[27] == RVN_R) { // Check to see if RVN starts at 27 ( this->size() < 105) + if ((*this)[28] == RVN_V) + if ((*this)[29] == RVN_N) + index = 30; + } else { + if ((*this)[28] == RVN_R) // Check to see if RVN starts at 28 ( this->size() >= 105) + if ((*this)[29] == RVN_V) + if ((*this)[30] == RVN_N) + index = 31; + } + + if (index > 0) { + nStartingIndex = index + 1; // Set the index where the asset data begins. Use to serialize the asset data into asset objects + if ((*this)[index] == RVN_T) { // Transfer first anticipating more transfers than other assets operations + nType = TX_TRANSFER_ASSET; + return true; + } else if ((*this)[index] == RVN_Q && this->size() > 39) { + nType = TX_NEW_ASSET; + fIsOwner = false; + return true; + } else if ((*this)[index] == RVN_O) { + nType = TX_NEW_ASSET; + fIsOwner = true; + return true; + } else if ((*this)[index] == RVN_R) { + nType = TX_REISSUE_ASSET; + return true; + } else { + nType = TX_RESERVED_ASSET; + return false; + } + } + } + } + return false; +} + + bool CScript::IsNewAsset() const { - // Extra-fast test for new-asset CScripts: - return (this->size() > 39 && - (*this)[25] == OP_RVN_ASSET && - (*this)[27] == RVN_R && - (*this)[28] == RVN_V && - (*this)[29] == RVN_N && - (*this)[30] == RVN_Q); + + int nType = 0; + bool fIsOwner = false; + if (IsAssetScript(nType, fIsOwner)) + return !fIsOwner && nType == TX_NEW_ASSET; + + return false; } bool CScript::IsOwnerAsset() const { - // Extra-fast test for new-asset CScripts: - return (this->size() > 30 && - (*this)[25] == OP_RVN_ASSET && - (*this)[27] == RVN_R && - (*this)[28] == RVN_V && - (*this)[29] == RVN_N && - (*this)[30] == RVN_O); + int nType = 0; + bool fIsOwner = false; + if (IsAssetScript(nType, fIsOwner)) + return fIsOwner && nType == TX_NEW_ASSET; + + return false; } bool CScript::IsReissueAsset() const { - // Extra-fast test for new-asset CScripts: - return (this->size() > 30 && - (*this)[25] == OP_RVN_ASSET && - (*this)[27] == RVN_R && - (*this)[28] == RVN_V && - (*this)[29] == RVN_N && - (*this)[30] == RVN_R); + int nType = 0; + bool fIsOwner = false; + if (IsAssetScript(nType, fIsOwner)) + return nType == TX_REISSUE_ASSET; + + return false; } bool CScript::IsTransferAsset() const { - // Extra-fast test for new-asset CScripts: - return (this->size() > 30 && - (*this)[25] == OP_RVN_ASSET && - (*this)[27] == RVN_R && - (*this)[28] == RVN_V && - (*this)[29] == RVN_N && - (*this)[30] == RVN_T); + int nType = 0; + bool fIsOwner = false; + if (IsAssetScript(nType, fIsOwner)) + return nType == TX_TRANSFER_ASSET; + + return false; } bool CScript::IsReservedAsset() const { - return (this->size() > 30 && - (*this)[25] == OP_RVN_ASSET && - (*this)[27] == RVN_R && - (*this)[28] == RVN_V && - (*this)[29] == RVN_N); -} + int nType = 0; + bool fIsOwner = false; + if (!IsAssetScript(nType, fIsOwner)) + return nType == TX_RESERVED_ASSET; -bool CScript::IsAsset() const { - return IsTransferAsset() || IsReissueAsset() || IsOwnerAsset() || IsNewAsset() || IsReservedAsset(); + return false; } /** RVN END */ diff --git a/src/script/script.h b/src/script/script.h index aa04979a1f..2233e59adf 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -652,6 +652,10 @@ class CScript : public CScriptBase bool IsWitnessProgram(int& version, std::vector& program) const; /** RVN START */ + enum class txnouttype; + bool IsAssetScript(int& nType, bool& fIsOwner, int& nStartingIndex) const; + bool IsAssetScript(int& nType, bool& fIsOwner) const; + bool IsAssetScript() const; bool IsNewAsset() const; bool IsOwnerAsset() const; bool IsReissueAsset() const; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index b0d27c53fb..0bd023e307 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -72,36 +72,10 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); - vSolutionsRet.push_back(hashBytes); - return true; - } - - if (scriptPubKey.IsTransferAsset()) { - typeRet = TX_TRANSFER_ASSET; - std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); - vSolutionsRet.push_back(hashBytes); - return true; - } - - if (scriptPubKey.IsOwnerAsset()) { - typeRet = TX_NEW_ASSET; - std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); - vSolutionsRet.push_back(hashBytes); - return true; - } - - if (scriptPubKey.IsReissueAsset()) { - typeRet = TX_REISSUE_ASSET; - std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); - vSolutionsRet.push_back(hashBytes); - return true; - } - - if (scriptPubKey.IsReservedAsset()) { - typeRet = TX_RESERVED_ASSET; + int nType = 0; + bool fIsOwner = false; + if (scriptPubKey.IsAssetScript(nType, fIsOwner)) { + typeRet = (txnouttype)nType; std::vector hashBytes(scriptPubKey.begin()+3, scriptPubKey.begin()+23); vSolutionsRet.push_back(hashBytes); return true; @@ -223,8 +197,9 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { std::vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) + if (!Solver(scriptPubKey, whichType, vSolutions)) { return false; + } if (whichType == TX_PUBKEY) { @@ -245,7 +220,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) addressRet = CScriptID(uint160(vSolutions[0])); return true; /** RVN START */ - } else if (whichType == TX_NEW_ASSET || whichType == TX_REISSUE_ASSET || whichType == TX_TRANSFER_ASSET) { + } else if (whichType == TX_NEW_ASSET || whichType == TX_REISSUE_ASSET || whichType == TX_TRANSFER_ASSET || whichType == TX_RESERVED_ASSET) { addressRet = CKeyID(uint160(vSolutions[0])); return true; } @@ -261,7 +236,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: std::vector vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; - if (typeRet == TX_NULL_DATA){ + if (typeRet == TX_NULL_DATA) { // This is data, not addresses return false; } diff --git a/src/test/assets/asset_tests.cpp b/src/test/assets/asset_tests.cpp index feb6be4d19..f9b62b9dec 100644 --- a/src/test/assets/asset_tests.cpp +++ b/src/test/assets/asset_tests.cpp @@ -105,6 +105,8 @@ BOOST_FIXTURE_TEST_SUITE(asset_tests, BasicTestingSetup) BOOST_CHECK(IsAssetNameValid("ABC~1", type)); BOOST_CHECK(type == AssetType::MSGCHANNEL); BOOST_CHECK(IsAssetNameValid("ABC~STILL_MAX_OF_30.CHARS_1234", type)); + BOOST_CHECK(IsAssetNameValid("TEST/TEST~CHANNEL", type)); + BOOST_CHECK(type == AssetType::MSGCHANNEL); BOOST_CHECK(!IsAssetNameValid("MIN~", type)); BOOST_CHECK(!IsAssetNameValid("ABC~NO~TILDE", type)); @@ -129,7 +131,43 @@ BOOST_FIXTURE_TEST_SUITE(asset_tests, BasicTestingSetup) BOOST_CHECK(IsAssetNameValid("ABC!", type)); BOOST_CHECK(type == AssetType::OWNER); + // vote + BOOST_CHECK(IsAssetNameValid("ABC^VOTE")); + BOOST_CHECK(!IsAssetNameValid("ABC^")); + BOOST_CHECK(IsAssetNameValid("ABC^VOTING")); + BOOST_CHECK(IsAssetNameValid("ABC^VOTING_IS_30_CHARACTERS_LN")); + BOOST_CHECK(!IsAssetNameValid("ABC^VOTING_IS_31_CHARACTERS_LN!")); + BOOST_CHECK(IsAssetNameValid("ABC/SUB/SUB/SUB/SUB^VOTE")); + BOOST_CHECK(IsAssetNameValid("ABC/SUB/SUB/SUB/SUB/SUB/30^VOT")); + BOOST_CHECK(!IsAssetNameValid("ABC/SUB/SUB/SUB/SUB/SUB/31^VOTE")); + BOOST_CHECK(IsAssetNameValid("ABC/SUB/SUB^VOTE", type)); + BOOST_CHECK(type == AssetType::VOTE); + + // Check type for different type of sub assets + BOOST_CHECK(IsAssetNameValid("TEST/UYTH#UNIQUE", type)); + BOOST_CHECK(type == AssetType::UNIQUE); + + BOOST_CHECK(IsAssetNameValid("TEST/UYTH/SUB#UNIQUE", type)); + BOOST_CHECK(type == AssetType::UNIQUE); + + BOOST_CHECK(IsAssetNameValid("TEST/UYTH/SUB~CHANNEL", type)); + BOOST_CHECK(type == AssetType::MSGCHANNEL); + BOOST_CHECK(!IsAssetNameValid("TEST/UYTH/SUB#UNIQUE^VOTE", type)); + BOOST_CHECK(!IsAssetNameValid("TEST/UYTH/SUB#UNIQUE#UNIQUE", type)); + BOOST_CHECK(!IsAssetNameValid("TEST/UYTH/SUB~CHANNEL^VOTE", type)); + BOOST_CHECK(!IsAssetNameValid("TEST/UYTH/SUB~CHANNEL^UNIQUE", type)); + BOOST_CHECK(!IsAssetNameValid("TEST/UYTH/SUB~CHANNEL!", type)); + BOOST_CHECK(!IsAssetNameValid("TEST/UYTH/SUB^VOTE!", type)); + + // Check ParentName function + BOOST_CHECK(GetParentName("TEST!") == "TEST!"); + BOOST_CHECK(GetParentName("TEST") == "TEST"); + BOOST_CHECK(GetParentName("TEST/SUB") == "TEST"); + BOOST_CHECK(GetParentName("TEST/SUB#UNIQUE") == "TEST/SUB"); + BOOST_CHECK(GetParentName("TEST/TEST/SUB/SUB") == "TEST/TEST/SUB"); + BOOST_CHECK(GetParentName("TEST/SUB^VOTE") == "TEST/SUB"); + BOOST_CHECK(GetParentName("TEST/SUB/SUB~CHANNEL") == "TEST/SUB/SUB"); } BOOST_AUTO_TEST_CASE(transfer_asset_coin_check) { @@ -169,4 +207,15 @@ BOOST_FIXTURE_TEST_SUITE(asset_tests, BasicTestingSetup) BOOST_CHECK_MESSAGE(coin.IsAsset(), "New Asset Coin isn't as asset"); } + BOOST_AUTO_TEST_CASE(dwg_version_check) { + + + int32_t version = 0x30000000; + int32_t mask = 0xF0000000; + int32_t new_version = version & mask; + int32_t shifted = new_version >> 28; + + BOOST_CHECK_MESSAGE(shifted == 3, "New version didn't equal 3"); + } + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/validation.cpp b/src/validation.cpp index ae31d7ce3c..35d997f635 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -599,7 +599,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool /** RVN START */ if (!AreAssetsDeployed()) { for (auto out : tx.vout) { - if (out.scriptPubKey.IsAsset()) + if (out.scriptPubKey.IsAssetScript()) return state.DoS(100, false, REJECT_INVALID, "bad-txns-contained-asset-when-not-active"); } } @@ -2099,7 +2099,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd /** RVN START */ if (!AreAssetsDeployed()) { for (auto out : tx.vout) - if (out.scriptPubKey.IsAsset()) + if (out.scriptPubKey.IsAssetScript()) return state.DoS(100, error("%s : Received Block with tx that contained an asset when assets wasn't active", __func__), REJECT_INVALID, "bad-txns-assets-not-active"); } @@ -2190,6 +2190,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (tx.IsNewAsset()) { if (!AreAssetsDeployed()) return state.DoS(100, false, REJECT_INVALID, "bad-txns-new-asset-when-assets-is-not-active"); + CNewAsset asset; std::string strAddress; if (!AssetFromTransaction(tx, asset, strAddress)) @@ -2201,9 +2202,11 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!asset.IsValid(strError, *assetsCache)) return state.DoS(100, error("%s: %s", __func__, strError), REJECT_INVALID, "bad-txns-issue-asset"); + } else if (tx.IsReissueAsset()) { if (!AreAssetsDeployed()) return state.DoS(100, false, REJECT_INVALID, "bad-txns-reissue-asset-when-assets-is-not-active"); + CReissueAsset reissue; std::string strAddress; if (!ReissueAssetFromTransaction(tx, reissue, strAddress)) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fc7fb5c4f7..8a6f2177d8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1529,7 +1529,7 @@ void CWalletTx::GetAmounts(std::list& listReceived, address = CNoDestination(); } - if (!txout.scriptPubKey.IsAsset()) { + if (!txout.scriptPubKey.IsAssetScript()) { COutputEntry output = {address, txout.nValue, (int) i}; // If we are debited by the transaction, add the output as a "sent" entry @@ -1543,7 +1543,7 @@ void CWalletTx::GetAmounts(std::list& listReceived, /** RVN START */ if (AreAssetsDeployed()) { - if (txout.scriptPubKey.IsAsset()) { + if (txout.scriptPubKey.IsAssetScript()) { CAssetOutputEntry assetoutput; assetoutput.vout = i; GetAssetData(txout.scriptPubKey, assetoutput); @@ -2696,32 +2696,39 @@ bool CWallet::SelectAssets(const std::map >& m if (mapAssetTargetValue.at(asset.first) <= 0) continue; - if(!mapValueRet.count(asset.first)) + if (!mapValueRet.count(asset.first)) mapValueRet.insert(std::make_pair(asset.first, 0)); for (auto out : asset.second) { if (!out.fSpendable) continue; - if (out.tx->tx->vout[out.i].scriptPubKey.IsNewAsset()) { + int nType = 0; + bool fIsOwner = false; + if (!out.tx->tx->vout[out.i].scriptPubKey.IsAssetScript(nType, fIsOwner)) + continue; + + txnouttype type = (txnouttype) nType; + + if (type == TX_NEW_ASSET && !fIsOwner) { CNewAsset assetTemp; std::string address; if (!AssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, assetTemp, address)) continue; mapValueRet.at(assetTemp.strName) += assetTemp.nAmount; - } else if (out.tx->tx->vout[out.i].scriptPubKey.IsTransferAsset()) { + } else if (type == TX_TRANSFER_ASSET) { CAssetTransfer transferTemp; std::string address; if (!TransferAssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, transferTemp, address)) continue; mapValueRet.at(transferTemp.strName) += transferTemp.nAmount; - } else if (out.tx->tx->vout[out.i].scriptPubKey.IsOwnerAsset()) { + } else if (type == TX_NEW_ASSET && fIsOwner) { std::string ownerName; std::string address; if (!OwnerAssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, ownerName, address)) continue; mapValueRet.at(ownerName) = OWNER_ASSET_AMOUNT; - } else if (out.tx->tx->vout[out.i].scriptPubKey.IsReissueAsset()) { + } else if (type == TX_REISSUE_ASSET) { CReissueAsset reissueTemp; std::string address; if (!ReissueAssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, reissueTemp, address)) @@ -2731,18 +2738,25 @@ bool CWallet::SelectAssets(const std::map >& m continue; } + setCoinsRet.insert(CInputCoin(out.tx, out.i)); if (mapValueRet.at(asset.first) >= mapAssetTargetValue.at(asset.first)) break; } + for (auto pair : mapValueRet) { + std::cout << "Selected Assets : " << pair.first << " : " << pair.second << std::endl; + } + if (mapValueRet.at(asset.first) < mapAssetTargetValue.at(asset.first)) { - return error("%s : Tried to transfer an asset but this wallet didn't have enough, Asset Name: %s, Transfer Amount: %d, Wallet Total: %d", __func__, asset.first, mapValueRet.at(asset.first), mapAssetTargetValue.at(asset.first)); + return error( + "%s : Tried to transfer an asset but this wallet didn't have enough, Asset Name: %s, Transfer Amount: %d, Wallet Total: %d", + __func__, asset.first, mapValueRet.at(asset.first), mapAssetTargetValue.at(asset.first)); } } - return true; + return true; } /** RVN END */ @@ -2825,12 +2839,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC } bool CWallet::CreateTransactionWithAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination destination, bool sign) + std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination destination, const std::set& setAssetOutPoints, const AssetType& type, bool sign) { - - std::set setAssetOutPoints; CReissueAsset reissueAsset; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, true, asset, destination, false, setAssetOutPoints, false, reissueAsset, sign); + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, true, asset, destination, false, setAssetOutPoints, false, reissueAsset, type, sign); } bool CWallet::CreateTransactionWithTransferAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, @@ -2843,14 +2855,16 @@ bool CWallet::CreateTransactionWithTransferAsset(const std::vector& CNewAsset asset; CReissueAsset reissueAsset; CTxDestination destination; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, true, setAssetOutPoints, false, reissueAsset, sign); + AssetType assetType = AssetType::INVALID; + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, true, setAssetOutPoints, false, reissueAsset, assetType, sign); } bool CWallet::CreateTransactionWithReissueAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, const CReissueAsset& reissueAsset, const CTxDestination destination, const std::set& setAssetOutPoints, bool sign) { CNewAsset asset; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, setAssetOutPoints, true, reissueAsset, sign); + AssetType assetType = AssetType::REISSUE; + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, setAssetOutPoints, true, reissueAsset, assetType, sign); } bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, @@ -2861,12 +2875,13 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT CReissueAsset reissueAsset; CTxDestination destination; std::set setAssetOutPoints; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, setAssetOutPoints, false, reissueAsset, sign); + AssetType assetType = AssetType::INVALID; + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, setAssetOutPoints, false, reissueAsset, assetType, sign); } bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, - int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination destination, bool fTransferAsset, const std::set& setAssetOutPoints, bool fReissueAsset, const CReissueAsset& reissueAsset, bool sign) + int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination destination, bool fTransferAsset, const std::set& setAssetOutPoints, bool fReissueAsset, const CReissueAsset& reissueAsset, const AssetType& assetType, bool sign) { if (!AreAssetsDeployed() && (fTransferAsset || fNewAsset || fReissueAsset)) @@ -2896,7 +2911,7 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall for (const auto& recipient : vecSend) { /** RVN START */ - if (fTransferAsset || fReissueAsset) { + if (fTransferAsset || fReissueAsset || assetType == AssetType::SUB) { CAssetTransfer assetTransfer; std::string address; if (TransferAssetFromScript(recipient.scriptPubKey, assetTransfer, address)) { @@ -2976,7 +2991,7 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall /** RVN START */ std::vector vAvailableCoins; std::map > mapAssetCoins; - if (fTransferAsset || fReissueAsset) + if (fTransferAsset || fReissueAsset || assetType == AssetType::SUB) AvailableCoinsWithAssets(vAvailableCoins, mapAssetCoins, setAssetOutPoints, true, true, &coin_control); else AvailableCoins(vAvailableCoins, true, &coin_control); @@ -3059,8 +3074,9 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall else strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); } - else + else { strFailReason = _("Transaction amount too small"); + } return false; } txNew.vout.push_back(txout); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 98d3292c25..25b3892a10 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -971,7 +971,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /** RVN START */ bool CreateTransactionWithAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination dest, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination dest, const std::set& setAssetOutPoints, const AssetType& assetType, bool sign = true); bool CreateTransactionWithTransferAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, const std::set& setAssetOutPoints, bool sign = true); @@ -988,7 +988,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface * @note passing nChangePosInOut as -1 will result in setting a random position */ bool CreateTransactionAll(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination dest, bool fTransferAsset, const std::set& setAssetOutPoints, bool fReissueAsset, const CReissueAsset& reissueAsset, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination dest, bool fTransferAsset, const std::set& setAssetOutPoints, bool fReissueAsset, const CReissueAsset& reissueAsset, const AssetType& assetType, bool sign = true); /** RVN END */