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