rippled
Loading...
Searching...
No Matches
GatewayBalances.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/main/Application.h>
21#include <xrpld/app/paths/TrustLine.h>
22#include <xrpld/rpc/Context.h>
23#include <xrpld/rpc/detail/RPCHelpers.h>
24
25#include <xrpl/ledger/ReadView.h>
26#include <xrpl/protocol/AccountID.h>
27#include <xrpl/protocol/ErrorCodes.h>
28#include <xrpl/protocol/RPCErr.h>
29#include <xrpl/protocol/jss.h>
30#include <xrpl/resource/Fees.h>
31
32namespace ripple {
33
34// Query:
35// 1) Specify ledger to query.
36// 2) Specify issuer account (cold wallet) in "account" field.
37// 3) Specify accounts that hold gateway assets (such as hot wallets)
38// using "hotwallet" field which should be either a string (if just
39// one wallet) or an array of strings (if more than one).
40
41// Response:
42// 1) Array, "obligations", indicating the total obligations of the
43// gateway in each currency. Obligations to specified hot wallets
44// are not counted here.
45// 2) Object, "balances", indicating balances in each account
46// that holds gateway assets. (Those specified in the "hotwallet"
47// field.)
48// 3) Object of "assets" indicating accounts that owe the gateway.
49// (Gateways typically do not hold positive balances. This is unusual.)
50
51// gateway_balances [<ledger>] <account> [<howallet> [<hotwallet [...
52
55{
56 auto& params = context.params;
57
58 // Get the current ledger
60 auto result = RPC::lookupLedger(ledger, context);
61
62 if (!ledger)
63 return result;
64
65 if (!(params.isMember(jss::account) || params.isMember(jss::ident)))
66 return RPC::missing_field_error(jss::account);
67
68 std::string const strIdent(
69 params.isMember(jss::account) ? params[jss::account].asString()
70 : params[jss::ident].asString());
71
72 // Get info on account.
73 auto id = parseBase58<AccountID>(strIdent);
74 if (!id)
76 auto const accountID{std::move(id.value())};
78
79 result[jss::account] = toBase58(accountID);
80
81 if (context.apiVersion > 1u && !ledger->exists(keylet::account(accountID)))
82 {
84 return result;
85 }
86
87 // Parse the specified hotwallet(s), if any
88 std::set<AccountID> hotWallets;
89
90 if (params.isMember(jss::hotwallet))
91 {
92 auto addHotWallet = [&hotWallets](Json::Value const& j) {
93 if (j.isString())
94 {
95 if (auto id = parseBase58<AccountID>(j.asString()); id)
96 {
97 hotWallets.insert(std::move(id.value()));
98 return true;
99 }
100 }
101
102 return false;
103 };
104
105 Json::Value const& hw = params[jss::hotwallet];
106 bool valid = true;
107
108 // null is treated as a valid 0-sized array of hotwallet
109 if (hw.isArrayOrNull())
110 {
111 for (unsigned i = 0; i < hw.size(); ++i)
112 valid &= addHotWallet(hw[i]);
113 }
114 else if (hw.isString())
115 {
116 valid &= addHotWallet(hw);
117 }
118 else
119 {
120 valid = false;
121 }
122
123 if (!valid)
124 {
125 // The documentation states that invalidParams is used when
126 // One or more fields are specified incorrectly.
127 // invalidHotwallet should be used when the account exists, but does
128 // not have currency issued by the account from the request.
129 if (context.apiVersion < 2u)
130 {
132 }
133 else
134 {
136 }
137 return result;
138 }
139 }
140
146
147 // Traverse the cold wallet's trust lines
148 {
150 *ledger, accountID, [&](std::shared_ptr<SLE const> const& sle) {
151 if (sle->getType() == ltESCROW)
152 {
153 auto const& escrow = sle->getFieldAmount(sfAmount);
154 auto& bal = locked[escrow.getCurrency()];
155 if (bal == beast::zero)
156 {
157 // This is needed to set the currency code correctly
158 bal = escrow;
159 }
160 else
161 {
162 try
163 {
164 bal += escrow;
165 }
166 catch (std::runtime_error const&)
167 {
168 // Presumably the exception was caused by overflow.
169 // On overflow return the largest valid STAmount.
170 // Very large sums of STAmount are approximations
171 // anyway.
172 bal = STAmount(
173 bal.issue(),
176 }
177 }
178 }
179
180 auto rs = PathFindTrustLine::makeItem(accountID, sle);
181
182 if (!rs)
183 return;
184
185 int balSign = rs->getBalance().signum();
186 if (balSign == 0)
187 return;
188
189 auto const& peer = rs->getAccountIDPeer();
190
191 // Here, a negative balance means the cold wallet owes (normal)
192 // A positive balance means the cold wallet has an asset
193 // (unusual)
194
195 if (hotWallets.count(peer) > 0)
196 {
197 // This is a specified hot wallet
198 hotBalances[peer].push_back(-rs->getBalance());
199 }
200 else if (balSign > 0)
201 {
202 // This is a gateway asset
203 assets[peer].push_back(rs->getBalance());
204 }
205 else if (rs->getFreeze())
206 {
207 // An obligation the gateway has frozen
208 frozenBalances[peer].push_back(-rs->getBalance());
209 }
210 else
211 {
212 // normal negative balance, obligation to customer
213 auto& bal = sums[rs->getBalance().getCurrency()];
214 if (bal == beast::zero)
215 {
216 // This is needed to set the currency code correctly
217 bal = -rs->getBalance();
218 }
219 else
220 {
221 try
222 {
223 bal -= rs->getBalance();
224 }
225 catch (std::runtime_error const&)
226 {
227 // Presumably the exception was caused by overflow.
228 // On overflow return the largest valid STAmount.
229 // Very large sums of STAmount are approximations
230 // anyway.
231 bal = STAmount(
232 bal.issue(),
235 }
236 }
237 }
238 });
239 }
240
241 if (!sums.empty())
242 {
243 Json::Value j;
244 for (auto const& [k, v] : sums)
245 {
246 j[to_string(k)] = v.getText();
247 }
248 result[jss::obligations] = std::move(j);
249 }
250
251 auto populateResult =
252 [&result](
254 Json::StaticString const& name) {
255 if (!array.empty())
256 {
257 Json::Value j;
258 for (auto const& [accId, accBalances] : array)
259 {
260 Json::Value balanceArray;
261 for (auto const& balance : accBalances)
262 {
264 entry[jss::currency] =
265 to_string(balance.issue().currency);
266 entry[jss::value] = balance.getText();
267 balanceArray.append(std::move(entry));
268 }
269 j[to_string(accId)] = std::move(balanceArray);
270 }
271 result[name] = std::move(j);
272 }
273 };
274
275 populateResult(hotBalances, jss::balances);
276 populateResult(frozenBalances, jss::frozen_balances);
277 populateResult(assets, jss::assets);
278
279 // Add total escrow to the result
280 if (!locked.empty())
281 {
282 Json::Value j;
283 for (auto const& [k, v] : locked)
284 {
285 j[to_string(k)] = v.getText();
286 }
287 result[jss::locked] = std::move(j);
288 }
289
290 return result;
291}
292
293} // namespace ripple
Lightweight wrapper to tag static string.
Definition json_value.h:63
Represents a JSON value.
Definition json_value.h:149
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
bool isString() const
bool isArrayOrNull() const
static std::optional< PathFindTrustLine > makeItem(AccountID const &accountID, std::shared_ptr< SLE const > const &sle)
Definition TrustLine.cpp:52
static int const cMaxOffset
Definition STAmount.h:66
static std::uint64_t const cMaxValue
Definition STAmount.h:70
T count(T... args)
T empty(T... args)
T insert(T... args)
void inject_error(error_code_i code, JsonValue &json)
Add or update the json update to reflect the error code.
Definition ErrorCodes.h:233
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Json::Value missing_field_error(std::string const &name)
Definition ErrorCodes.h:283
Charge const feeHeavyBurdenRPC
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Json::Value entry(jtx::Env &env, jtx::Account const &account, jtx::Account const &authorize)
Definition delegate.cpp:55
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
@ rpcACT_NOT_FOUND
Definition ErrorCodes.h:70
@ rpcACT_MALFORMED
Definition ErrorCodes.h:90
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:84
@ rpcINVALID_HOTWALLET
Definition ErrorCodes.h:81
Json::Value rpcError(int iError)
Definition RPCErr.cpp:31
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition View.cpp:656
Json::Value doGatewayBalances(RPC::JsonContext &context)
unsigned int apiVersion
Definition Context.h:49
Resource::Charge & loadType
Definition Context.h:42
T to_string(T... args)