rippled
Loading...
Searching...
No Matches
Credentials.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 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 <xrpld/app/tx/detail/Credentials.h>
21
22#include <xrpl/basics/Log.h>
23#include <xrpl/ledger/ApplyView.h>
24#include <xrpl/ledger/CredentialHelpers.h>
25#include <xrpl/ledger/View.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/TxFlags.h>
29
30#include <chrono>
31
32namespace ripple {
33
34/*
35 Credentials
36 ======
37
38 A verifiable credentials (VC
39 https://en.wikipedia.org/wiki/Verifiable_credentials), as defined by the W3C
40 specification (https://www.w3.org/TR/vc-data-model-2.0/), is a
41 secure and tamper-evident way to represent information about a subject, such
42 as an individual, organization, or even an IoT device. These credentials are
43 issued by a trusted entity and can be verified by third parties without
44 directly involving the issuer at all.
45*/
46
47using namespace credentials;
48
49// ------- CREATE --------------------------
50
53{
54 // 0 means "Allow any flags"
55 return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
56}
57
60{
61 auto const& tx = ctx.tx;
62 auto& j = ctx.j;
63
64 if (!tx[sfSubject])
65 {
66 JLOG(j.trace()) << "Malformed transaction: Invalid Subject";
67 return temMALFORMED;
68 }
69
70 auto const uri = tx[~sfURI];
71 if (uri && (uri->empty() || (uri->size() > maxCredentialURILength)))
72 {
73 JLOG(j.trace()) << "Malformed transaction: invalid size of URI.";
74 return temMALFORMED;
75 }
76
77 auto const credType = tx[sfCredentialType];
78 if (credType.empty() || (credType.size() > maxCredentialTypeLength))
79 {
80 JLOG(j.trace())
81 << "Malformed transaction: invalid size of CredentialType.";
82 return temMALFORMED;
83 }
84
85 return tesSUCCESS;
86}
87
88TER
90{
91 auto const credType(ctx.tx[sfCredentialType]);
92 auto const subject = ctx.tx[sfSubject];
93
94 if (!ctx.view.exists(keylet::account(subject)))
95 {
96 JLOG(ctx.j.trace()) << "Subject doesn't exist.";
97 return tecNO_TARGET;
98 }
99
100 if (ctx.view.exists(
101 keylet::credential(subject, ctx.tx[sfAccount], credType)))
102 {
103 JLOG(ctx.j.trace()) << "Credential already exists.";
104 return tecDUPLICATE;
105 }
106
107 return tesSUCCESS;
108}
109
110TER
112{
113 auto const subject = ctx_.tx[sfSubject];
114 auto const credType(ctx_.tx[sfCredentialType]);
115 Keylet const credentialKey =
116 keylet::credential(subject, account_, credType);
117
118 auto const sleCred = std::make_shared<SLE>(credentialKey);
119 if (!sleCred)
120 return tefINTERNAL; // LCOV_EXCL_LINE
121
122 auto const optExp = ctx_.tx[~sfExpiration];
123 if (optExp)
124 {
125 std::uint32_t const closeTime =
127
128 if (closeTime > *optExp)
129 {
130 JLOG(j_.trace()) << "Malformed transaction: "
131 "Expiration time is in the past.";
132 return tecEXPIRED;
133 }
134
135 sleCred->setFieldU32(sfExpiration, ctx_.tx.getFieldU32(sfExpiration));
136 }
137
138 auto const sleIssuer = view().peek(keylet::account(account_));
139 if (!sleIssuer)
140 return tefINTERNAL; // LCOV_EXCL_LINE
141
142 {
143 STAmount const reserve{view().fees().accountReserve(
144 sleIssuer->getFieldU32(sfOwnerCount) + 1)};
145 if (mPriorBalance < reserve)
147 }
148
149 sleCred->setAccountID(sfSubject, subject);
150 sleCred->setAccountID(sfIssuer, account_);
151 sleCred->setFieldVL(sfCredentialType, credType);
152
153 if (ctx_.tx.isFieldPresent(sfURI))
154 sleCred->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI));
155
156 {
157 auto const page = view().dirInsert(
159 credentialKey,
161 JLOG(j_.trace()) << "Adding Credential to owner directory "
162 << to_string(credentialKey.key) << ": "
163 << (page ? "success" : "failure");
164 if (!page)
165 return tecDIR_FULL;
166 sleCred->setFieldU64(sfIssuerNode, *page);
167
168 adjustOwnerCount(view(), sleIssuer, 1, j_);
169 }
170
171 if (subject == account_)
172 {
173 sleCred->setFieldU32(sfFlags, lsfAccepted);
174 }
175 else
176 {
177 auto const page = view().dirInsert(
178 keylet::ownerDir(subject),
179 credentialKey,
180 describeOwnerDir(subject));
181 JLOG(j_.trace()) << "Adding Credential to owner directory "
182 << to_string(credentialKey.key) << ": "
183 << (page ? "success" : "failure");
184 if (!page)
185 return tecDIR_FULL;
186 sleCred->setFieldU64(sfSubjectNode, *page);
187 view().update(view().peek(keylet::account(subject)));
188 }
189
190 view().insert(sleCred);
191
192 return tesSUCCESS;
193}
194
195// ------- DELETE --------------------------
196
199{
200 // 0 means "Allow any flags"
201 return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
202}
203
204NotTEC
206{
207 auto const subject = ctx.tx[~sfSubject];
208 auto const issuer = ctx.tx[~sfIssuer];
209
210 if (!subject && !issuer)
211 {
212 // Neither field is present, the transaction is malformed.
213 JLOG(ctx.j.trace()) << "Malformed transaction: "
214 "No Subject or Issuer fields.";
215 return temMALFORMED;
216 }
217
218 // Make sure that the passed account is valid.
219 if ((subject && subject->isZero()) || (issuer && issuer->isZero()))
220 {
221 JLOG(ctx.j.trace()) << "Malformed transaction: Subject or Issuer "
222 "field zeroed.";
224 }
225
226 auto const credType = ctx.tx[sfCredentialType];
227 if (credType.empty() || (credType.size() > maxCredentialTypeLength))
228 {
229 JLOG(ctx.j.trace())
230 << "Malformed transaction: invalid size of CredentialType.";
231 return temMALFORMED;
232 }
233
234 return tesSUCCESS;
235}
236
237TER
239{
240 AccountID const account{ctx.tx[sfAccount]};
241 auto const subject = ctx.tx[~sfSubject].value_or(account);
242 auto const issuer = ctx.tx[~sfIssuer].value_or(account);
243 auto const credType(ctx.tx[sfCredentialType]);
244
245 if (!ctx.view.exists(keylet::credential(subject, issuer, credType)))
246 return tecNO_ENTRY;
247
248 return tesSUCCESS;
249}
250
251TER
253{
254 auto const subject = ctx_.tx[~sfSubject].value_or(account_);
255 auto const issuer = ctx_.tx[~sfIssuer].value_or(account_);
256
257 auto const credType(ctx_.tx[sfCredentialType]);
258 auto const sleCred =
259 view().peek(keylet::credential(subject, issuer, credType));
260 if (!sleCred)
261 return tefINTERNAL; // LCOV_EXCL_LINE
262
263 if ((subject != account_) && (issuer != account_) &&
265 {
266 JLOG(j_.trace()) << "Can't delete non-expired credential.";
267 return tecNO_PERMISSION;
268 }
269
270 return deleteSLE(view(), sleCred, j_);
271}
272
273// ------- APPLY --------------------------
274
277{
278 // 0 means "Allow any flags"
279 return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
280}
281
282NotTEC
284{
285 if (!ctx.tx[sfIssuer])
286 {
287 JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed.";
289 }
290
291 auto const credType = ctx.tx[sfCredentialType];
292 if (credType.empty() || (credType.size() > maxCredentialTypeLength))
293 {
294 JLOG(ctx.j.trace())
295 << "Malformed transaction: invalid size of CredentialType.";
296 return temMALFORMED;
297 }
298
299 return tesSUCCESS;
300}
301
302TER
304{
305 AccountID const subject = ctx.tx[sfAccount];
306 AccountID const issuer = ctx.tx[sfIssuer];
307 auto const credType(ctx.tx[sfCredentialType]);
308
309 if (!ctx.view.exists(keylet::account(issuer)))
310 {
311 JLOG(ctx.j.warn()) << "No issuer: " << to_string(issuer);
312 return tecNO_ISSUER;
313 }
314
315 auto const sleCred =
316 ctx.view.read(keylet::credential(subject, issuer, credType));
317 if (!sleCred)
318 {
319 JLOG(ctx.j.warn()) << "No credential: " << to_string(subject) << ", "
320 << to_string(issuer) << ", " << credType;
321 return tecNO_ENTRY;
322 }
323
324 if (sleCred->getFieldU32(sfFlags) & lsfAccepted)
325 {
326 JLOG(ctx.j.warn()) << "Credential already accepted: "
327 << to_string(subject) << ", " << to_string(issuer)
328 << ", " << credType;
329 return tecDUPLICATE;
330 }
331
332 return tesSUCCESS;
333}
334
335TER
337{
338 AccountID const issuer{ctx_.tx[sfIssuer]};
339
340 // Both exist as credential object exist itself (checked in preclaim)
341 auto const sleSubject = view().peek(keylet::account(account_));
342 auto const sleIssuer = view().peek(keylet::account(issuer));
343
344 if (!sleSubject || !sleIssuer)
345 return tefINTERNAL; // LCOV_EXCL_LINE
346
347 {
348 STAmount const reserve{view().fees().accountReserve(
349 sleSubject->getFieldU32(sfOwnerCount) + 1)};
350 if (mPriorBalance < reserve)
352 }
353
354 auto const credType(ctx_.tx[sfCredentialType]);
355 Keylet const credentialKey = keylet::credential(account_, issuer, credType);
356 auto const sleCred = view().peek(credentialKey); // Checked in preclaim()
357
358 if (checkExpired(sleCred, view().info().parentCloseTime))
359 {
360 JLOG(j_.trace()) << "Credential is expired: " << sleCred->getText();
361 // delete expired credentials even if the transaction failed
362 auto const err = credentials::deleteSLE(view(), sleCred, j_);
363 return isTesSuccess(err) ? tecEXPIRED : err;
364 }
365
366 sleCred->setFieldU32(sfFlags, lsfAccepted);
367 view().update(sleCred);
368
369 adjustOwnerCount(view(), sleIssuer, -1, j_);
370 adjustOwnerCount(view(), sleSubject, 1, j_);
371
372 return tesSUCCESS;
373}
374
375} // namespace ripple
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
ApplyView & view()
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:319
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:663
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:615
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
AccountID const account_
Definition Transactor.h:147
ApplyView & view()
Definition Transactor.h:163
beast::Journal const j_
Definition Transactor.h:145
XRPAmount mPriorBalance
Definition Transactor.h:148
ApplyContext & ctx_
Definition Transactor.h:143
T is_same_v
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
bool checkExpired(std::shared_ptr< SLE const > const &sleCredential, NetClock::time_point const &closed)
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:553
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::size_t constexpr maxCredentialURILength
The maximum length of a URI inside a Credential.
Definition Protocol.h:103
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1032
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1050
@ tefINTERNAL
Definition TER.h:173
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition Protocol.h:106
@ tecNO_ENTRY
Definition TER.h:306
@ tecNO_ISSUER
Definition TER.h:299
@ tecNO_TARGET
Definition TER.h:304
@ tecDIR_FULL
Definition TER.h:287
@ tecDUPLICATE
Definition TER.h:315
@ tecNO_PERMISSION
Definition TER.h:305
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecEXPIRED
Definition TER.h:314
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
@ temMALFORMED
Definition TER.h:87
@ temINVALID_ACCOUNT_ID
Definition TER.h:119
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
uint256 key
Definition Keylet.h:40
NetClock::time_point parentCloseTime
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35
beast::Journal const j
Definition Transactor.h:42
T time_since_epoch(T... args)