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