mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 19:25:51 +00:00
GatewayBalances RPC command
RPC command to get a gateway's hot wallet balances and total obligations.
This commit is contained in:
@@ -2967,6 +2967,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\FetchInfo.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\GatewayBalances.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\GetCounts.cpp">
|
||||
<ExcludedFromBuild>True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
||||
@@ -3651,6 +3651,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\FetchInfo.cpp">
|
||||
<Filter>ripple\rpc\handlers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\GatewayBalances.cpp">
|
||||
<Filter>ripple\rpc\handlers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\rpc\handlers\GetCounts.cpp">
|
||||
<Filter>ripple\rpc\handlers</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
using pointer = std::shared_ptr <RippleState>;
|
||||
|
||||
public:
|
||||
RippleState() = delete;
|
||||
RippleState () = delete;
|
||||
|
||||
virtual ~RippleState() = default;
|
||||
|
||||
|
||||
@@ -790,6 +790,46 @@ private:
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
// parse gateway balances
|
||||
// gateway_balances [<ledger>] <issuer_account> [ <hotwallet> [ <hotwallet> ]]
|
||||
|
||||
Json::Value parseGatewayBalances (Json::Value const& jvParams)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
const unsigned int size = jvParams.size ();
|
||||
|
||||
Json::Value jvRequest;
|
||||
|
||||
std::string param = jvParams[index++].asString ();
|
||||
if (param.empty ())
|
||||
return RPC::make_param_error ("Invalid first parameter");
|
||||
|
||||
if (param[0] != 'r')
|
||||
{
|
||||
if (param.size() == 64)
|
||||
jvRequest[jss::ledger_hash] = param;
|
||||
else
|
||||
jvRequest[jss::ledger_index] = param;
|
||||
|
||||
if (size <= index)
|
||||
return RPC::make_param_error ("Invalid hotwallet");
|
||||
|
||||
param = jvParams[index++].asString ();
|
||||
}
|
||||
|
||||
jvRequest[jss::account] = param;
|
||||
|
||||
if (index < size)
|
||||
{
|
||||
Json::Value& hotWallets =
|
||||
(jvRequest["hotwallet"] = Json::arrayValue);
|
||||
while (index < size)
|
||||
hotWallets.append (jvParams[index++].asString ());
|
||||
}
|
||||
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -858,6 +898,7 @@ public:
|
||||
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 },
|
||||
{ "feature", &RPCParser::parseFeature, 0, 2 },
|
||||
{ "fetch_info", &RPCParser::parseFetchInfo, 0, 1 },
|
||||
{ "gateway_balances", &RPCParser::parseGatewayBalances , 1, -1 },
|
||||
{ "get_counts", &RPCParser::parseGetCounts, 0, 1 },
|
||||
{ "json", &RPCParser::parseJson, 2, 2 },
|
||||
{ "ledger", &RPCParser::parseLedger, 0, 2 },
|
||||
|
||||
@@ -79,8 +79,10 @@ JSS ( age ); // out: UniqueNodeList, NetworkOPs
|
||||
JSS ( alternatives ); // out: PathRequest, RipplePathFind
|
||||
JSS ( amendment_blocked ); // out: NetworkOPs
|
||||
JSS ( asks ); // out: Subscribe
|
||||
JSS ( assets ); // out: GatewayBalances
|
||||
JSS ( authorized ); // out: AccountLines
|
||||
JSS ( balance ); // out: AccountLines
|
||||
JSS ( balances ); // out: GatewayBalances
|
||||
JSS ( base ); // out: LogLevel
|
||||
JSS ( base_fee ); // out: NetworkOPs
|
||||
JSS ( base_fee_xrp ); // out: NetworkOPs
|
||||
@@ -252,6 +254,7 @@ JSS ( node_reads_total ); // out: GetCounts
|
||||
JSS ( node_writes ); // out: GetCounts
|
||||
JSS ( node_written_bytes ); // out: GetCounts
|
||||
JSS ( nodes ); // out: LedgerEntrySet, PathState
|
||||
JSS ( obligations ); // out: GatewayBalances
|
||||
JSS ( offer ); // in: LedgerEntry
|
||||
JSS ( offers ); // out: NetworkOPs, AccountOffers, Subscribe
|
||||
JSS ( offline ); // in: TransactionSign
|
||||
|
||||
229
src/ripple/rpc/handlers/GatewayBalances.cpp
Normal file
229
src/ripple/rpc/handlers/GatewayBalances.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2014 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/rpc/impl/AccountFromString.h>
|
||||
#include <ripple/rpc/impl/LookupLedger.h>
|
||||
#include <ripple/app/paths/RippleState.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Query:
|
||||
// 1) Specify ledger to query.
|
||||
// 2) Specify issuer account (cold wallet) in "account" field.
|
||||
// 3) Specify accounts that hold gateway assets (such as hot wallets)
|
||||
// using "hotwallet" field which should be either a string (if just
|
||||
// one wallet) or an array of strings (if more than one).
|
||||
|
||||
// Response:
|
||||
// 1) Array, "obligations", indicating the total obligations of the
|
||||
// gateway in each currency. Obligations to specified hot wallets
|
||||
// are not counted here.
|
||||
// 2) Object, "balances", indicating balances in each account
|
||||
// that holds gateway assets. (Those specified in the "hotwallet"
|
||||
// field.)
|
||||
// 3) Object of "assets" indicating accounts that owe the gateway.
|
||||
// (Gateways typically do not hold positive balances. This is unusual.)
|
||||
|
||||
// gateway_balances [<ledger>] <account> [<howallet> [<hotwallet [...
|
||||
|
||||
Json::Value doGatewayBalances (RPC::Context& context)
|
||||
{
|
||||
auto& params = context.params;
|
||||
|
||||
// Get the current ledger
|
||||
Ledger::pointer ledger;
|
||||
Json::Value result (RPC::lookupLedger (params, ledger, context.netOps));
|
||||
|
||||
if (!ledger)
|
||||
return result;
|
||||
|
||||
if (!(params.isMember (jss::account) || params.isMember (jss::ident)))
|
||||
return RPC::missing_field_error (jss::account);
|
||||
|
||||
std::string const strIdent (params.isMember (jss::account)
|
||||
? params[jss::account].asString ()
|
||||
: params[jss::ident].asString ());
|
||||
|
||||
int iIndex = 0;
|
||||
|
||||
if (params.isMember (jss::account_index))
|
||||
{
|
||||
auto const& accountIndex = params[jss::account_index];
|
||||
if (!accountIndex.isUInt() && !accountIndex.isInt ())
|
||||
return RPC::invalid_field_message (jss::account_index);
|
||||
iIndex = accountIndex.asUInt ();
|
||||
}
|
||||
|
||||
bool const bStrict = params.isMember (jss::strict) &&
|
||||
params[jss::strict].asBool ();
|
||||
|
||||
// Get info on account.
|
||||
bool bIndex; // out param
|
||||
RippleAddress naAccount; // out param
|
||||
Json::Value jvAccepted (RPC::accountFromString (
|
||||
naAccount, bIndex, strIdent, iIndex, bStrict));
|
||||
|
||||
if (!jvAccepted.empty ())
|
||||
return jvAccepted;
|
||||
|
||||
context.loadType = Resource::feeHighBurdenRPC;
|
||||
|
||||
result[jss::account] = naAccount.humanAccountID();
|
||||
auto accountID = naAccount.getAccountID();
|
||||
|
||||
// Parse the specified hotwallet(s), if any
|
||||
std::set <Account> hotWallets;
|
||||
|
||||
if (params.isMember ("hotwallet"))
|
||||
{
|
||||
Json::Value const& hw = params["hotwallet"];
|
||||
bool valid = true;
|
||||
|
||||
auto addHotWallet = [&valid, &hotWallets](Json::Value const& j)
|
||||
{
|
||||
if (j.isString())
|
||||
{
|
||||
RippleAddress ra;
|
||||
if (! ra.setAccountPublic (j.asString ()) &&
|
||||
! ra.setAccountID (j.asString()))
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
hotWallets.insert (ra.getAccountID ());
|
||||
}
|
||||
else
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (hw.isArray())
|
||||
{
|
||||
for (unsigned i = 0; i < hw.size(); ++i)
|
||||
addHotWallet (hw[i]);
|
||||
}
|
||||
else if (hw.isString())
|
||||
{
|
||||
addHotWallet (hw);
|
||||
}
|
||||
else
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (! valid)
|
||||
{
|
||||
result[jss::error] = "invalidHotWallet";
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::map <Currency, STAmount> sums;
|
||||
std::map <Account, std::vector <STAmount>> hotBalances;
|
||||
std::map <Account, std::vector <STAmount>> assets;
|
||||
|
||||
// Traverse the cold wallet's trust lines
|
||||
forEachItem(*ledger, accountID, getApp().getSLECache(),
|
||||
[&](std::shared_ptr<SLE const> const& sle)
|
||||
{
|
||||
auto rs = RippleState::makeItem (accountID, sle);
|
||||
|
||||
if (!rs)
|
||||
return;
|
||||
|
||||
int balSign = rs->getBalance().signum();
|
||||
if (balSign == 0)
|
||||
return;
|
||||
|
||||
auto const& peer = rs->getAccountIDPeer();
|
||||
|
||||
// Here, a negative balance means the cold wallet owes (normal)
|
||||
// A positive balance means the cold wallet has an asset (unusual)
|
||||
|
||||
if (hotWallets.count (peer) > 0)
|
||||
{
|
||||
// This is a specified hot wallt
|
||||
hotBalances[peer].push_back (-rs->getBalance ());
|
||||
}
|
||||
else if (balSign > 0)
|
||||
{
|
||||
// This is a gateway asset
|
||||
assets[peer].push_back (rs->getBalance ());
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal negative balance, obligation to customer
|
||||
auto& bal = sums[rs->getBalance().getCurrency()];
|
||||
if (bal == zero)
|
||||
{
|
||||
// This is needed to set the currency code correctly
|
||||
bal = -rs->getBalance();
|
||||
}
|
||||
else
|
||||
bal -= rs->getBalance();
|
||||
}
|
||||
});
|
||||
|
||||
if (! sums.empty())
|
||||
{
|
||||
Json::Value& j = (result [jss::obligations] = Json::objectValue);
|
||||
for (auto const& e : sums)
|
||||
{
|
||||
j[to_string (e.first)] = e.second.getText ();
|
||||
}
|
||||
}
|
||||
|
||||
if (! hotBalances.empty())
|
||||
{
|
||||
Json::Value& j = (result [jss::balances] = Json::objectValue);
|
||||
for (auto const& account : hotBalances)
|
||||
{
|
||||
Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue);
|
||||
for (auto const& balance : account.second)
|
||||
{
|
||||
Json::Value& entry = balanceArray.append (Json::objectValue);
|
||||
entry[jss::currency] = to_string (balance.issue ().currency);
|
||||
entry[jss::value] = balance.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! assets.empty())
|
||||
{
|
||||
Json::Value& j = (result [jss::assets] = Json::objectValue);
|
||||
|
||||
for (auto const& account : assets)
|
||||
{
|
||||
Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue);
|
||||
for (auto const& balance : account.second)
|
||||
{
|
||||
Json::Value& entry = balanceArray.append (Json::objectValue);
|
||||
entry[jss::currency] = to_string (balance.issue ().currency);
|
||||
entry[jss::value] = balance.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
@@ -37,6 +37,7 @@ Json::Value doConnect (RPC::Context&);
|
||||
Json::Value doConsensusInfo (RPC::Context&);
|
||||
Json::Value doFeature (RPC::Context&);
|
||||
Json::Value doFetchInfo (RPC::Context&);
|
||||
Json::Value doGatewayBalances (RPC::Context&);
|
||||
Json::Value doGetCounts (RPC::Context&);
|
||||
Json::Value doInternal (RPC::Context&);
|
||||
Json::Value doLedgerAccept (RPC::Context&);
|
||||
|
||||
@@ -109,6 +109,7 @@ HandlerTable HANDLERS({
|
||||
{ "can_delete", byRef (&doCanDelete), Role::ADMIN, NO_CONDITION },
|
||||
{ "connect", byRef (&doConnect), Role::ADMIN, NO_CONDITION },
|
||||
{ "consensus_info", byRef (&doConsensusInfo), Role::ADMIN, NO_CONDITION },
|
||||
{ "gateway_balances", byRef (&doGatewayBalances), Role::USER, NO_CONDITION },
|
||||
{ "get_counts", byRef (&doGetCounts), Role::ADMIN, NO_CONDITION },
|
||||
{ "internal", byRef (&doInternal), Role::ADMIN, NO_CONDITION },
|
||||
{ "feature", byRef (&doFeature), Role::ADMIN, NO_CONDITION },
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <ripple/rpc/handlers/ConsensusInfo.cpp>
|
||||
#include <ripple/rpc/handlers/Feature.cpp>
|
||||
#include <ripple/rpc/handlers/FetchInfo.cpp>
|
||||
#include <ripple/rpc/handlers/GatewayBalances.cpp>
|
||||
#include <ripple/rpc/handlers/GetCounts.cpp>
|
||||
#include <ripple/rpc/handlers/Internal.cpp>
|
||||
#include <ripple/rpc/handlers/Ledger.cpp>
|
||||
|
||||
Reference in New Issue
Block a user