diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index f07de4961a..d5b65ca1be 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. +#include #include #include /// CAccountsHistoryWriter @@ -13,7 +14,7 @@ #include /// ValueFromAmount #include -extern UniValue AmountsToJSON(TAmounts const & diffs); +extern UniValue AmountsToJSON(TAmounts const & diffs, AmountFormat format = AmountFormat::Symbol); static inline std::string trim_all_ws(std::string s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { diff --git a/src/masternodes/mn_rpc.h b/src/masternodes/mn_rpc.h index 7a2cb3f8fa..46a15477fe 100644 --- a/src/masternodes/mn_rpc.h +++ b/src/masternodes/mn_rpc.h @@ -35,6 +35,16 @@ typedef enum { SelectionPie, } AccountSelectionMode; +enum class AmountFormat : uint8_t { + Unknown, + // amount@0 + Id, + // amount@DFI + Symbol, + // amount@0#DFI + Combined, +}; + class CWalletCoinsUnlocker { std::shared_ptr pwallet; std::vector coins; diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index 4520a4f942..b1981a3886 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -2,22 +2,38 @@ #include #include -std::string tokenAmountString(CTokenAmount const& amount) { +std::string tokenAmountString(CTokenAmount const& amount, AmountFormat format = AmountFormat::Symbol) { const auto token = pcustomcsview->GetToken(amount.nTokenId); - const auto valueString = ValueFromAmount(amount.nValue).getValStr(); - return valueString + "@" + token->CreateSymbolKey(amount.nTokenId); + const auto amountString = ValueFromAmount(amount.nValue).getValStr(); + + std::string tokenStr = {}; + switch (format) { + case AmountFormat::Id: + tokenStr = amount.nTokenId.ToString(); + break; + case AmountFormat::Symbol: + tokenStr = token->CreateSymbolKey(amount.nTokenId); + break; + case AmountFormat::Combined: + tokenStr = amount.nTokenId.ToString() + "#" + token->CreateSymbolKey(amount.nTokenId); + break; + case AmountFormat::Unknown: + tokenStr = "unknown"; + break; + } + return amountString + "@" + tokenStr; } -UniValue AmountsToJSON(TAmounts const & diffs) { +UniValue AmountsToJSON(TAmounts const & diffs, AmountFormat format = AmountFormat::Symbol) { UniValue obj(UniValue::VARR); for (auto const & diff : diffs) { - obj.push_back(tokenAmountString({diff.first, diff.second})); + obj.push_back(tokenAmountString({diff.first, diff.second}, format)); } return obj; } -UniValue accountToJSON(CScript const& owner, CTokenAmount const& amount, bool verbose, bool indexed_amounts) { +UniValue accountToJSON(CScript const& owner, CTokenAmount const& amount, bool verbose, bool indexed_amounts,AmountFormat format = AmountFormat::Symbol) { // encode CScript into JSON UniValue ownerObj(UniValue::VOBJ); ScriptPubKeyToUniv(owner, ownerObj, true); @@ -40,13 +56,13 @@ UniValue accountToJSON(CScript const& owner, CTokenAmount const& amount, bool ve obj.pushKV("amount", amountObj); } else { - obj.pushKV("amount", tokenAmountString(amount)); + obj.pushKV("amount", tokenAmountString(amount, format)); } return obj; } -UniValue accounthistoryToJSON(AccountHistoryKey const & key, AccountHistoryValue const & value) { +UniValue accounthistoryToJSON(AccountHistoryKey const & key, AccountHistoryValue const & value, AmountFormat format = AmountFormat::Symbol) { UniValue obj(UniValue::VOBJ); obj.pushKV("owner", ScriptToString(key.owner)); @@ -58,11 +74,11 @@ UniValue accounthistoryToJSON(AccountHistoryKey const & key, AccountHistoryValue obj.pushKV("type", ToString(CustomTxCodeToType(value.category))); obj.pushKV("txn", (uint64_t) key.txn); obj.pushKV("txid", value.txid.ToString()); - obj.pushKV("amounts", AmountsToJSON(value.diff)); + obj.pushKV("amounts", AmountsToJSON(value.diff, format)); return obj; } -UniValue rewardhistoryToJSON(CScript const & owner, uint32_t height, DCT_ID const & poolId, RewardType type, CTokenAmount amount) { +UniValue rewardhistoryToJSON(CScript const & owner, uint32_t height, DCT_ID const & poolId, RewardType type, CTokenAmount amount, AmountFormat format = AmountFormat::Id) { UniValue obj(UniValue::VOBJ); obj.pushKV("owner", ScriptToString(owner)); obj.pushKV("blockHeight", (uint64_t) height); @@ -76,11 +92,11 @@ UniValue rewardhistoryToJSON(CScript const & owner, uint32_t height, DCT_ID cons } obj.pushKV("poolID", poolId.ToString()); TAmounts amounts({{amount.nTokenId,amount.nValue}}); - obj.pushKV("amounts", AmountsToJSON(amounts)); + obj.pushKV("amounts", AmountsToJSON(amounts, format)); return obj; } -UniValue outputEntryToJSON(COutputEntry const & entry, CBlockIndex const * index, CWalletTx const * pwtx) { +UniValue outputEntryToJSON(COutputEntry const & entry, CBlockIndex const * index, CWalletTx const * pwtx, AmountFormat format = AmountFormat::Symbol) { UniValue obj(UniValue::VOBJ); obj.pushKV("owner", EncodeDestination(entry.destination)); @@ -97,7 +113,7 @@ UniValue outputEntryToJSON(COutputEntry const & entry, CBlockIndex const * index obj.pushKV("txn", (uint64_t) pwtx->nIndex); obj.pushKV("txid", pwtx->GetHash().ToString()); TAmounts amounts({{DCT_ID{0},entry.amount}}); - obj.pushKV("amounts", AmountsToJSON(amounts)); + obj.pushKV("amounts", AmountsToJSON(amounts, format)); return obj; } @@ -968,6 +984,9 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { "Maximum number of records to return, 100 by default"}, {"txn", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Order in block, unlimited by default"}, + {"format", RPCArg::Type::STR, RPCArg::Optional::OMITTED, + "Return amounts with the following: 'id' -> @id; (default)'symbol' -> @symbol"}, + }, }, }, @@ -998,6 +1017,7 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { uint32_t limit = 100; auto txType = CustomTxType::None; uint32_t txn = std::numeric_limits::max(); + AmountFormat format = AmountFormat::Symbol; if (request.params.size() > 1) { UniValue optionsObj = request.params[1].get_obj(); @@ -1010,6 +1030,7 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { {"txtype", UniValueType(UniValue::VSTR)}, {"limit", UniValueType(UniValue::VNUM)}, {"txn", UniValueType(UniValue::VNUM)}, + {"format", UniValueType(UniValue::VSTR)} }, true, true); if (!optionsObj["maxBlockHeight"].isNull()) { @@ -1045,6 +1066,19 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { if (!optionsObj["txn"].isNull()) { txn = (uint32_t) optionsObj["txn"].get_int64(); } + + if (!optionsObj["format"].isNull()) { + const auto formatStr = optionsObj["format"].getValStr(); + if (formatStr == "symbol"){ + format = AmountFormat::Symbol; + } + else if (formatStr == "id") { + format = AmountFormat::Id; + } + else { + throw JSONRPCError(RPC_INVALID_REQUEST, "format must be one of the following: \"id\", \"symbol\""); + } + } } std::function isMatchOwner = [](CScript const &) { @@ -1145,7 +1179,7 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { if (accountRecord && (tokenFilter.empty() || hasToken(value.diff))) { auto& array = ret.emplace(workingHeight, UniValue::VARR).first->second; - array.push_back(accounthistoryToJSON(key, value)); + array.push_back(accounthistoryToJSON(key, value, format)); if (shouldSearchInWallet) { txs.insert(value.txid); } @@ -1157,7 +1191,7 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { [&](int32_t height, DCT_ID poolId, RewardType type, CTokenAmount amount) { if (tokenFilter.empty() || hasToken({{amount.nTokenId, amount.nValue}})) { auto& array = ret.emplace(height, UniValue::VARR).first->second; - array.push_back(rewardhistoryToJSON(key.owner, height, poolId, type, amount)); + array.push_back(rewardhistoryToJSON(key.owner, height, poolId, type, amount, format)); count ? --count : 0; } } @@ -1199,7 +1233,7 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { return true; } auto& array = ret.emplace(index->nHeight, UniValue::VARR).first->second; - array.push_back(outputEntryToJSON(entry, index, pwtx)); + array.push_back(outputEntryToJSON(entry, index, pwtx, format)); return --count != 0; } ); @@ -1811,7 +1845,7 @@ UniValue getburninfo(const JSONRPCRequest& request) { } for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) { - if (kv.first == CommunityAccountType::Unallocated || + if (kv.first == CommunityAccountType::Unallocated || kv.first == CommunityAccountType::IncentiveFunding || (height >= fortCanningHeight && kv.first == CommunityAccountType::Loan)) { burnt += view.GetCommunityBalance(kv.first); @@ -1866,7 +1900,7 @@ UniValue getburninfo(const JSONRPCRequest& request) { }; burnView->ForEachAccountHistory(calculateBurnAmounts); - + UniValue result(UniValue::VOBJ); result.pushKV("address", ScriptToString(burnAddress)); result.pushKV("amount", ValueFromAmount(burntDFI)); diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index ba1aab2d5a..84f506dbb7 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -3,8 +3,8 @@ #include #include -extern UniValue AmountsToJSON(TAmounts const & diffs); -extern std::string tokenAmountString(CTokenAmount const& amount); +extern UniValue AmountsToJSON(TAmounts const & diffs, AmountFormat format = AmountFormat::Symbol); +extern std::string tokenAmountString(CTokenAmount const& amount, AmountFormat format = AmountFormat::Symbol); namespace { diff --git a/test/functional/feature_token_split_usd_value.py b/test/functional/feature_token_split_usd_value.py index 2544235dbd..2c546ad5b3 100755 --- a/test/functional/feature_token_split_usd_value.py +++ b/test/functional/feature_token_split_usd_value.py @@ -113,7 +113,6 @@ def setup_accounts(self): self.generate_and_fill_accounts() def add_total_account_to_liquidity_pool(self): - print(f'Adding liquidity with {len(self.accounts)} accounts...') size = 1000000 for account in self.accounts: totalAmount = Decimal(self.get_amount_from_account(account, self.symbolDUSD)) @@ -128,7 +127,6 @@ def add_total_account_to_liquidity_pool(self): self.nodes[0].addpoolliquidity({account: [str(finalAmount)+"@T1", str(finalAmount)+"@DUSD"]}, account) self.nodes[0].generate(1) totalAmount -= finalAmount - print(f'account {account} finished') def setup_pools(self): self.nodes[0].createpoolpair({ @@ -212,14 +210,12 @@ def accounts_usd_values(self): def compare_value_list(self, pre, post): for index, amount in enumerate(pre): - print(f'Comparing values in valut {amount["account"]}') if index != 0: almost_equal(amount["DUSD"], post[index]["DUSD"]) almost_equal(amount["T1"], post[index]["T1"]) def compare_vaults_list(self, pre, post): for index, vault in enumerate(pre): - print(f'Comparing valuts {vault["vaultId"]}') if index != 0: almost_equal(vault["collateralValue"], post[index]["collateralValue"]) almost_equal(vault["loanValue"], post[index]["loanValue"]) diff --git a/test/functional/rpc_getaccounthistory.py b/test/functional/rpc_getaccounthistory.py index 03bfd9bd11..06c8eadb6d 100755 --- a/test/functional/rpc_getaccounthistory.py +++ b/test/functional/rpc_getaccounthistory.py @@ -6,10 +6,9 @@ """Test getaccounthistory RPC.""" from test_framework.test_framework import DefiTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal -from test_framework.util import ( - assert_equal -) class TokensRPCGetAccountHistory(DefiTestFramework): def set_test_params(self): @@ -53,7 +52,28 @@ def run_test(self): self.nodes[0].generate(1) # Get node 0 results - results = self.nodes[0].listaccounthistory(collateral_a) + # test amount@id format + results = self.nodes[0].listaccounthistory(collateral_a, {"format": "id"} ) + # test token ids match token symbol + for result in results: + for amount in result["amounts"]: + symbol = amount.split('@')[1] + assert(symbol.isnumeric()) + + # test amount@symbol format + results = self.nodes[0].listaccounthistory(collateral_a, {"format": "symbol"} ) + # test token ids match token symbol + for result in results: + for amount in result["amounts"]: + symbol = amount.split('@')[1] + assert(symbol.isnumeric() == False) + + # test amount@symbol format + try: + results = self.nodes[0].listaccounthistory(collateral_a, {"format": "combined"} ) + except JSONRPCException as e: + errorString = e.error['message'] + assert("format must be one of the following: \"id\", \"symbol\"" in errorString) # An account history from listaccounthistory and gettaccounthistory must be matched expected = results[0] diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 0bb6a07c3a..5b8bf08fee 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -28,6 +28,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "consensus/tx_verify -> masternodes/masternodes -> validation -> consensus/tx_verify" "consensus/tx_verify -> masternodes/mn_checks -> txmempool -> consensus/tx_verify" "index/txindex -> validation -> index/txindex" + "init -> miner -> masternodes/govvariables/attributes -> masternodes/mn_rpc -> wallet/rpcwallet -> init" "masternodes/accountshistory -> masternodes/masternodes -> masternodes/accountshistory" "masternodes/accountshistory -> masternodes/masternodes -> masternodes/mn_checks -> masternodes/accountshistory" "masternodes/accountshistory -> masternodes/masternodes -> validation -> masternodes/accountshistory" @@ -40,6 +41,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "masternodes/govvariables/attributes -> masternodes/masternodes -> masternodes/govvariables/attributes" "masternodes/govvariables/attributes -> masternodes/masternodes -> masternodes/poolpairs -> masternodes/govvariables/attributes" "masternodes/govvariables/attributes -> masternodes/mn_checks -> masternodes/govvariables/attributes" + "masternodes/govvariables/attributes -> masternodes/mn_rpc -> masternodes/govvariables/attributes" "masternodes/govvariables/attributes -> validation -> masternodes/govvariables/attributes" "masternodes/govvariables/icx_takerfee_per_btc -> masternodes/gv -> masternodes/govvariables/icx_takerfee_per_btc" "masternodes/govvariables/loan_daily_reward -> masternodes/gv -> masternodes/govvariables/loan_daily_reward"