rippled
Loading...
Searching...
No Matches
Change.cpp
1#include <xrpld/app/ledger/Ledger.h>
2#include <xrpld/app/main/Application.h>
3#include <xrpld/app/misc/AmendmentTable.h>
4#include <xrpld/app/misc/NetworkOPs.h>
5#include <xrpld/app/tx/detail/Change.h>
6
7#include <xrpl/basics/Log.h>
8#include <xrpl/ledger/Sandbox.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/TxFlags.h>
12
13#include <string_view>
14
15namespace ripple {
16
17template <>
19Transactor::invokePreflight<Change>(PreflightContext const& ctx)
20{
21 // 0 means "Allow any flags"
22 if (auto const ret = preflight0(ctx, 0))
23 return ret;
24
25 auto account = ctx.tx.getAccountID(sfAccount);
26 if (account != beast::zero)
27 {
28 JLOG(ctx.j.warn()) << "Change: Bad source id";
29 return temBAD_SRC_ACCOUNT;
30 }
31
32 // No point in going any further if the transaction fee is malformed.
33 auto const fee = ctx.tx.getFieldAmount(sfFee);
34 if (!fee.native() || fee != beast::zero)
35 {
36 JLOG(ctx.j.warn()) << "Change: invalid fee";
37 return temBAD_FEE;
38 }
39
40 if (!ctx.tx.getSigningPubKey().empty() || !ctx.tx.getSignature().empty() ||
41 ctx.tx.isFieldPresent(sfSigners))
42 {
43 JLOG(ctx.j.warn()) << "Change: Bad signature";
44 return temBAD_SIGNATURE;
45 }
46
47 if (ctx.tx.getFieldU32(sfSequence) != 0 ||
48 ctx.tx.isFieldPresent(sfPreviousTxnID))
49 {
50 JLOG(ctx.j.warn()) << "Change: Bad sequence";
51 return temBAD_SEQUENCE;
52 }
53
54 if (ctx.tx.getTxnType() == ttUNL_MODIFY &&
55 !ctx.rules.enabled(featureNegativeUNL))
56 {
57 JLOG(ctx.j.warn()) << "Change: NegativeUNL not enabled";
58 return temDISABLED;
59 }
60
61 return tesSUCCESS;
62}
63
64TER
66{
67 // If tapOPEN_LEDGER is resurrected into ApplyFlags,
68 // this block can be moved to preflight.
69 if (ctx.view.open())
70 {
71 JLOG(ctx.j.warn()) << "Change transaction against open ledger";
72 return temINVALID;
73 }
74
75 switch (ctx.tx.getTxnType())
76 {
77 case ttFEE:
78 if (ctx.view.rules().enabled(featureXRPFees))
79 {
80 // The ttFEE transaction format defines these fields as
81 // optional, but once the XRPFees feature is enabled, they are
82 // required.
83 if (!ctx.tx.isFieldPresent(sfBaseFeeDrops) ||
84 !ctx.tx.isFieldPresent(sfReserveBaseDrops) ||
85 !ctx.tx.isFieldPresent(sfReserveIncrementDrops))
86 return temMALFORMED;
87 // The ttFEE transaction format defines these fields as
88 // optional, but once the XRPFees feature is enabled, they are
89 // forbidden.
90 if (ctx.tx.isFieldPresent(sfBaseFee) ||
91 ctx.tx.isFieldPresent(sfReferenceFeeUnits) ||
92 ctx.tx.isFieldPresent(sfReserveBase) ||
93 ctx.tx.isFieldPresent(sfReserveIncrement))
94 return temMALFORMED;
95 }
96 else
97 {
98 // The ttFEE transaction format formerly defined these fields
99 // as required. When the XRPFees feature was implemented, they
100 // were changed to be optional. Until the feature has been
101 // enabled, they are required.
102 if (!ctx.tx.isFieldPresent(sfBaseFee) ||
103 !ctx.tx.isFieldPresent(sfReferenceFeeUnits) ||
104 !ctx.tx.isFieldPresent(sfReserveBase) ||
105 !ctx.tx.isFieldPresent(sfReserveIncrement))
106 return temMALFORMED;
107 // The ttFEE transaction format defines these fields as
108 // optional, but without the XRPFees feature, they are
109 // forbidden.
110 if (ctx.tx.isFieldPresent(sfBaseFeeDrops) ||
111 ctx.tx.isFieldPresent(sfReserveBaseDrops) ||
112 ctx.tx.isFieldPresent(sfReserveIncrementDrops))
113 return temDISABLED;
114 }
115 return tesSUCCESS;
116 case ttAMENDMENT:
117 case ttUNL_MODIFY:
118 return tesSUCCESS;
119 default:
120 return temUNKNOWN;
121 }
122}
123
124TER
126{
127 switch (ctx_.tx.getTxnType())
128 {
129 case ttAMENDMENT:
130 return applyAmendment();
131 case ttFEE:
132 return applyFee();
133 case ttUNL_MODIFY:
134 return applyUNLModify();
135 // LCOV_EXCL_START
136 default:
137 UNREACHABLE("ripple::Change::doApply : invalid transaction type");
138 return tefFAILURE;
139 // LCOV_EXCL_STOP
140 }
141}
142
143void
145{
146 XRPL_ASSERT(
147 account_ == beast::zero, "ripple::Change::preCompute : zero account");
148}
149
150void
152{
153 JLOG(j_.warn()) << "fixTrustLinesToSelf amendment activation code starting";
154
155 auto removeTrustLineToSelf = [this](Sandbox& sb, uint256 id) {
156 auto tl = sb.peek(keylet::child(id));
157
158 if (tl == nullptr)
159 {
160 JLOG(j_.warn()) << id << ": Unable to locate trustline";
161 return true;
162 }
163
164 if (tl->getType() != ltRIPPLE_STATE)
165 {
166 JLOG(j_.warn()) << id << ": Unexpected type "
167 << static_cast<std::uint16_t>(tl->getType());
168 return true;
169 }
170
171 auto const& lo = tl->getFieldAmount(sfLowLimit);
172 auto const& hi = tl->getFieldAmount(sfHighLimit);
173
174 if (lo != hi)
175 {
176 JLOG(j_.warn()) << id << ": Trustline doesn't meet requirements";
177 return true;
178 }
179
180 if (auto const page = tl->getFieldU64(sfLowNode); !sb.dirRemove(
181 keylet::ownerDir(lo.getIssuer()), page, tl->key(), false))
182 {
183 JLOG(j_.error()) << id << ": failed to remove low entry from "
184 << toBase58(lo.getIssuer()) << ":" << page
185 << " owner directory";
186 return false;
187 }
188
189 if (auto const page = tl->getFieldU64(sfHighNode); !sb.dirRemove(
190 keylet::ownerDir(hi.getIssuer()), page, tl->key(), false))
191 {
192 JLOG(j_.error()) << id << ": failed to remove high entry from "
193 << toBase58(hi.getIssuer()) << ":" << page
194 << " owner directory";
195 return false;
196 }
197
198 if (tl->getFlags() & lsfLowReserve)
200 sb, sb.peek(keylet::account(lo.getIssuer())), -1, j_);
201
202 if (tl->getFlags() & lsfHighReserve)
204 sb, sb.peek(keylet::account(hi.getIssuer())), -1, j_);
205
206 sb.erase(tl);
207
208 JLOG(j_.warn()) << "Successfully deleted trustline " << id;
209
210 return true;
211 };
212
213 using namespace std::literals;
214
215 Sandbox sb(&view());
216
217 if (removeTrustLineToSelf(
218 sb,
219 uint256{
220 "2F8F21EFCAFD7ACFB07D5BB04F0D2E18587820C7611305BB674A64EAB0FA71E1"sv}) &&
221 removeTrustLineToSelf(
222 sb,
223 uint256{
224 "326035D5C0560A9DA8636545DD5A1B0DFCFF63E68D491B5522B767BB00564B1A"sv}))
225 {
226 JLOG(j_.warn()) << "fixTrustLinesToSelf amendment activation code "
227 "executed successfully";
228 sb.apply(ctx_.rawView());
229 }
230}
231
232TER
234{
235 uint256 amendment(ctx_.tx.getFieldH256(sfAmendment));
236
237 auto const k = keylet::amendments();
238
239 SLE::pointer amendmentObject = view().peek(k);
240
241 if (!amendmentObject)
242 {
243 amendmentObject = std::make_shared<SLE>(k);
244 view().insert(amendmentObject);
245 }
246
247 STVector256 amendments = amendmentObject->getFieldV256(sfAmendments);
248
249 if (std::find(amendments.begin(), amendments.end(), amendment) !=
250 amendments.end())
251 return tefALREADY;
252
253 auto flags = ctx_.tx.getFlags();
254
255 bool const gotMajority = (flags & tfGotMajority) != 0;
256 bool const lostMajority = (flags & tfLostMajority) != 0;
257
258 if (gotMajority && lostMajority)
259 return temINVALID_FLAG;
260
261 STArray newMajorities(sfMajorities);
262
263 bool found = false;
264 if (amendmentObject->isFieldPresent(sfMajorities))
265 {
266 STArray const& oldMajorities =
267 amendmentObject->getFieldArray(sfMajorities);
268 for (auto const& majority : oldMajorities)
269 {
270 if (majority.getFieldH256(sfAmendment) == amendment)
271 {
272 if (gotMajority)
273 return tefALREADY;
274 found = true;
275 }
276 else
277 {
278 // pass through
279 newMajorities.push_back(majority);
280 }
281 }
282 }
283
284 if (!found && lostMajority)
285 return tefALREADY;
286
287 if (gotMajority)
288 {
289 // This amendment now has a majority
290 newMajorities.push_back(STObject::makeInnerObject(sfMajority));
291 auto& entry = newMajorities.back();
292 entry[sfAmendment] = amendment;
293 entry[sfCloseTime] =
295
296 if (!ctx_.app.getAmendmentTable().isSupported(amendment))
297 {
298 JLOG(j_.warn()) << "Unsupported amendment " << amendment
299 << " received a majority.";
300 }
301 }
302 else if (!lostMajority)
303 {
304 // No flags, enable amendment
305 amendments.push_back(amendment);
306 amendmentObject->setFieldV256(sfAmendments, amendments);
307
308 if (amendment == fixTrustLinesToSelf)
310
311 ctx_.app.getAmendmentTable().enable(amendment);
312
313 if (!ctx_.app.getAmendmentTable().isSupported(amendment))
314 {
315 JLOG(j_.error()) << "Unsupported amendment " << amendment
316 << " activated: server blocked.";
318 }
319 }
320
321 if (newMajorities.empty())
322 amendmentObject->makeFieldAbsent(sfMajorities);
323 else
324 amendmentObject->setFieldArray(sfMajorities, newMajorities);
325
326 view().update(amendmentObject);
327
328 return tesSUCCESS;
329}
330
331TER
333{
334 auto const k = keylet::fees();
335
336 SLE::pointer feeObject = view().peek(k);
337
338 if (!feeObject)
339 {
340 feeObject = std::make_shared<SLE>(k);
341 view().insert(feeObject);
342 }
343 auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) {
344 feeObject->at(field) = tx[field];
345 };
346 if (view().rules().enabled(featureXRPFees))
347 {
348 set(feeObject, ctx_.tx, sfBaseFeeDrops);
349 set(feeObject, ctx_.tx, sfReserveBaseDrops);
350 set(feeObject, ctx_.tx, sfReserveIncrementDrops);
351 // Ensure the old fields are removed
352 feeObject->makeFieldAbsent(sfBaseFee);
353 feeObject->makeFieldAbsent(sfReferenceFeeUnits);
354 feeObject->makeFieldAbsent(sfReserveBase);
355 feeObject->makeFieldAbsent(sfReserveIncrement);
356 }
357 else
358 {
359 set(feeObject, ctx_.tx, sfBaseFee);
360 set(feeObject, ctx_.tx, sfReferenceFeeUnits);
361 set(feeObject, ctx_.tx, sfReserveBase);
362 set(feeObject, ctx_.tx, sfReserveIncrement);
363 }
364
365 view().update(feeObject);
366
367 JLOG(j_.warn()) << "Fees have been changed";
368 return tesSUCCESS;
369}
370
371TER
373{
374 if (!isFlagLedger(view().seq()))
375 {
376 JLOG(j_.warn()) << "N-UNL: applyUNLModify, not a flag ledger, seq="
377 << view().seq();
378 return tefFAILURE;
379 }
380
381 if (!ctx_.tx.isFieldPresent(sfUNLModifyDisabling) ||
382 ctx_.tx.getFieldU8(sfUNLModifyDisabling) > 1 ||
383 !ctx_.tx.isFieldPresent(sfLedgerSequence) ||
384 !ctx_.tx.isFieldPresent(sfUNLModifyValidator))
385 {
386 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong Tx format.";
387 return tefFAILURE;
388 }
389
390 bool const disabling = ctx_.tx.getFieldU8(sfUNLModifyDisabling);
391 auto const seq = ctx_.tx.getFieldU32(sfLedgerSequence);
392 if (seq != view().seq())
393 {
394 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong ledger seq=" << seq;
395 return tefFAILURE;
396 }
397
398 Blob const validator = ctx_.tx.getFieldVL(sfUNLModifyValidator);
399 if (!publicKeyType(makeSlice(validator)))
400 {
401 JLOG(j_.warn()) << "N-UNL: applyUNLModify, bad validator key";
402 return tefFAILURE;
403 }
404
405 JLOG(j_.info()) << "N-UNL: applyUNLModify, "
406 << (disabling ? "ToDisable" : "ToReEnable")
407 << " seq=" << seq
408 << " validator data:" << strHex(validator);
409
410 auto const k = keylet::negativeUNL();
411 SLE::pointer negUnlObject = view().peek(k);
412 if (!negUnlObject)
413 {
414 negUnlObject = std::make_shared<SLE>(k);
415 view().insert(negUnlObject);
416 }
417
418 bool const found = [&] {
419 if (negUnlObject->isFieldPresent(sfDisabledValidators))
420 {
421 auto const& negUnl =
422 negUnlObject->getFieldArray(sfDisabledValidators);
423 for (auto const& v : negUnl)
424 {
425 if (v.isFieldPresent(sfPublicKey) &&
426 v.getFieldVL(sfPublicKey) == validator)
427 return true;
428 }
429 }
430 return false;
431 }();
432
433 if (disabling)
434 {
435 // cannot have more than one toDisable
436 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
437 {
438 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToDisable";
439 return tefFAILURE;
440 }
441
442 // cannot be the same as toReEnable
443 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
444 {
445 if (negUnlObject->getFieldVL(sfValidatorToReEnable) == validator)
446 {
447 JLOG(j_.warn())
448 << "N-UNL: applyUNLModify, ToDisable is same as ToReEnable";
449 return tefFAILURE;
450 }
451 }
452
453 // cannot be in negative UNL already
454 if (found)
455 {
456 JLOG(j_.warn())
457 << "N-UNL: applyUNLModify, ToDisable already in negative UNL";
458 return tefFAILURE;
459 }
460
461 negUnlObject->setFieldVL(sfValidatorToDisable, validator);
462 }
463 else
464 {
465 // cannot have more than one toReEnable
466 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
467 {
468 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToReEnable";
469 return tefFAILURE;
470 }
471
472 // cannot be the same as toDisable
473 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
474 {
475 if (negUnlObject->getFieldVL(sfValidatorToDisable) == validator)
476 {
477 JLOG(j_.warn())
478 << "N-UNL: applyUNLModify, ToReEnable is same as ToDisable";
479 return tefFAILURE;
480 }
481 }
482
483 // must be in negative UNL
484 if (!found)
485 {
486 JLOG(j_.warn())
487 << "N-UNL: applyUNLModify, ToReEnable is not in negative UNL";
488 return tefFAILURE;
489 }
490
491 negUnlObject->setFieldVL(sfValidatorToReEnable, validator);
492 }
493
494 view().update(negUnlObject);
495 return tesSUCCESS;
496}
497
498} // namespace ripple
Stream error() const
Definition Journal.h:327
Stream info() const
Definition Journal.h:315
Stream warn() const
Definition Journal.h:321
virtual bool isSupported(uint256 const &amendment) const =0
virtual bool enable(uint256 const &amendment)=0
virtual NetworkOPs & getOPs()=0
virtual AmendmentTable & getAmendmentTable()=0
Application & app
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
void activateTrustLinesToSelfFix()
Definition Change.cpp:151
TER applyUNLModify()
Definition Change.cpp:372
void preCompute() override
Definition Change.cpp:144
static TER preclaim(PreclaimContext const &ctx)
Definition Change.cpp:65
TER applyAmendment()
Definition Change.cpp:233
TER doApply() override
Definition Change.cpp:125
virtual void setAmendmentBlocked()=0
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition ReadView.h:92
virtual bool open() const =0
Returns true if this reflects an open ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:99
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:111
bool empty() const
Definition STArray.h:235
void push_back(STObject const &object)
Definition STArray.h:193
STObject & back()
Definition STArray.h:174
unsigned char getFieldU8(SField const &field) const
Definition STObject.cpp:584
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:644
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:638
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:596
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:652
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:465
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:76
std::uint32_t getFlags() const
Definition STObject.cpp:518
uint256 getFieldH256(SField const &field) const
Definition STObject.cpp:626
static Blob getSignature(STObject const &sigObject)
Definition STTx.cpp:184
Blob getSigningPubKey() const
Definition STTx.h:224
TxType getTxnType() const
Definition STTx.h:218
Discardable, editable view to a ledger.
Definition Sandbox.h:16
void apply(RawView &to)
Definition Sandbox.h:36
AccountID const account_
Definition Transactor.h:128
ApplyView & view()
Definition Transactor.h:144
beast::Journal const j_
Definition Transactor.h:126
ApplyContext & ctx_
Definition Transactor.h:124
void erase(std::shared_ptr< SLE > const &sle) override
Remove a peeked SLE.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T empty(T... args)
T find(T... args)
T is_same_v
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:171
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition Indexes.cpp:211
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:195
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet const & fees() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:203
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:355
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
constexpr std::uint32_t tfGotMajority
Definition TxFlags.h:109
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:1013
@ tefFAILURE
Definition TER.h:147
@ tefALREADY
Definition TER.h:148
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
bool isFlagLedger(LedgerIndex seq)
Returns true if the given ledgerIndex is a flag ledgerIndex.
Definition Ledger.cpp:942
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:225
@ tesSUCCESS
Definition TER.h:226
constexpr std::uint32_t tfLostMajority
Definition TxFlags.h:110
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
@ temBAD_SRC_ACCOUNT
Definition TER.h:87
@ temUNKNOWN
Definition TER.h:105
@ temBAD_FEE
Definition TER.h:73
@ temBAD_SEQUENCE
Definition TER.h:85
@ temMALFORMED
Definition TER.h:68
@ temINVALID
Definition TER.h:91
@ temINVALID_FLAG
Definition TER.h:92
@ temDISABLED
Definition TER.h:95
@ temBAD_SIGNATURE
Definition TER.h:86
uint256 key
Definition Keylet.h:21
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:16
beast::Journal const j
Definition Transactor.h:23
T time_since_epoch(T... args)