xrpld
Loading...
Searching...
No Matches
STPathSet.cpp
1#include <xrpl/protocol/STPathSet.h>
2
3#include <xrpl/basics/CountedObject.h>
4#include <xrpl/basics/Log.h>
5#include <xrpl/basics/UnorderedContainers.h>
6#include <xrpl/basics/base_uint.h>
7#include <xrpl/basics/contract.h>
8#include <xrpl/beast/hash/uhash.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/json/json_value.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/SField.h>
13#include <xrpl/protocol/STBase.h>
14#include <xrpl/protocol/Serializer.h>
15#include <xrpl/protocol/UintTypes.h>
16#include <xrpl/protocol/detail/STVar.h>
17#include <xrpl/protocol/jss.h>
18
19#include <algorithm>
20#include <cstddef>
21#include <memory>
22#include <stdexcept>
23#include <utility>
24#include <vector>
25
26namespace xrpl {
27
28std::size_t
30{
31 std::size_t hashAccount = 2654435761;
32 std::size_t hashCurrency = 2654435761;
33 std::size_t hashIssuer = 2654435761;
34
35 // NIKB NOTE: This doesn't have to be a secure hash as speed is more
36 // important. We don't even really need to fully hash the whole
37 // base_uint here, as a few bytes would do for our use.
38 //
39 // The note above is only true because the result of this function reaches
40 // nothing but STPathElement::operator==, where it is a fast-reject
41 // prefilter ahead of the field comparisons that decide the answer. Do not
42 // use it to key a container.
43
44 for (auto const x : element.getAccountID())
45 hashAccount += (hashAccount * 257) ^ x;
46
47 // Check pathAsset type instead of element's type_
48 // In some cases type_ might be account but the asset
49 // is still set to either MPT or currency (see Pathfinder::addLink())
50 element.getPathAsset().visit(
51 [&](MPTID const& mpt) { hashCurrency += beast::Uhash<>{}(mpt); },
52 [&](Currency const& currency) {
53 for (auto const x : currency)
54 hashCurrency += (hashCurrency * 509) ^ x;
55 });
56
57 for (auto const x : element.getIssuerID())
58 hashIssuer += (hashIssuer * 911) ^ x;
59
60 return (hashAccount ^ hashCurrency ^ hashIssuer);
61}
62
63// For guidance on deciding which option to pursue:
64// 1. Try to decrease the size of the STPathSet first. For instance, if a std::optional was
65// injected into the type, could you get the same functionality using a std::unique_ptr instead?
66// 2. If the size of the STPathSet is already as small as it can be, then consider what the cost
67// of increasing STVar::kMaxSize would be on all the other STVar types. Each of those types
68// will carry the additional cost of accommodating the larger STPathSet in their SBO.
69// 3. If the cost of increasing STVar::kMaxSize is too high, then heap allocate the STPathSet and
70// remove this static_assert.
71static_assert(
73 "STPathSet is too large to fit in STVar's small object optimization. Please verify if it "
74 "should, if the kMaxSize should be increased, or if STPathSet should be stored on the heap "
75 "instead of in STVar.");
76
80
82 : STBase{other}
83 , CountedObject<STPathSet>{other}
84 , value_{other.value_}
85 , seen_{
86 other.seen_ != nullptr ? std::make_unique<HardenedHashSet<STPath>>(*other.seen_)
87 : nullptr}
88{
89}
90
93{
94 if (this == &other)
95 {
96 return *this;
97 }
98 auto newSeen =
99 other.seen_ != nullptr ? std::make_unique<HardenedHashSet<STPath>>(*other.seen_) : nullptr;
100 STBase::operator=(other);
102 value_ = other.value_;
103 seen_ = std::move(newSeen);
104 return *this;
105}
106
108{
110 for (;;)
111 {
112 int const iType = sit.get8();
113
115 {
116 if (path.empty())
117 {
118 JLOG(debugLog().error()) << "Empty path in pathset";
119 Throw<std::runtime_error>("empty path");
120 }
121
122 // Move rather than converting the vector to an STPath by copy.
123 value_.emplace_back(std::move(path));
124 path.clear();
125
126 if (iType == STPathElement::TypeNone)
127 return;
128 }
129 else if ((iType & ~STPathElement::TypeAll) != 0)
130 {
131 JLOG(debugLog().error()) << "Bad path element " << iType << " in pathset";
132 Throw<std::runtime_error>("bad path element");
133 }
134 else
135 {
136 auto const hasAccount = (iType & STPathElement::TypeAccount) != 0u;
137 auto const hasCurrency = (iType & STPathElement::TypeCurrency) != 0u;
138 auto const hasIssuer = (iType & STPathElement::TypeIssuer) != 0u;
139 auto const hasMPT = (iType & STPathElement::TypeMpt) != 0u;
140
141 AccountID account;
142 PathAsset asset;
143 AccountID issuer;
144
145 if (hasAccount)
146 account = sit.get160();
147
148 if (hasCurrency && hasMPT)
149 {
150 JLOG(debugLog().error()) << "Bad path element MPT and Currency in pathset";
151 Throw<std::runtime_error>("bad path element: MPT and Currency");
152 }
153
154 if (hasCurrency)
155 asset = Currency::fromRaw(sit.get160());
156
157 if (hasMPT)
158 asset = sit.get192();
159
160 if (hasIssuer)
161 issuer = sit.get160();
162
163 path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
164 }
165 }
166}
167
168STBase*
169STPathSet::copy(std::size_t n, void* buf) const
170{
171 return emplace(n, buf, *this);
172}
173
174STBase*
176{
177 return emplace(n, buf, std::move(*this));
178}
179
180bool
182{ // assemble base+tail and add it to the set if it's not a duplicate
183 XRPL_ASSERT(seen_ != nullptr, "xrpl::STPathSet::assembleAdd : DeduplicationTag");
184 STPath combined = base;
185 combined.pushBack(tail);
186 return appendUnique([&](auto& value) { value.push_back(std::move(combined)); });
187}
188
189bool
191{
192 auto const* v = dynamic_cast<STPathSet const*>(&t);
193 return (v != nullptr) && (value_ == v->value_);
194}
195
196bool
198{
199 return value_.empty();
200}
201
202bool
203STPath::hasSeen(AccountID const& account, PathAsset const& asset, AccountID const& issuer) const
204{
205 return std::ranges::any_of(path_, [&](auto& p) {
206 return p.getAccountID() == account && p.getPathAsset() == asset &&
207 p.getIssuerID() == issuer;
208 });
209}
210
213{
215
216 for (auto const& it : path_)
217 {
219 auto const iType = it.getNodeType();
220
221 elem[jss::type] = iType;
222
223 if ((iType & STPathElement::TypeAccount) != 0u)
224 elem[jss::account] = to_string(it.getAccountID());
225
226 XRPL_ASSERT(
227 ((iType & STPathElement::TypeCurrency) == 0u) ||
228 ((iType & STPathElement::TypeMpt) == 0u),
229 "xrpl::STPath::getJson : not type Currency and MPT");
230 if ((iType & STPathElement::TypeCurrency) != 0u)
231 elem[jss::currency] = to_string(it.getCurrency());
232
233 if ((iType & STPathElement::TypeMpt) != 0u)
234 elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
235
236 if ((iType & STPathElement::TypeIssuer) != 0u)
237 elem[jss::issuer] = to_string(it.getIssuerID());
238
239 ret.append(elem);
240 }
241
242 return ret;
243}
244
247{
249 for (auto const& it : value_)
250 ret.append(it.getJson(options));
251
252 return ret;
253}
254
257{
258 return STI_PATHSET;
259}
260
261void
263{
264 XRPL_ASSERT(getFName().isBinary(), "xrpl::STPathSet::add : field is binary");
265 XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
266 bool first = true;
267
268 for (auto const& spPath : value_)
269 {
270 if (!first)
272
273 for (auto const& speElement : spPath)
274 {
275 int const iType = speElement.getNodeType();
276
277 s.add8(iType);
278
279 if ((iType & STPathElement::TypeAccount) != 0u)
280 s.addBitString(speElement.getAccountID());
281
282 if ((iType & STPathElement::TypeMpt) != 0u)
283 s.addBitString(speElement.getMPTID());
284
285 if ((iType & STPathElement::TypeCurrency) != 0u)
286 s.addBitString(speElement.getCurrency());
287
288 if ((iType & STPathElement::TypeIssuer) != 0u)
289 s.addBitString(speElement.getIssuerID());
290 }
291
292 first = false;
293 }
294
296}
297
298} // namespace xrpl
T any_of(T... args)
Represents a JSON value.
Definition json_value.h:117
Value & append(Value const &value)
Append value to array at the end.
static BaseUInt fromRaw(Container const &c)
Definition base_uint.h:302
CountedObject & operator=(CountedObject const &) noexcept=default
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition PathAsset.h:52
Identifies fields.
Definition SField.h:132
A type which can be exported to a well known binary format.
Definition STBase.h:129
SField const & getFName() const
Definition STBase.cpp:120
static STBase * emplace(std::size_t n, void *buf, T &&val)
Definition STBase.h:226
STBase & operator=(STBase const &t)
Definition STBase.cpp:25
static std::size_t getHash(STPathElement const &element)
Definition STPathSet.cpp:29
AccountID const & getAccountID() const
Definition STPathSet.h:483
PathAsset const & getPathAsset() const
Definition STPathSet.h:489
AccountID const & getIssuerID() const
Definition STPathSet.h:507
void add(Serializer &s) const override
STPathSet()=default
bool assembleAdd(STPath const &base, STPathElement const &tail)
assembleAdd adds a path to the set by combining a base path and a tail element.
STBase * copy(std::size_t n, void *buf) const override
json::Value getJson(JsonOptions) const override
std::vector< STPath > value_
Definition STPathSet.h:201
STBase * move(std::size_t n, void *buf) override
bool isEquivalent(STBase const &t) const override
std::unique_ptr< HardenedHashSet< STPath > > seen_
Deduplication index over value_, for pathfinding.
Definition STPathSet.h:210
bool isDefault() const override
SerializedTypeID getSType() const override
STPathSet & operator=(STPathSet const &other)
Definition STPathSet.cpp:92
bool appendUnique(Append &&append)
Append a path via append, then register it in the deduplication index.
Definition STPathSet.h:637
std::vector< STPathElement > path_
Definition STPathSet.h:135
bool hasSeen(AccountID const &account, PathAsset const &asset, AccountID const &issuer) const
void pushBack(STPathElement const &e)
Definition STPathSet.h:538
json::Value getJson(JsonOptions) const
UInt192 get192()
Definition Serializer.h:547
UInt160 get160()
Definition Serializer.h:541
int addBitString(BaseUInt< Bits, Tag > const &v)
Definition Serializer.h:202
int add8(unsigned char byteValue)
static constexpr std::size_t kMaxSize
Definition STVar.h:39
T make_unique(T... args)
@ Array
array value (ordered list)
Definition json_value.h:28
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
STL namespace.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::unordered_set< Value, Hash, Pred, Allocator > HardenedHashSet
beast::Journal debugLog()
Returns a debug journal.
Definition Log.cpp:399
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:42
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
SerializedTypeID
Definition SField.h:94
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:54
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
Note, should be treated as flags that can be | and &.
Definition STBase.h:22