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