xrpld
Loading...
Searching...
No Matches
LedgerReplayMsgHandler.cpp
1#include <xrpld/app/ledger/detail/LedgerReplayMsgHandler.h>
2
3#include <xrpld/app/ledger/LedgerMaster.h>
4#include <xrpld/app/ledger/LedgerReplayer.h>
5#include <xrpld/app/main/Application.h>
6
7#include <xrpl/basics/Blob.h>
8#include <xrpl/basics/Log.h>
9#include <xrpl/basics/Slice.h>
10#include <xrpl/basics/base_uint.h>
11#include <xrpl/basics/safe_cast.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/LedgerHeader.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/STObject.h>
16#include <xrpl/protocol/STTx.h>
17#include <xrpl/protocol/Serializer.h>
18#include <xrpl/shamap/SHAMapItem.h>
19#include <xrpl/shamap/SHAMapMissingNode.h>
20#include <xrpl/shamap/SHAMapTreeNode.h>
21
22#include <boost/smart_ptr/intrusive_ptr.hpp>
23
24#include <xrpl.pb.h>
25
26#include <cstdint>
27#include <exception>
28#include <map>
29#include <memory>
30#include <optional>
31#include <utility>
32#include <vector>
33
34namespace xrpl {
36 : app_(app), replayer_(replayer), journal_(app.getJournal("LedgerReplayMsgHandler"))
37{
38}
39
40protocol::TMProofPathResponse
43{
44 protocol::TMProofPathRequest& packet = *msg;
45 protocol::TMProofPathResponse reply;
46
47 if (!packet.has_key() || !packet.has_ledgerhash() || !packet.has_type() ||
48 packet.ledgerhash().size() != uint256::size() || 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 = uint256::fromRaw(packet.key());
60 uint256 const ledgerHash = uint256::fromRaw(packet.ledgerhash());
61 auto ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash);
62 if (!ledger)
63 {
64 JLOG(journal_.debug()) << "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::lmTRANSACTION:
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 << " of ledger "
86 << ledgerHash;
87 reply.set_error(protocol::TMReplyError::reNO_NODE);
88 return reply;
89 }
90
91 // pack header
92 Serializer nData(128);
93 addRaw(ledger->header(), 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 << " of ledger " << ledgerHash
100 << " path length " << path->size();
101 return reply;
102}
103
104bool
107{
108 protocol::TMProofPathResponse const& reply = *msg;
109 if (reply.has_error() || !reply.has_key() || !reply.has_ledgerhash() || !reply.has_type() ||
110 !reply.has_ledgerheader() || reply.path_size() == 0 ||
111 reply.ledgerhash().size() != uint256::size() || reply.key().size() != uint256::size())
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()) << "Bad message: we only support the state ShaMap for now";
120 return false;
121 }
122
123 // deserialize the header
124 auto info = deserializeHeader({reply.ledgerheader().data(), reply.ledgerheader().size()});
125 uint256 const replyHash = uint256::fromRaw(reply.ledgerhash());
126 if (calculateLedgerHash(info) != replyHash)
127 {
128 JLOG(journal_.debug()) << "Bad message: Hash mismatch";
129 return false;
130 }
131 info.hash = replyHash;
132
133 uint256 const key = uint256::fromRaw(reply.key());
134 if (key != keylet::skip().key)
135 {
136 JLOG(journal_.debug()) << "Bad message: we only support the short skip list for now. "
137 "Key in reply "
138 << key;
139 return false;
140 }
141
142 // verify the skip list
144 path.reserve(reply.path_size());
145 for (int i = 0; i < reply.path_size(); ++i)
146 {
147 path.emplace_back(reply.path(i).begin(), reply.path(i).end());
148 }
149
150 if (!SHAMap::verifyProofPath(info.accountHash, key, path))
151 {
152 JLOG(journal_.debug()) << "Bad message: Proof path verify failed";
153 return false;
154 }
155
156 // deserialize the SHAMapItem
157 auto node = SHAMapTreeNode::makeFromWire(makeSlice(path.front()));
158 if (!node || !node->isLeaf())
159 {
160 JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
161 return false;
162 }
163
164 if (auto item = safeDowncast<SHAMapLeafNode*>(node.get())->peekItem())
165 {
166 replayer_.gotSkipList(info, item);
167 return true;
168 }
169
170 JLOG(journal_.debug()) << "Bad message: Cannot get ShaMapItem";
171 return false;
172}
173
174protocol::TMReplayDeltaResponse
177{
178 protocol::TMReplayDeltaRequest const& packet = *msg;
179 protocol::TMReplayDeltaResponse reply;
180
181 if (!packet.has_ledgerhash() || packet.ledgerhash().size() != uint256::size())
182 {
183 JLOG(journal_.debug()) << "getReplayDelta: Invalid request";
184 reply.set_error(protocol::TMReplyError::reBAD_REQUEST);
185 return reply;
186 }
187 reply.set_ledgerhash(packet.ledgerhash());
188
189 uint256 const ledgerHash = uint256::fromRaw(packet.ledgerhash());
190 auto ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash);
191 if (!ledger || !ledger->isImmutable())
192 {
193 JLOG(journal_.debug()) << "getReplayDelta: Don't have ledger " << ledgerHash;
194 reply.set_error(protocol::TMReplyError::reNO_LEDGER);
195 return reply;
196 }
197
198 // pack header
199 Serializer nData(128);
200 addRaw(ledger->header(), nData);
201 reply.set_ledgerheader(nData.getDataPtr(), nData.getLength());
202 // pack transactions
203 auto const& txMap = ledger->txMap();
204 txMap.visitLeaves([&](boost::intrusive_ptr<SHAMapItem const> const& txNode) {
205 reply.add_transaction(txNode->data(), txNode->size());
206 });
207
208 JLOG(journal_.debug()) << "getReplayDelta for ledger " << ledgerHash << " txMap hash "
209 << txMap.getHash().asUInt256();
210 return reply;
211}
212
213bool
216{
217 protocol::TMReplayDeltaResponse const& reply = *msg;
218 if (reply.has_error() || !reply.has_ledgerheader() || !reply.has_ledgerhash() ||
219 reply.ledgerhash().size() != uint256::size())
220 {
221 JLOG(journal_.debug()) << "Bad message: Error reply";
222 return false;
223 }
224
225 auto info = deserializeHeader({reply.ledgerheader().data(), reply.ledgerheader().size()});
226 uint256 const replyHash = uint256::fromRaw(reply.ledgerhash());
227 if (calculateLedgerHash(info) != replyHash)
228 {
229 JLOG(journal_.debug()) << "Bad message: Hash mismatch";
230 return false;
231 }
232 info.hash = replyHash;
233
234 auto numTxns = reply.transaction_size();
236 SHAMap txMap(SHAMapType::TRANSACTION, app_.getNodeFamily());
237 try
238 {
239 for (int i = 0; i < numTxns; ++i)
240 {
241 // deserialize:
242 // -- TxShaMapItem for building a ShaMap for verification
243 // -- Tx
244 // -- TxMetaData for Tx ordering
245 Serializer const shaMapItemData(
246 reply.transaction(i).data(), reply.transaction(i).size());
247
248 SerialIter txMetaSit(makeSlice(reply.transaction(i)));
249 SerialIter txSit(txMetaSit.getSlice(txMetaSit.getVLDataLength()));
250 SerialIter metaSit(txMetaSit.getSlice(txMetaSit.getVLDataLength()));
251
252 auto tx = std::make_shared<STTx const>(txSit);
253 if (!tx)
254 {
255 JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
256 return false;
257 }
258 auto tid = tx->getTransactionID();
259 STObject meta(metaSit, sfMetadata);
260 orderedTxns.emplace(meta[sfTransactionIndex], std::move(tx));
261
262 if (!txMap.addGiveItem(
264 {
265 JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
266 return false;
267 }
268 }
269 }
270 catch (std::exception const&)
271 {
272 JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
273 return false;
274 }
275
276 if (txMap.getHash().asUInt256() != info.txHash)
277 {
278 JLOG(journal_.debug()) << "Bad message: Transactions verify failed";
279 return false;
280 }
281
282 replayer_.gotReplayDelta(info, std::move(orderedTxns));
283 return true;
284}
285
286} // namespace xrpl
static BaseUInt fromRaw(Container const &c)
Definition base_uint.h:294
static constexpr std::size_t size()
Definition base_uint.h:530
bool processProofPathResponse(std::shared_ptr< protocol::TMProofPathResponse > const &msg)
Process TMProofPathResponse.
protocol::TMProofPathResponse processProofPathRequest(std::shared_ptr< protocol::TMProofPathRequest > const &msg)
Process TMProofPathRequest and return TMProofPathResponse.
LedgerReplayMsgHandler(Application &app, LedgerReplayer &replayer)
bool processReplayDeltaResponse(std::shared_ptr< protocol::TMReplayDeltaResponse > const &msg)
Process TMReplayDeltaResponse.
protocol::TMReplayDeltaResponse processReplayDeltaRequest(std::shared_ptr< protocol::TMReplayDeltaRequest > const &msg)
Process TMReplayDeltaRequest and return TMReplayDeltaResponse.
Manages the lifetime of ledger replay tasks.
uint256 const & asUInt256() const
Definition SHAMapHash.h:24
static SHAMapTreeNodePtr makeFromWire(Slice rawNode)
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition SHAMap.h:77
static bool verifyProofPath(uint256 const &rootHash, uint256 const &key, std::vector< Blob > const &path)
Verify the proof path.
bool addGiveItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:761
SHAMapHash getHash() const
Definition SHAMap.cpp:836
Slice getSlice(std::size_t bytes)
void const * getDataPtr() const
Definition Serializer.h:197
int getLength() const
Definition Serializer.h:207
Slice slice() const noexcept
Definition Serializer.h:44
T emplace(T... args)
T make_shared(T... args)
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:198
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Dest safeDowncast(Src *s) noexcept
Definition safe_cast.h:78
LedgerHeader deserializeHeader(Slice data, bool hasHash=false)
Deserialize a ledger header from a byte array.
uint256 calculateLedgerHash(LedgerHeader const &info)
Calculate the hash of a ledger header.
boost::intrusive_ptr< SHAMapItem > makeShamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:139
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
BaseUInt< 256 > uint256
Definition base_uint.h:562
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215