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