rippled
LedgerReplayMsgHandler.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2020 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/LedgerMaster.h>
21 #include <ripple/app/ledger/LedgerReplayer.h>
22 #include <ripple/app/ledger/impl/LedgerReplayMsgHandler.h>
23 #include <ripple/app/main/Application.h>
24 
25 #include <memory>
26 
27 namespace ripple {
29  Application& app,
30  LedgerReplayer& replayer)
31  : app_(app)
32  , replayer_(replayer)
33  , journal_(app.journal("LedgerReplayMsgHandler"))
34 {
35 }
36 
37 protocol::TMProofPathResponse
40 {
41  protocol::TMProofPathRequest& packet = *msg;
42  protocol::TMProofPathResponse reply;
43 
44  if (!packet.has_key() || !packet.has_ledgerhash() || !packet.has_type() ||
45  packet.ledgerhash().size() != uint256::size() ||
46  packet.key().size() != uint256::size() ||
47  !protocol::TMLedgerMapType_IsValid(packet.type()))
48  {
49  JLOG(journal_.debug()) << "getProofPath: Invalid request";
50  reply.set_error(protocol::TMReplyError::reBAD_REQUEST);
51  return reply;
52  }
53  reply.set_key(packet.key());
54  reply.set_ledgerhash(packet.ledgerhash());
55  reply.set_type(packet.type());
56 
57  uint256 const key(packet.key());
58  uint256 const ledgerHash(packet.ledgerhash());
59  auto ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash);
60  if (!ledger)
61  {
62  JLOG(journal_.debug())
63  << "getProofPath: Don't have ledger " << ledgerHash;
64  reply.set_error(protocol::TMReplyError::reNO_LEDGER);
65  return reply;
66  }
67 
68  auto const path = [&]() -> std::optional<std::vector<Blob>> {
69  switch (packet.type())
70  {
71  case protocol::lmACCOUNT_STATE:
72  return ledger->stateMap().getProofPath(key);
73  case protocol::lmTRANASCTION:
74  return ledger->txMap().getProofPath(key);
75  default:
76  // should not be here
77  // because already tested with TMLedgerMapType_IsValid()
78  return {};
79  }
80  }();
81 
82  if (!path)
83  {
84  JLOG(journal_.debug()) << "getProofPath: Don't have the node " << key
85  << " of ledger " << ledgerHash;
86  reply.set_error(protocol::TMReplyError::reNO_NODE);
87  return reply;
88  }
89 
90  // pack header
91  Serializer nData(128);
92  addRaw(ledger->info(), nData);
93  reply.set_ledgerheader(nData.getDataPtr(), nData.getLength());
94  // pack path
95  for (auto const& b : *path)
96  reply.add_path(b.data(), b.size());
97 
98  JLOG(journal_.debug()) << "getProofPath for the node " << key
99  << " of ledger " << ledgerHash << " path length "
100  << path->size();
101  return reply;
102 }
103 
104 bool
107 {
108  protocol::TMProofPathResponse& reply = *msg;
109  if (reply.has_error() || !reply.has_key() || !reply.has_ledgerhash() ||
110  !reply.has_type() || !reply.has_ledgerheader() ||
111  reply.path_size() == 0)
112  {
113  JLOG(journal_.debug()) << "Bad message: Error reply";
114  return false;
115  }
116 
117  if (reply.type() != protocol::lmACCOUNT_STATE)
118  {
119  JLOG(journal_.debug())
120  << "Bad message: we only support the state ShaMap for now";
121  return false;
122  }
123 
124  // deserialize the header
125  auto info = deserializeHeader(
126  {reply.ledgerheader().data(), reply.ledgerheader().size()});
127  uint256 replyHash(reply.ledgerhash());
128  if (calculateLedgerHash(info) != replyHash)
129  {
130  JLOG(journal_.debug()) << "Bad message: Hash mismatch";
131  return false;
132  }
133  info.hash = replyHash;
134 
135  uint256 key(reply.key());
136  if (key != keylet::skip().key)
137  {
138  JLOG(journal_.debug())
139  << "Bad message: we only support the short skip list for now. "
140  "Key in reply "
141  << key;
142  return false;
143  }
144 
145  // verify the skip list
146  std::vector<Blob> path;
147  path.reserve(reply.path_size());
148  for (int i = 0; i < reply.path_size(); ++i)
149  {
150  path.emplace_back(reply.path(i).begin(), reply.path(i).end());
151  }
152 
153  if (!SHAMap::verifyProofPath(info.accountHash, key, path))
154  {
155  JLOG(journal_.debug()) << "Bad message: Proof path verify failed";
156  return false;
157  }
158 
159  // deserialize the SHAMapItem
160  auto node = SHAMapTreeNode::makeFromWire(makeSlice(path.front()));
161  if (!node || !node->isLeaf())
162  {
163  JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
164  return false;
165  }
166 
167  if (auto item = static_cast<SHAMapLeafNode*>(node.get())->peekItem())
168  {
169  replayer_.gotSkipList(info, item);
170  return true;
171  }
172 
173  JLOG(journal_.debug()) << "Bad message: Cannot get ShaMapItem";
174  return false;
175 }
176 
177 protocol::TMReplayDeltaResponse
180 {
181  protocol::TMReplayDeltaRequest& packet = *msg;
182  protocol::TMReplayDeltaResponse reply;
183 
184  if (!packet.has_ledgerhash() ||
185  packet.ledgerhash().size() != uint256::size())
186  {
187  JLOG(journal_.debug()) << "getReplayDelta: Invalid request";
188  reply.set_error(protocol::TMReplyError::reBAD_REQUEST);
189  return reply;
190  }
191  reply.set_ledgerhash(packet.ledgerhash());
192 
193  uint256 const ledgerHash{packet.ledgerhash()};
194  auto ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash);
195  if (!ledger || !ledger->isImmutable())
196  {
197  JLOG(journal_.debug())
198  << "getReplayDelta: Don't have ledger " << ledgerHash;
199  reply.set_error(protocol::TMReplyError::reNO_LEDGER);
200  return reply;
201  }
202 
203  // pack header
204  Serializer nData(128);
205  addRaw(ledger->info(), nData);
206  reply.set_ledgerheader(nData.getDataPtr(), nData.getLength());
207  // pack transactions
208  auto const& txMap = ledger->txMap();
209  txMap.visitLeaves(
210  [&](boost::intrusive_ptr<SHAMapItem const> const& txNode) {
211  reply.add_transaction(txNode->data(), txNode->size());
212  });
213 
214  JLOG(journal_.debug()) << "getReplayDelta for ledger " << ledgerHash
215  << " txMap hash " << txMap.getHash().as_uint256();
216  return reply;
217 }
218 
219 bool
222 {
223  protocol::TMReplayDeltaResponse& reply = *msg;
224  if (reply.has_error() || !reply.has_ledgerheader())
225  {
226  JLOG(journal_.debug()) << "Bad message: Error reply";
227  return false;
228  }
229 
230  auto info = deserializeHeader(
231  {reply.ledgerheader().data(), reply.ledgerheader().size()});
232  uint256 replyHash(reply.ledgerhash());
233  if (calculateLedgerHash(info) != replyHash)
234  {
235  JLOG(journal_.debug()) << "Bad message: Hash mismatch";
236  return false;
237  }
238  info.hash = replyHash;
239 
240  auto numTxns = reply.transaction_size();
243  try
244  {
245  for (int i = 0; i < numTxns; ++i)
246  {
247  // deserialize:
248  // -- TxShaMapItem for building a ShaMap for verification
249  // -- Tx
250  // -- TxMetaData for Tx ordering
251  Serializer shaMapItemData(
252  reply.transaction(i).data(), reply.transaction(i).size());
253 
254  SerialIter txMetaSit(makeSlice(reply.transaction(i)));
255  SerialIter txSit(txMetaSit.getSlice(txMetaSit.getVLDataLength()));
256  SerialIter metaSit(txMetaSit.getSlice(txMetaSit.getVLDataLength()));
257 
258  auto tx = std::make_shared<STTx const>(txSit);
259  if (!tx)
260  {
261  JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
262  return false;
263  }
264  auto tid = tx->getTransactionID();
265  STObject meta(metaSit, sfMetadata);
266  orderedTxns.emplace(meta[sfTransactionIndex], std::move(tx));
267 
268  if (!txMap.addGiveItem(
270  make_shamapitem(tid, shaMapItemData.slice())))
271  {
272  JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
273  return false;
274  }
275  }
276  }
277  catch (std::exception const&)
278  {
279  JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
280  return false;
281  }
282 
283  if (txMap.getHash().as_uint256() != info.txHash)
284  {
285  JLOG(journal_.debug()) << "Bad message: Transactions verify failed";
286  return false;
287  }
288 
289  replayer_.gotReplayDelta(info, std::move(orderedTxns));
290  return true;
291 }
292 
293 } // namespace ripple
ripple::Application
Definition: Application.h:115
ripple::Application::getNodeFamily
virtual Family & getNodeFamily()=0
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:241
ripple::sfMetadata
const SField sfMetadata
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:852
ripple::HashPrefix::txNode
@ txNode
transaction plus metadata
std::exception
STL class.
ripple::SHAMapLeafNode::peekItem
boost::intrusive_ptr< SHAMapItem const > const & peekItem() const
Definition: SHAMapLeafNode.cpp:44
ripple::SHAMapType::TRANSACTION
@ TRANSACTION
ripple::make_shamapitem
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition: SHAMapItem.h:160
ripple::addRaw
void addRaw(LedgerInfo const &info, Serializer &s, bool includeHash)
Definition: View.cpp:162
std::vector
STL class.
ripple::LedgerReplayMsgHandler::LedgerReplayMsgHandler
LedgerReplayMsgHandler(Application &app, LedgerReplayer &replayer)
Definition: LedgerReplayMsgHandler.cpp:28
ripple::keylet::skip
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition: Indexes.cpp:145
std::map::emplace
T emplace(T... args)
ripple::LedgerReplayMsgHandler::processReplayDeltaRequest
protocol::TMReplayDeltaResponse processReplayDeltaRequest(std::shared_ptr< protocol::TMReplayDeltaRequest > const &msg)
Process TMReplayDeltaRequest and return TMReplayDeltaResponse.
Definition: LedgerReplayMsgHandler.cpp:178
ripple::LedgerMaster::getLedgerByHash
std::shared_ptr< Ledger const > getLedgerByHash(uint256 const &hash)
Definition: LedgerMaster.cpp:1854
ripple::LedgerReplayMsgHandler::journal_
beast::Journal journal_
Definition: LedgerReplayMsgHandler.h:73
ripple::SHAMapTreeNode::makeFromWire
static std::shared_ptr< SHAMapTreeNode > makeFromWire(Slice rawNode)
Definition: SHAMapTreeNode.cpp:116
ripple::SHAMapLeafNode
Definition: SHAMapLeafNode.h:32
ripple::base_uint< 256 >::size
constexpr static std::size_t size()
Definition: base_uint.h:519
ripple::LedgerReplayer::gotSkipList
void gotSkipList(LedgerInfo const &info, boost::intrusive_ptr< SHAMapItem const > const &data)
Process a skip list (extracted from a TMProofPathResponse message)
Definition: LedgerReplayer.cpp:173
ripple::base_uint< 256 >
ripple::SHAMap::verifyProofPath
static bool verifyProofPath(uint256 const &rootHash, uint256 const &key, std::vector< Blob > const &path)
Verify the proof path.
Definition: SHAMapSync.cpp:826
ripple::LedgerReplayMsgHandler::processProofPathResponse
bool processProofPathResponse(std::shared_ptr< protocol::TMProofPathResponse > const &msg)
Process TMProofPathResponse.
Definition: LedgerReplayMsgHandler.cpp:105
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::SHAMap::addGiveItem
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:774
ripple::LedgerReplayMsgHandler::app_
Application & app_
Definition: LedgerReplayMsgHandler.h:71
ripple::calculateLedgerHash
uint256 calculateLedgerHash(LedgerInfo const &info)
Definition: Ledger.cpp:65
ripple::Serializer::getDataPtr
const void * getDataPtr() const
Definition: Serializer.h:189
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:95
ripple::SHAMapNodeType::tnTRANSACTION_MD
@ tnTRANSACTION_MD
ripple::LedgerReplayMsgHandler::processProofPathRequest
protocol::TMProofPathResponse processProofPathRequest(std::shared_ptr< protocol::TMProofPathRequest > const &msg)
Process TMProofPathRequest and return TMProofPathResponse.
Definition: LedgerReplayMsgHandler.cpp:38
ripple::Serializer::slice
Slice slice() const noexcept
Definition: Serializer.h:63
ripple::SerialIter
Definition: Serializer.h:310
ripple::LedgerReplayer::gotReplayDelta
void gotReplayDelta(LedgerInfo const &info, std::map< std::uint32_t, std::shared_ptr< STTx const >> &&txns)
Process a ledger delta (extracted from a TMReplayDeltaResponse message)
Definition: LedgerReplayer.cpp:196
std::map
STL class.
memory
ripple::sfTransactionIndex
const SF_UINT32 sfTransactionIndex
ripple::Serializer
Definition: Serializer.h:39
ripple::LedgerReplayMsgHandler::replayer_
LedgerReplayer & replayer_
Definition: LedgerReplayMsgHandler.h:72
ripple::STObject
Definition: STObject.h:51
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::deserializeHeader
LedgerInfo deserializeHeader(Slice data, bool hasHash)
Deserialize a ledger header from a byte array.
Definition: InboundLedger.cpp:275
ripple::SerialIter::getVLDataLength
int getVLDataLength()
Definition: Serializer.cpp:470
ripple::LedgerReplayer
Manages the lifetime of ledger replay tasks.
Definition: LedgerReplayer.h:72
std::optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapHash.h:43
ripple::Serializer::getLength
int getLength() const
Definition: Serializer.h:199
ripple::LedgerReplayMsgHandler::processReplayDeltaResponse
bool processReplayDeltaResponse(std::shared_ptr< protocol::TMReplayDeltaResponse > const &msg)
Process TMReplayDeltaResponse.
Definition: LedgerReplayMsgHandler.cpp:220
ripple::SerialIter::getSlice
Slice getSlice(std::size_t bytes)
Definition: Serializer.cpp:495