rippled
TransactionAcquire.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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 <ripple/app/ledger/ConsensusTransSetSF.h>
21 #include <ripple/app/ledger/InboundLedgers.h>
22 #include <ripple/app/ledger/InboundTransactions.h>
23 #include <ripple/app/ledger/impl/TransactionAcquire.h>
24 #include <ripple/app/main/Application.h>
25 #include <ripple/app/misc/NetworkOPs.h>
26 #include <ripple/overlay/Overlay.h>
27 
28 #include <memory>
29 
30 namespace ripple {
31 
32 using namespace std::chrono_literals;
33 
34 // Timeout interval in milliseconds
35 auto constexpr TX_ACQUIRE_TIMEOUT = 250ms;
36 
37 enum {
40 };
41 
43  : PeerSet(app, hash, TX_ACQUIRE_TIMEOUT, app.journal("TransactionAcquire"))
44  , mHaveRoot(false)
45 {
46  mMap = std::make_shared<SHAMap>(
48  mMap->setUnbacked();
49 }
50 
51 void
53 {
55  jtTXN_DATA, "TransactionAcquire", [ptr = shared_from_this()](Job&) {
56  ptr->invokeOnTimer();
57  });
58 }
59 
60 void
62 {
63  // We hold a PeerSet lock and so cannot do real work here
64 
65  if (mFailed)
66  {
67  JLOG(m_journal.warn()) << "Failed to acquire TX set " << mHash;
68  }
69  else
70  {
71  JLOG(m_journal.debug()) << "Acquired TX set " << mHash;
72  mMap->setImmutable();
73 
74  uint256 const& hash(mHash);
75  std::shared_ptr<SHAMap> const& map(mMap);
76  auto const pap = &app_;
77  // Note that, when we're in the process of shutting down, addJob()
78  // may reject the request. If that happens then giveSet() will
79  // not be called. That's fine. According to David the giveSet() call
80  // just updates the consensus and related structures when we acquire
81  // a transaction set. No need to update them if we're shutting down.
83  jtTXN_DATA, "completeAcquire", [pap, hash, map](Job&) {
84  pap->getInboundTransactions().giveSet(hash, map, true);
85  });
86  }
87 }
88 
89 void
91 {
92  if (mTimeouts > MAX_TIMEOUTS)
93  {
94  mFailed = true;
95  done();
96  return;
97  }
98 
99  if (mTimeouts >= NORM_TIMEOUTS)
100  trigger(nullptr);
101 
102  addPeers(1);
103 }
104 
107 {
108  return shared_from_this();
109 }
110 
111 void
113 {
114  if (mComplete)
115  {
116  JLOG(m_journal.info()) << "trigger after complete";
117  return;
118  }
119  if (mFailed)
120  {
121  JLOG(m_journal.info()) << "trigger after fail";
122  return;
123  }
124 
125  if (!mHaveRoot)
126  {
127  JLOG(m_journal.trace()) << "TransactionAcquire::trigger "
128  << (peer ? "havePeer" : "noPeer") << " no root";
129  protocol::TMGetLedger tmGL;
130  tmGL.set_ledgerhash(mHash.begin(), mHash.size());
131  tmGL.set_itype(protocol::liTS_CANDIDATE);
132  tmGL.set_querydepth(3); // We probably need the whole thing
133 
134  if (mTimeouts != 0)
135  tmGL.set_querytype(protocol::qtINDIRECT);
136 
137  *(tmGL.add_nodeids()) = SHAMapNodeID().getRawString();
138  sendRequest(tmGL, peer);
139  }
140  else if (!mMap->isValid())
141  {
142  mFailed = true;
143  done();
144  }
145  else
146  {
148  auto nodes = mMap->getMissingNodes(256, &sf);
149 
150  if (nodes.empty())
151  {
152  if (mMap->isValid())
153  mComplete = true;
154  else
155  mFailed = true;
156 
157  done();
158  return;
159  }
160 
161  protocol::TMGetLedger tmGL;
162  tmGL.set_ledgerhash(mHash.begin(), mHash.size());
163  tmGL.set_itype(protocol::liTS_CANDIDATE);
164 
165  if (mTimeouts != 0)
166  tmGL.set_querytype(protocol::qtINDIRECT);
167 
168  for (auto const& node : nodes)
169  {
170  *tmGL.add_nodeids() = node.first.getRawString();
171  }
172  sendRequest(tmGL, peer);
173  }
174 }
175 
178  const std::list<SHAMapNodeID>& nodeIDs,
179  const std::list<Blob>& data,
180  std::shared_ptr<Peer> const& peer)
181 {
182  ScopedLockType sl(mLock);
183 
184  if (mComplete)
185  {
186  JLOG(m_journal.trace()) << "TX set complete";
187  return SHAMapAddNode();
188  }
189 
190  if (mFailed)
191  {
192  JLOG(m_journal.trace()) << "TX set failed";
193  return SHAMapAddNode();
194  }
195 
196  try
197  {
198  if (nodeIDs.empty())
199  return SHAMapAddNode::invalid();
200 
201  std::list<SHAMapNodeID>::const_iterator nodeIDit = nodeIDs.begin();
202  std::list<Blob>::const_iterator nodeDatait = data.begin();
204 
205  while (nodeIDit != nodeIDs.end())
206  {
207  if (nodeIDit->isRoot())
208  {
209  if (mHaveRoot)
210  JLOG(m_journal.debug())
211  << "Got root TXS node, already have it";
212  else if (!mMap->addRootNode(
213  SHAMapHash{mHash},
214  makeSlice(*nodeDatait),
215  nullptr)
216  .isGood())
217  {
218  JLOG(m_journal.warn()) << "TX acquire got bad root node";
219  }
220  else
221  mHaveRoot = true;
222  }
223  else if (!mMap->addKnownNode(*nodeIDit, makeSlice(*nodeDatait), &sf)
224  .isGood())
225  {
226  JLOG(m_journal.warn()) << "TX acquire got bad non-root node";
227  return SHAMapAddNode::invalid();
228  }
229 
230  ++nodeIDit;
231  ++nodeDatait;
232  }
233 
234  trigger(peer);
235  mProgress = true;
236  return SHAMapAddNode::useful();
237  }
238  catch (std::exception const&)
239  {
240  JLOG(m_journal.error()) << "Peer sends us junky transaction node data";
241  return SHAMapAddNode::invalid();
242  }
243 }
244 
245 void
247 {
249  limit, [this](auto peer) { return peer->hasTxSet(mHash); });
250 }
251 
252 void
254 {
255  ScopedLockType sl(mLock);
256 
257  addPeers(numPeers);
258 
259  setTimer();
260 }
261 
262 void
264 {
265  ScopedLockType sl(mLock);
266 
267  if (mTimeouts > NORM_TIMEOUTS)
269 }
270 
271 } // namespace ripple
ripple::TransactionAcquire::mHaveRoot
bool mHaveRoot
Definition: TransactionAcquire.h:56
ripple::Application
Definition: Application.h:101
ripple::SHAMapAddNode
Definition: SHAMapAddNode.h:28
ripple::Application::getNodeFamily
virtual Family & getNodeFamily()=0
ripple::TX_ACQUIRE_TIMEOUT
constexpr auto TX_ACQUIRE_TIMEOUT
Definition: TransactionAcquire.cpp:35
ripple::Application::getTempNodeCache
virtual NodeCache & getTempNodeCache()=0
ripple::makeSlice
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:240
ripple::TransactionAcquire::TransactionAcquire
TransactionAcquire(Application &app, uint256 const &hash)
Definition: TransactionAcquire.cpp:42
std::shared_ptr
STL class.
ripple::PeerSet::mProgress
bool mProgress
Whether forward progress has been made.
Definition: PeerSet.h:117
std::exception
STL class.
std::list
STL class.
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::TransactionAcquire::queueJob
void queueJob() override
Queue a job to call invokeOnTimer().
Definition: TransactionAcquire.cpp:52
ripple::SHAMapType::TRANSACTION
@ TRANSACTION
ripple::TransactionAcquire::trigger
void trigger(std::shared_ptr< Peer > const &)
Definition: TransactionAcquire.cpp:112
ripple::TransactionAcquire::addPeers
void addPeers(std::size_t limit)
Definition: TransactionAcquire.cpp:246
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::PeerSet::mComplete
bool mComplete
Definition: PeerSet.h:114
ripple::JobQueue::addJob
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition: JobQueue.h:166
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::TransactionAcquire::pmDowncast
std::weak_ptr< PeerSet > pmDowncast() override
Return a weak pointer to this.
Definition: TransactionAcquire.cpp:106
ripple::PeerSet::addPeers
void addPeers(std::size_t limit, std::function< bool(std::shared_ptr< Peer > const &)> score)
Add at most limit peers to this set from the overlay.
Definition: PeerSet.cpp:50
ripple::PeerSet::mHash
const uint256 mHash
The hash of the object (in practice, always a ledger) we are trying to fetch.
Definition: PeerSet.h:112
ripple::SHAMapHash
Definition: SHAMapTreeNode.h:47
ripple::base_uint::size
constexpr static std::size_t size()
Definition: base_uint.h:426
ripple::PeerSet::sendRequest
void sendRequest(const protocol::TMGetLedger &message, std::shared_ptr< Peer > const &peer)
Send a GetLedger message to one or all peers.
Definition: PeerSet.cpp:127
ripple::ConsensusTransSetSF
Definition: ConsensusTransSetSF.h:34
ripple::SHAMapAddNode::useful
static SHAMapAddNode useful()
Definition: SHAMapAddNode.h:144
ripple::jtTXN_DATA
@ jtTXN_DATA
Definition: Job.h:55
ripple::TransactionAcquire::mMap
std::shared_ptr< SHAMap > mMap
Definition: TransactionAcquire.h:55
ripple::base_uint< 256 >
std::enable_shared_from_this< TransactionAcquire >::shared_from_this
T shared_from_this(T... args)
ripple::SHAMapAddNode::invalid
static SHAMapAddNode invalid()
Definition: SHAMapAddNode.h:150
ripple::TransactionAcquire::onTimer
void onTimer(bool progress, ScopedLockType &peerSetLock) override
Hook called from invokeOnTimer().
Definition: TransactionAcquire.cpp:90
std::unique_lock< std::recursive_mutex >
ripple::MAX_TIMEOUTS
@ MAX_TIMEOUTS
Definition: TransactionAcquire.cpp:39
ripple::SHAMapNodeID::getRawString
std::string getRawString() const
Definition: SHAMapNodeID.cpp:65
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::Job
Definition: Job.h:82
ripple::TransactionAcquire::init
void init(int startPeers)
Definition: TransactionAcquire.cpp:253
memory
std::weak_ptr
STL class.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::TransactionAcquire::takeNodes
SHAMapAddNode takeNodes(const std::list< SHAMapNodeID > &IDs, const std::list< Blob > &data, std::shared_ptr< Peer > const &)
Definition: TransactionAcquire.cpp:177
ripple::base_uint::begin
iterator begin()
Definition: base_uint.h:124
ripple::PeerSet::app_
Application & app_
Definition: PeerSet.h:105
std::list::begin
T begin(T... args)
ripple::PeerSet
Supports data retrieval by managing a set of peers.
Definition: PeerSet.h:48
ripple::PeerSet::mFailed
bool mFailed
Definition: PeerSet.h:115
ripple::TransactionAcquire::stillNeed
void stillNeed()
Definition: TransactionAcquire.cpp:263
std::list::empty
T empty(T... args)
ripple::PeerSet::m_journal
beast::Journal m_journal
Definition: PeerSet.h:106
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
std::list::end
T end(T... args)
ripple::PeerSet::mTimeouts
int mTimeouts
Definition: PeerSet.h:113
ripple::PeerSet::setTimer
void setTimer()
Schedule a call to queueJob() after mTimerInterval.
Definition: PeerSet.cpp:87
ripple::PeerSet::mLock
std::recursive_mutex mLock
Definition: PeerSet.h:108
ripple::NORM_TIMEOUTS
@ NORM_TIMEOUTS
Definition: TransactionAcquire.cpp:38
ripple::TransactionAcquire::done
void done()
Definition: TransactionAcquire.cpp:61