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