rippled
LedgerHandler.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 <ripple/app/ledger/LedgerToJson.h>
21 #include <ripple/app/main/Application.h>
22 #include <ripple/app/misc/LoadFeeTrack.h>
23 #include <ripple/json/Object.h>
24 #include <ripple/protocol/ErrorCodes.h>
25 #include <ripple/protocol/jss.h>
26 #include <ripple/resource/Fees.h>
27 #include <ripple/rpc/GRPCHandlers.h>
28 #include <ripple/rpc/Role.h>
29 #include <ripple/rpc/handlers/LedgerHandler.h>
30 #include <ripple/rpc/impl/RPCHelpers.h>
31 
32 namespace ripple {
33 namespace RPC {
34 
35 LedgerHandler::LedgerHandler(JsonContext& context) : context_(context)
36 {
37 }
38 
39 Status
41 {
42  auto const& params = context_.params;
43  bool needsLedger = params.isMember(jss::ledger) ||
44  params.isMember(jss::ledger_hash) ||
45  params.isMember(jss::ledger_index) || context_.app.config().reporting();
46  if (!needsLedger)
47  return Status::OK;
48 
49  if (auto s = lookupLedger(ledger_, context_, result_))
50  return s;
51 
52  bool const full = params[jss::full].asBool();
53  bool const transactions = params[jss::transactions].asBool();
54  bool const accounts = params[jss::accounts].asBool();
55  bool const expand = params[jss::expand].asBool();
56  bool const binary = params[jss::binary].asBool();
57  bool const owner_funds = params[jss::owner_funds].asBool();
58  bool const queue = params[jss::queue].asBool();
59  auto type = chooseLedgerEntryType(params);
60  if (type.first)
61  return type.first;
62  type_ = type.second;
63 
64  options_ = (full ? LedgerFill::full : 0) |
65  (expand ? LedgerFill::expand : 0) |
66  (transactions ? LedgerFill::dumpTxrp : 0) |
67  (accounts ? LedgerFill::dumpState : 0) |
68  (binary ? LedgerFill::binary : 0) |
69  (owner_funds ? LedgerFill::ownerFunds : 0) |
70  (queue ? LedgerFill::dumpQueue : 0);
71 
72  if (full || accounts)
73  {
74  // Until some sane way to get full ledgers has been implemented,
75  // disallow retrieving all state nodes.
77  return rpcNO_PERMISSION;
78 
81  {
82  return rpcTOO_BUSY;
83  }
86  }
87  if (queue)
88  {
89  if (!ledger_ || !ledger_->open())
90  {
91  // It doesn't make sense to request the queue
92  // with a non-existent or closed/validated ledger.
93  return rpcINVALID_PARAMS;
94  }
95 
97  }
98 
99  return Status::OK;
100 }
101 
102 } // namespace RPC
103 
106 {
107  auto begin = std::chrono::system_clock::now();
108  org::xrpl::rpc::v1::GetLedgerRequest& request = context.params;
109  org::xrpl::rpc::v1::GetLedgerResponse response;
110  grpc::Status status = grpc::Status::OK;
111 
113  if (auto status = RPC::ledgerFromRequest(ledger, context))
114  {
115  grpc::Status errorStatus;
116  if (status.toErrorCode() == rpcINVALID_PARAMS)
117  {
118  errorStatus = grpc::Status(
119  grpc::StatusCode::INVALID_ARGUMENT, status.message());
120  }
121  else
122  {
123  errorStatus =
124  grpc::Status(grpc::StatusCode::NOT_FOUND, status.message());
125  }
126  return {response, errorStatus};
127  }
128 
129  Serializer s;
130  addRaw(ledger->info(), s, true);
131 
132  response.set_ledger_header(s.peekData().data(), s.getLength());
133 
134  if (request.transactions())
135  {
136  try
137  {
138  for (auto& i : ledger->txs)
139  {
140  assert(i.first);
141  if (request.expand())
142  {
143  auto txn = response.mutable_transactions_list()
144  ->add_transactions();
145  Serializer sTxn = i.first->getSerializer();
146  txn->set_transaction_blob(sTxn.data(), sTxn.getLength());
147  if (i.second)
148  {
149  Serializer sMeta = i.second->getSerializer();
150  txn->set_metadata_blob(sMeta.data(), sMeta.getLength());
151  }
152  }
153  else
154  {
155  auto const& hash = i.first->getTransactionID();
156  response.mutable_hashes_list()->add_hashes(
157  hash.data(), hash.size());
158  }
159  }
160  }
161  catch (std::exception const& e)
162  {
163  JLOG(context.j.error())
164  << __func__ << " - Error deserializing transaction in ledger "
165  << ledger->info().seq
166  << " . skipping transaction and following transactions. You "
167  "should look into this further";
168  }
169  }
170 
171  if (request.get_objects())
172  {
174  context.app.getLedgerMaster().getLedgerBySeq(ledger->seq() - 1);
175 
177  std::dynamic_pointer_cast<Ledger const>(parent);
178  if (!base)
179  {
180  grpc::Status errorStatus{
181  grpc::StatusCode::NOT_FOUND, "parent ledger not validated"};
182  return {response, errorStatus};
183  }
184 
186  std::dynamic_pointer_cast<Ledger const>(ledger);
187  if (!desired)
188  {
189  grpc::Status errorStatus{
190  grpc::StatusCode::NOT_FOUND, "ledger not validated"};
191  return {response, errorStatus};
192  }
193  SHAMap::Delta differences;
194 
195  int maxDifferences = std::numeric_limits<int>::max();
196 
197  bool res = base->stateMap().compare(
198  desired->stateMap(), differences, maxDifferences);
199  if (!res)
200  {
201  grpc::Status errorStatus{
202  grpc::StatusCode::RESOURCE_EXHAUSTED,
203  "too many differences between specified ledgers"};
204  return {response, errorStatus};
205  }
206 
207  for (auto& [k, v] : differences)
208  {
209  auto obj = response.mutable_ledger_objects()->add_objects();
210  auto inBase = v.first;
211  auto inDesired = v.second;
212 
213  obj->set_key(k.data(), k.size());
214  if (inDesired)
215  {
216  assert(inDesired->size() > 0);
217  obj->set_data(inDesired->data(), inDesired->size());
218  }
219  if (inBase && inDesired)
220  obj->set_mod_type(
221  org::xrpl::rpc::v1::RawLedgerObject::MODIFIED);
222  else if (inBase && !inDesired)
223  obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
224  else
225  obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
226  auto const blob = inDesired ? inDesired->slice() : inBase->slice();
227  auto const objectType =
228  static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
229 
230  if (request.get_object_neighbors())
231  {
232  if (!(inBase && inDesired))
233  {
234  auto lb = desired->stateMap().lower_bound(k);
235  auto ub = desired->stateMap().upper_bound(k);
236  if (lb != desired->stateMap().end())
237  obj->set_predecessor(
238  lb->key().data(), lb->key().size());
239  if (ub != desired->stateMap().end())
240  obj->set_successor(ub->key().data(), ub->key().size());
241  if (objectType == ltDIR_NODE)
242  {
243  auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
244  if (!sle->isFieldPresent(sfOwner))
245  {
246  auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
247  if (!inBase && inDesired)
248  {
249  auto firstBook =
250  desired->stateMap().upper_bound(
251  bookBase.key);
252  if (firstBook != desired->stateMap().end() &&
253  firstBook->key() <
254  getQualityNext(bookBase.key) &&
255  firstBook->key() == k)
256  {
257  auto succ = response.add_book_successors();
258  succ->set_book_base(
259  bookBase.key.data(),
260  bookBase.key.size());
261  succ->set_first_book(
262  firstBook->key().data(),
263  firstBook->key().size());
264  }
265  }
266  if (inBase && !inDesired)
267  {
268  auto oldFirstBook =
269  base->stateMap().upper_bound(bookBase.key);
270  if (oldFirstBook != base->stateMap().end() &&
271  oldFirstBook->key() <
272  getQualityNext(bookBase.key) &&
273  oldFirstBook->key() == k)
274  {
275  auto succ = response.add_book_successors();
276  succ->set_book_base(
277  bookBase.key.data(),
278  bookBase.key.size());
279  auto newFirstBook =
280  desired->stateMap().upper_bound(
281  bookBase.key);
282 
283  if (newFirstBook !=
284  desired->stateMap().end() &&
285  newFirstBook->key() <
286  getQualityNext(bookBase.key))
287  {
288  succ->set_first_book(
289  newFirstBook->key().data(),
290  newFirstBook->key().size());
291  }
292  }
293  }
294  }
295  }
296  }
297  }
298  }
299  response.set_objects_included(true);
300  response.set_object_neighbors_included(request.get_object_neighbors());
301  response.set_skiplist_included(true);
302  }
303 
304  response.set_validated(
305  RPC::isValidated(context.ledgerMaster, *ledger, context.app));
306 
307  auto end = std::chrono::system_clock::now();
308  auto duration =
309  std::chrono::duration_cast<std::chrono::milliseconds>(end - begin)
310  .count() *
311  1.0;
312  JLOG(context.j.warn())
313  << __func__ << " - Extract time = " << duration
314  << " - num objects = " << response.ledger_objects().objects_size()
315  << " - num txns = " << response.transactions_list().transactions_size()
316  << " - ms per obj "
317  << duration / response.ledger_objects().objects_size()
318  << " - ms per txn "
319  << duration / response.transactions_list().transactions_size();
320 
321  return {response, status};
322 }
323 } // namespace ripple
ripple::ReadView::info
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
ripple::LedgerFill::dumpTxrp
@ dumpTxrp
Definition: LedgerToJson.h:46
ripple::RPC::JsonContext
Definition: Context.h:53
ripple::RPC::LedgerHandler::context_
JsonContext & context_
Definition: LedgerHandler.h:80
std::shared_ptr
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
std::exception
STL class.
ripple::Resource::feeMediumBurdenRPC
const Charge feeMediumBurdenRPC
std::pair
ripple::sfOwner
const SF_ACCOUNT sfOwner
ripple::addRaw
void addRaw(LedgerInfo const &info, Serializer &s, bool includeHash)
Definition: View.cpp:162
ripple::RPC::Context::loadType
Resource::Charge & loadType
Definition: Context.h:43
ripple::TxQ::getTxs
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
Definition: TxQ.cpp:1811
ripple::RPC::LedgerHandler::ledger_
std::shared_ptr< ReadView const > ledger_
Definition: LedgerHandler.h:81
ripple::LedgerFill::dumpQueue
@ dumpQueue
Definition: LedgerToJson.h:52
ripple::RPC::Context::ledgerMaster
LedgerMaster & ledgerMaster
Definition: Context.h:45
ripple::doLedgerGrpc
std::pair< org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status > doLedgerGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetLedgerRequest > &context)
Definition: LedgerHandler.cpp:105
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::rpcTOO_BUSY
@ rpcTOO_BUSY
Definition: ErrorCodes.h:56
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:83
ripple::getQualityNext
uint256 getQualityNext(uint256 const &uBase)
Definition: Indexes.cpp:100
ripple::RPC::Context::role
Role role
Definition: Context.h:47
ripple::LedgerFill::expand
@ expand
Definition: LedgerToJson.h:48
ripple::RPC::lookupLedger
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.
Definition: RPCHelpers.cpp:675
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
ripple::ltDIR_NODE
@ ltDIR_NODE
A ledger object which contains a list of object identifiers.
Definition: LedgerFormats.h:66
ripple::Serializer::data
void const * data() const noexcept
Definition: Serializer.h:75
ripple::RPC::Context::j
const beast::Journal j
Definition: Context.h:41
ripple::LoadFeeTrack::isLoadedLocal
bool isLoadedLocal() const
Definition: LoadFeeTrack.h:126
ripple::RPC::Status::OK
static constexpr Code OK
Definition: Status.h:46
ripple::Config::reporting
bool reporting() const
Definition: Config.h:337
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::Application::config
virtual Config & config()=0
ripple::Ledger::stateMap
SHAMap const & stateMap() const
Definition: Ledger.h:310
ripple::LedgerFill::full
@ full
Definition: LedgerToJson.h:49
ripple::RPC::GRPCContext
Definition: Context.h:70
ripple::Application::getTxQ
virtual TxQ & getTxQ()=0
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::LedgerMaster::getLedgerBySeq
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Definition: LedgerMaster.cpp:1818
ripple::SerialIter
Definition: Serializer.h:310
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
std::map
STL class.
ripple::RPC::Status
Status represents the results of an operation that might fail.
Definition: Status.h:39
ripple::isUnlimited
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition: Role.cpp:124
ripple::Serializer
Definition: Serializer.h:39
ripple::LedgerFill::binary
@ binary
Definition: LedgerToJson.h:50
ripple::SHAMap::upper_bound
const_iterator upper_bound(uint256 const &id) const
Find the first item after the given item.
Definition: SHAMap.cpp:615
ripple::RPC::GRPCContext::params
RequestType params
Definition: Context.h:72
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::LedgerFill::ownerFunds
@ ownerFunds
Definition: LedgerToJson.h:51
ripple::RPC::isValidated
bool isValidated(LedgerMaster &ledgerMaster, ReadView const &ledger, Application &app)
Definition: RPCHelpers.cpp:603
ripple::RPC::LedgerHandler::LedgerHandler
LedgerHandler(JsonContext &)
Definition: LedgerHandler.cpp:35
ripple::LedgerEntryType
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:53
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:193
ripple::RPC::chooseLedgerEntryType
std::pair< RPC::Status, LedgerEntryType > chooseLedgerEntryType(Json::Value const &params)
Definition: RPCHelpers.cpp:979
ripple::rpcNO_PERMISSION
@ rpcNO_PERMISSION
Definition: ErrorCodes.h:53
ripple::SHAMap::end
const_iterator end() const
Definition: SHAMap.h:752
ripple::RPC::LedgerHandler::type_
LedgerEntryType type_
Definition: LedgerHandler.h:85
ripple::Serializer::peekData
Blob const & peekData() const
Definition: Serializer.h:168
ripple::RPC::LedgerHandler::check
Status check()
Definition: LedgerHandler.cpp:40
ripple::SHAMap::compare
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
Definition: SHAMapDelta.cpp:124
ripple::keylet::quality
Keylet quality(Keylet const &k, std::uint64_t q) noexcept
The initial directory page for a specific quality.
Definition: Indexes.cpp:228
ripple::RPC::LedgerHandler::options_
int options_
Definition: LedgerHandler.h:84
ripple::RPC::LedgerHandler::queueTxs_
std::vector< TxQ::TxDetails > queueTxs_
Definition: LedgerHandler.h:82
ripple::SHAMap::lower_bound
const_iterator lower_bound(uint256 const &id) const
Find the object with the greatest object id smaller than the input id.
Definition: SHAMap.cpp:652
ripple::LedgerFill::dumpState
@ dumpState
Definition: LedgerToJson.h:47
std::numeric_limits::max
T max(T... args)
ripple::Serializer::getLength
int getLength() const
Definition: Serializer.h:199
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
std::vector::data
T data(T... args)
ripple::RPC::ledgerFromRequest
Status ledgerFromRequest(T &ledger, GRPCContext< R > &context)
Definition: RPCHelpers.cpp:394
ripple::Resource::feeHighBurdenRPC
const Charge feeHighBurdenRPC
ripple::RPC::LedgerHandler::result_
Json::Value result_
Definition: LedgerHandler.h:83
ripple::ReadView::txs
txs_type txs
Definition: ReadView.h:323
std::chrono::system_clock::now
T now(T... args)