xrpld
Loading...
Searching...
No Matches
Permissions.cpp
1#include <xrpl/protocol/Permissions.h>
2
3#include <xrpl/basics/base_uint.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/protocol/Feature.h> // IWYU pragma: keep
7#include <xrpl/protocol/Rules.h>
8#include <xrpl/protocol/SField.h>
9#include <xrpl/protocol/SOTemplate.h>
10#include <xrpl/protocol/STTx.h>
11#include <xrpl/protocol/TxFlags.h> // IWYU pragma: keep
12#include <xrpl/protocol/TxFormats.h>
13#include <xrpl/protocol/TxSettings.h>
14
15#include <algorithm>
16#include <cstdint>
17#include <format>
18#include <functional>
19#include <limits>
20#include <optional>
21#include <stdexcept>
22#include <string>
23#include <tuple>
24#include <unordered_set>
25#include <utility>
26#include <vector>
27
28namespace xrpl {
29
41
43{
44 {
45#pragma push_macro("UNWRAP")
46#undef UNWRAP
47#pragma push_macro("TRANSACTION")
48#undef TRANSACTION
49
50#define UNWRAP(...) __VA_ARGS__
51#define TRANSACTION(tag, value, name, settings, ...) \
52 { \
53 TxSettings const s = UNWRAP settings; \
54 txDelegationMap_[static_cast<TxType>(value)] = {s.amendment, s.delegable}; \
55 }
56
57#include <xrpl/protocol/detail/transactions.macro>
58
59#undef TRANSACTION
60#pragma pop_macro("TRANSACTION")
61#undef UNWRAP
62#pragma pop_macro("UNWRAP")
63 }
64
66#pragma push_macro("GRANULAR_PERMISSION")
67#undef GRANULAR_PERMISSION
68
69#define GRANULAR_PERMISSION(type, ...) {#type, type},
70
71#include <xrpl/protocol/detail/permissions.macro>
72
73#undef GRANULAR_PERMISSION
74#pragma pop_macro("GRANULAR_PERMISSION")
75 };
76
77 {
78#pragma push_macro("GRANULAR_PERMISSION")
79#undef GRANULAR_PERMISSION
80
81// NOLINTBEGIN(bugprone-macro-parentheses)
82#define GRANULAR_PERMISSION(type, txType, value, flags, fields) \
83 granularPermissions_.emplace( \
84 std::piecewise_construct, \
85 std::forward_as_tuple(GranularPermissionType::type), \
86 std::forward_as_tuple( \
87 #type, txType, static_cast<std::uint32_t>(flags), std::vector<SOElement> fields));
88 // NOLINTEND(bugprone-macro-parentheses)
89
90#include <xrpl/protocol/detail/permissions.macro>
91
92#undef GRANULAR_PERMISSION
93#pragma pop_macro("GRANULAR_PERMISSION")
94 }
95
97 {
98 // LCOV_EXCL_START
100 "granularPermissionsByName_ and granularPermissions_ must have same size");
101 // LCOV_EXCL_STOP
102 }
103
104 for (auto const& [name, type] : granularPermissionsByName_)
105 {
106 if (type <= UINT16_MAX)
107 {
108 // LCOV_EXCL_START
110 "Granular permission value must exceed the maximum uint16_t value: {}", name));
111 // LCOV_EXCL_STOP
112 }
113 }
114
115 for (auto const& [type, entry] : granularPermissions_)
116 granularTxTypes_.insert(entry.txType);
117
118 // Validate that all fields listed in permissions.macro exist in the
119 // corresponding transaction type's format, catching typos at startup.
120 for (auto const& [type, entry] : granularPermissions_)
121 {
122 if (!txDelegationMap_.contains(entry.txType))
123 {
124 // LCOV_EXCL_START
125 Throw<std::logic_error>("Invalid granular permission txType in txDelegationMap_");
126 // LCOV_EXCL_STOP
127 }
128
129 auto const* fmt = TxFormats::getInstance().findByType(entry.txType);
130 if (fmt == nullptr)
131 {
132 // LCOV_EXCL_START
133 Throw<std::logic_error>("Invalid granular permission txType");
134 // LCOV_EXCL_STOP
135 }
136
137 for (auto const& field : entry.permittedFields)
138 {
139 if (fmt->getSOTemplate().getIndex(field.sField()) == -1)
140 {
141 // LCOV_EXCL_START
142 Throw<std::logic_error>("Invalid granular permission field");
143 // LCOV_EXCL_STOP
144 }
145 }
146 }
147}
148
149Permission const&
151{
152 static Permission const kInstance;
153 return kInstance;
154}
155
158{
159 if (value == 0)
160 return std::nullopt;
161
162 auto const permissionValue = static_cast<GranularPermissionType>(value);
163 if (auto const granular = getGranularName(permissionValue))
164 return granular;
165
166 // not a granular permission, check if it maps to a transaction type
167 if (auto const txType = permissionToTxType(value))
168 {
169 if (auto const* item = TxFormats::getInstance().findByType(*txType); item != nullptr)
170 return item->getName();
171 }
172
173 return std::nullopt;
174}
175
178{
179 auto const it = granularPermissionsByName_.find(name);
180 if (it != granularPermissionsByName_.end())
181 return static_cast<uint32_t>(it->second);
182
183 return std::nullopt;
184}
185
188{
189 auto const it = granularPermissions_.find(value);
190 if (it != granularPermissions_.end())
191 return it->second.name;
192
193 return std::nullopt;
194}
195
198{
199 auto const it = granularPermissions_.find(gpType);
200 if (it != granularPermissions_.end())
201 return it->second.txType;
202
203 return std::nullopt;
204}
205
206bool
208{
209 return granularTxTypes_.contains(txType);
210}
211
214{
215 auto const it = txDelegationMap_.find(txType);
216 XRPL_ASSERT(
217 it != txDelegationMap_.end(),
218 "xrpl::Permission::getTxFeature : tx exists in txDelegationMap_");
219
220 if (it->second.amendment == UInt256{})
221 return std::nullopt;
222
223 return std::optional{std::cref(it->second.amendment)};
224}
225
226bool
227Permission::isDelegable(std::uint32_t permissionValue, Rules const& rules) const
228{
229 if (permissionValue == 0)
230 return false; // LCOV_EXCL_LINE
231
232 auto const amendmentEnabled = [&rules](TxDelegationEntry const& entry) {
233 return entry.amendment == UInt256{} || rules.enabled(entry.amendment);
234 };
235
236 // Granular permissions may authorize a limited subset of a tx type even
237 // when the full tx type is not delegable. They still require the
238 // underlying transaction amendment to be enabled.
239 if (auto const granularIt =
240 granularPermissions_.find(static_cast<GranularPermissionType>(permissionValue));
241 granularIt != granularPermissions_.end())
242 {
243 auto const txIt = txDelegationMap_.find(granularIt->second.txType);
244 return txIt != txDelegationMap_.end() && amendmentEnabled(txIt->second);
245 }
246
247 auto const txType = permissionToTxType(permissionValue);
248 if (!txType)
249 return false;
250
251 auto const txIt = txDelegationMap_.find(*txType);
252
253 // Tx-level permissions require the transaction type itself to be delegable, and
254 // the corresponding amendment enabled.
255 return txIt != txDelegationMap_.end() && txIt->second.delegable != Delegation::NotDelegable &&
256 amendmentEnabled(txIt->second);
257}
258
259uint32_t
261{
262 return static_cast<uint32_t>(type) + 1;
263}
264
267{
268 // Values outside this range [1, 65536] would silently truncate when cast to
269 // uint16_t, for example, 65537 would become 1, mapping to the Payment transaction.
270 if (value == 0 || value > std::numeric_limits<std::uint16_t>::max() + 1u)
271 return std::nullopt;
272
273 return static_cast<TxType>(value - 1);
274}
275
276bool
278 STTx const& tx,
279 std::unordered_set<GranularPermissionType> const& heldPermissions) const
280{
281 // Build union of flags upfront to enable an early exit. Fields are not stored and
282 // grouped in advance to avoid heap allocation.
283 std::uint32_t unionFlags = 0;
284 for (auto const& gp : heldPermissions)
285 {
286 auto const it = granularPermissions_.find(gp);
287 if (it != granularPermissions_.end())
288 unionFlags |= it->second.permittedFlags;
289 }
290
291 // Check if flags are permitted
292 if ((tx.getFlags() & ~unionFlags) != 0)
293 return false;
294
295 // Check if fields are permitted. Every present field must appear in at least one held
296 // permission's template. The common fields are included in the constructor.
297 for (auto const& field : tx)
298 {
299 if (field.getSType() == STI_NOTPRESENT)
300 continue;
301
302 if (!std::ranges::any_of(heldPermissions, [&](auto const& gp) {
303 auto const it = granularPermissions_.find(gp);
304 return it != granularPermissions_.end() &&
305 it->second.permittedFields.getIndex(field.getFName()) != -1;
306 }))
307 return false;
308 }
309
310 return true;
311}
312
313} // namespace xrpl
T any_of(T... args)
Item const * findByType(KeyType type) const
Retrieve a format based on its type.
std::unordered_map< std::string, GranularPermissionType > granularPermissionsByName_
Definition Permissions.h:69
static uint32_t txToPermissionType(TxType type)
std::optional< std::uint32_t > getGranularValue(std::string const &name) const
std::optional< std::reference_wrapper< UInt256 const > > getTxFeature(TxType txType) const
std::unordered_map< TxType, TxDelegationEntry > txDelegationMap_
Definition Permissions.h:68
std::optional< TxType > getGranularTxType(GranularPermissionType gpType) const
bool hasGranularPermissions(TxType txType) const
std::optional< std::string > getGranularName(GranularPermissionType value) const
std::unordered_map< GranularPermissionType, GranularPermissionEntry > granularPermissions_
Definition Permissions.h:70
std::unordered_set< TxType > granularTxTypes_
Definition Permissions.h:67
bool checkGranularSandbox(STTx const &tx, std::unordered_set< GranularPermissionType > const &heldPermissions) const
Verifies a delegated transaction against its granular permission template.
static std::optional< TxType > permissionToTxType(std::uint32_t value)
bool isDelegable(std::uint32_t permissionValue, Rules const &rules) const
std::optional< std::string > getPermissionName(std::uint32_t value) const
static Permission const & getInstance()
Rules controlling protocol behavior.
Definition Rules.h:40
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:182
std::uint32_t getFlags() const
Definition STObject.cpp:517
Manages the list of known transaction formats.
Definition TxFormats.h:83
static TxFormats const & getInstance()
Definition TxFormats.cpp:60
T format(T... args)
T max(T... args)
STL namespace.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TxType
Transaction type identifiers.
Definition TxFormats.h:45
GranularPermissionType
We have both transaction type permissions and granular type permissions.
Definition Permissions.h:30
BaseUInt< 256 > UInt256
Definition base_uint.h:580
static std::string fmt(BigInt const &value)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
T cref(T... args)
GranularPermissionEntry(std::string name, TxType txType, std::uint32_t permittedFlags, std::vector< SOElement > fields)