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