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