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