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