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