xrpld
Loading...
Searching...
No Matches
Change.cpp
1#include <xrpl/tx/transactors/system/Change.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Slice.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/basics/strHex.h>
7#include <xrpl/beast/utility/Zero.h>
8#include <xrpl/beast/utility/instrumentation.h>
9#include <xrpl/core/ServiceRegistry.h>
10#include <xrpl/ledger/AmendmentTable.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/Protocol.h>
14#include <xrpl/protocol/PublicKey.h>
15#include <xrpl/protocol/SField.h>
16#include <xrpl/protocol/STArray.h>
17#include <xrpl/protocol/STLedgerEntry.h>
18#include <xrpl/protocol/STTx.h>
19#include <xrpl/protocol/STVector256.h>
20#include <xrpl/protocol/TER.h>
21#include <xrpl/protocol/TxFlags.h>
22#include <xrpl/protocol/TxFormats.h>
23#include <xrpl/protocol/XRPAmount.h>
24#include <xrpl/server/NetworkOPs.h>
25#include <xrpl/tx/Transactor.h>
26
27#include <algorithm>
28#include <memory>
29
30namespace xrpl {
31
32template <>
35{
36 // 0 means "Allow any flags"
37 // The check for tfEnableAmendmentMask is gated by LendingProtocol because
38 // that feature introduced this parameter, and it's not worth adding another
39 // amendment just for this.
40 if (auto const ret =
41 preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfEnableAmendmentMask : 0))
42 return ret;
43
44 auto account = ctx.tx.getAccountID(sfAccount);
45 if (account != beast::kZero)
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::kZero)
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 || ctx.tx.isFieldPresent(sfPreviousTxnID))
67 {
68 JLOG(ctx.j.warn()) << "Change: Bad sequence";
69 return temBAD_SEQUENCE;
70 }
71
72 return tesSUCCESS;
73}
74
75TER
77{
78 // If tapOPEN_LEDGER is resurrected into ApplyFlags,
79 // this block can be moved to preflight.
80 if (ctx.view.open())
81 {
82 JLOG(ctx.j.warn()) << "Change transaction against open ledger";
83 return temINVALID;
84 }
85
86 switch (ctx.tx.getTxnType())
87 {
88 case ttFEE:
89 if (ctx.view.rules().enabled(featureXRPFees))
90 {
91 // The ttFEE transaction format defines these fields as
92 // optional, but once the XRPFees feature is enabled, they are
93 // required.
94 if (!ctx.tx.isFieldPresent(sfBaseFeeDrops) ||
95 !ctx.tx.isFieldPresent(sfReserveBaseDrops) ||
96 !ctx.tx.isFieldPresent(sfReserveIncrementDrops))
97 return temMALFORMED;
98 // The ttFEE transaction format defines these fields as
99 // optional, but once the XRPFees feature is enabled, they are
100 // forbidden.
101 if (ctx.tx.isFieldPresent(sfBaseFee) ||
102 ctx.tx.isFieldPresent(sfReferenceFeeUnits) ||
103 ctx.tx.isFieldPresent(sfReserveBase) ||
104 ctx.tx.isFieldPresent(sfReserveIncrement))
105 return temMALFORMED;
106 }
107 else
108 {
109 // The ttFEE transaction format formerly defined these fields
110 // as required. When the XRPFees feature was implemented, they
111 // were changed to be optional. Until the feature has been
112 // enabled, they are required.
113 if (!ctx.tx.isFieldPresent(sfBaseFee) ||
114 !ctx.tx.isFieldPresent(sfReferenceFeeUnits) ||
115 !ctx.tx.isFieldPresent(sfReserveBase) ||
116 !ctx.tx.isFieldPresent(sfReserveIncrement))
117 return temMALFORMED;
118 // The ttFEE transaction format defines these fields as
119 // optional, but without the XRPFees feature, they are
120 // forbidden.
121 if (ctx.tx.isFieldPresent(sfBaseFeeDrops) ||
122 ctx.tx.isFieldPresent(sfReserveBaseDrops) ||
123 ctx.tx.isFieldPresent(sfReserveIncrementDrops))
124 return temDISABLED;
125 }
126 return tesSUCCESS;
127 case ttAMENDMENT:
128 case ttUNL_MODIFY:
129 return tesSUCCESS;
130 default:
131 return temUNKNOWN;
132 }
133}
134
135TER
137{
138 switch (ctx_.tx.getTxnType())
139 {
140 case ttAMENDMENT:
141 return applyAmendment();
142 case ttFEE:
143 return applyFee();
144 case ttUNL_MODIFY:
145 return applyUNLModify();
146 // LCOV_EXCL_START
147 default:
148 UNREACHABLE("xrpl::Change::doApply : invalid transaction type");
149 return tefFAILURE;
150 // LCOV_EXCL_STOP
151 }
152}
153
154void
156{
157 XRPL_ASSERT(accountID_ == beast::kZero, "xrpl::Change::preCompute : zero account");
158}
159
160TER
162{
163 uint256 const amendment(ctx_.tx.getFieldH256(sfAmendment));
164
165 auto const k = keylet::amendments();
166
167 SLE::pointer amendmentObject = view().peek(k);
168
169 if (!amendmentObject)
170 {
171 amendmentObject = std::make_shared<SLE>(k);
172 view().insert(amendmentObject);
173 }
174
175 STVector256 amendments = amendmentObject->getFieldV256(sfAmendments);
176
177 if (std::ranges::find(amendments, amendment) != amendments.end())
178 return tefALREADY;
179
180 bool const gotMajority = ctx_.tx.isFlag(tfGotMajority);
181 bool const lostMajority = ctx_.tx.isFlag(tfLostMajority);
182
183 if (gotMajority && lostMajority)
184 return temINVALID_FLAG;
185
186 STArray newMajorities(sfMajorities);
187
188 bool found = false;
189 if (amendmentObject->isFieldPresent(sfMajorities))
190 {
191 STArray const& oldMajorities = amendmentObject->getFieldArray(sfMajorities);
192 for (auto const& majority : oldMajorities)
193 {
194 if (majority.getFieldH256(sfAmendment) == amendment)
195 {
196 if (gotMajority)
197 return tefALREADY;
198 found = true;
199 }
200 else
201 {
202 // pass through
203 newMajorities.pushBack(majority);
204 }
205 }
206 }
207
208 if (!found && lostMajority)
209 return tefALREADY;
210
211 if (gotMajority)
212 {
213 // This amendment now has a majority
214 newMajorities.pushBack(STObject::makeInnerObject(sfMajority));
215 auto& entry = newMajorities.back();
216 entry[sfAmendment] = amendment;
217 entry[sfCloseTime] = view().parentCloseTime().time_since_epoch().count();
218
219 if (!ctx_.registry.get().getAmendmentTable().isSupported(amendment))
220 {
221 JLOG(j_.warn()) << "Unsupported amendment " << amendment << " received a majority.";
222 }
223 }
224 else if (!lostMajority)
225 {
226 // No flags, enable amendment
227 amendments.pushBack(amendment);
228 amendmentObject->setFieldV256(sfAmendments, amendments);
229
230 ctx_.registry.get().getAmendmentTable().enable(amendment);
231
232 if (!ctx_.registry.get().getAmendmentTable().isSupported(amendment))
233 {
234 JLOG(j_.error()) << "Unsupported amendment " << amendment
235 << " activated: server blocked.";
236 ctx_.registry.get().getOPs().setAmendmentBlocked();
237 }
238 }
239
240 if (newMajorities.empty())
241 {
242 amendmentObject->makeFieldAbsent(sfMajorities);
243 }
244 else
245 {
246 amendmentObject->setFieldArray(sfMajorities, newMajorities);
247 }
248
249 view().update(amendmentObject);
250
251 return tesSUCCESS;
252}
253
254TER
256{
257 auto const k = keylet::feeSettings();
258
259 SLE::pointer feeObject = view().peek(k);
260
261 if (!feeObject)
262 {
263 feeObject = std::make_shared<SLE>(k);
264 view().insert(feeObject);
265 }
266 auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) {
267 feeObject->at(field) = tx[field];
268 };
269 if (view().rules().enabled(featureXRPFees))
270 {
271 set(feeObject, ctx_.tx, sfBaseFeeDrops);
272 set(feeObject, ctx_.tx, sfReserveBaseDrops);
273 set(feeObject, ctx_.tx, sfReserveIncrementDrops);
274 // Ensure the old fields are removed
275 feeObject->makeFieldAbsent(sfBaseFee);
276 feeObject->makeFieldAbsent(sfReferenceFeeUnits);
277 feeObject->makeFieldAbsent(sfReserveBase);
278 feeObject->makeFieldAbsent(sfReserveIncrement);
279 }
280 else
281 {
282 set(feeObject, ctx_.tx, sfBaseFee);
283 set(feeObject, ctx_.tx, sfReferenceFeeUnits);
284 set(feeObject, ctx_.tx, sfReserveBase);
285 set(feeObject, ctx_.tx, sfReserveIncrement);
286 }
287
288 view().update(feeObject);
289
290 JLOG(j_.warn()) << "Fees have been changed";
291 return tesSUCCESS;
292}
293
294TER
296{
297 if (!isFlagLedger(view().seq()))
298 {
299 JLOG(j_.warn()) << "N-UNL: applyUNLModify, not a flag ledger, seq=" << view().seq();
300 return tefFAILURE;
301 }
302
303 if (!ctx_.tx.isFieldPresent(sfUNLModifyDisabling) ||
304 ctx_.tx.getFieldU8(sfUNLModifyDisabling) > 1 || !ctx_.tx.isFieldPresent(sfLedgerSequence) ||
305 !ctx_.tx.isFieldPresent(sfUNLModifyValidator))
306 {
307 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong Tx format.";
308 return tefFAILURE;
309 }
310
311 bool const disabling = ctx_.tx.getFieldU8(sfUNLModifyDisabling) != 0u;
312 auto const seq = ctx_.tx.getFieldU32(sfLedgerSequence);
313 if (seq != view().seq())
314 {
315 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong ledger seq=" << seq;
316 return tefFAILURE;
317 }
318
319 Blob const validator = ctx_.tx.getFieldVL(sfUNLModifyValidator);
320 if (!publicKeyType(makeSlice(validator)))
321 {
322 JLOG(j_.warn()) << "N-UNL: applyUNLModify, bad validator key";
323 return tefFAILURE;
324 }
325
326 JLOG(j_.info()) << "N-UNL: applyUNLModify, " << (disabling ? "ToDisable" : "ToReEnable")
327 << " seq=" << seq << " validator data:" << strHex(validator);
328
329 auto const k = keylet::negativeUNL();
330 SLE::pointer negUnlObject = view().peek(k);
331 if (!negUnlObject)
332 {
333 negUnlObject = std::make_shared<SLE>(k);
334 view().insert(negUnlObject);
335 }
336
337 bool const found = [&] {
338 if (negUnlObject->isFieldPresent(sfDisabledValidators))
339 {
340 auto const& negUnl = negUnlObject->getFieldArray(sfDisabledValidators);
341 for (auto const& v : negUnl)
342 {
343 if (v.isFieldPresent(sfPublicKey) && v.getFieldVL(sfPublicKey) == validator)
344 return true;
345 }
346 }
347 return false;
348 }();
349
350 if (disabling)
351 {
352 // cannot have more than one toDisable
353 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
354 {
355 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToDisable";
356 return tefFAILURE;
357 }
358
359 // cannot be the same as toReEnable
360 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
361 {
362 if (negUnlObject->getFieldVL(sfValidatorToReEnable) == validator)
363 {
364 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToDisable is same as ToReEnable";
365 return tefFAILURE;
366 }
367 }
368
369 // cannot be in negative UNL already
370 if (found)
371 {
372 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToDisable already in negative UNL";
373 return tefFAILURE;
374 }
375
376 negUnlObject->setFieldVL(sfValidatorToDisable, validator);
377 }
378 else
379 {
380 // cannot have more than one toReEnable
381 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
382 {
383 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToReEnable";
384 return tefFAILURE;
385 }
386
387 // cannot be the same as toDisable
388 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
389 {
390 if (negUnlObject->getFieldVL(sfValidatorToDisable) == validator)
391 {
392 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToReEnable is same as ToDisable";
393 return tefFAILURE;
394 }
395 }
396
397 // must be in negative UNL
398 if (!found)
399 {
400 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToReEnable is not in negative UNL";
401 return tefFAILURE;
402 }
403
404 negUnlObject->setFieldVL(sfValidatorToReEnable, validator);
405 }
406
407 view().update(negUnlObject);
408 return tesSUCCESS;
409}
410
411void
413{
414 // No transaction-specific invariants yet (future work).
415}
416
417bool
419{
420 // No transaction-specific invariants yet (future work).
421 return true;
422}
423
424} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Stream warn() const
Definition Journal.h:309
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void insert(SLE::ref sle)=0
Insert a new state SLE.
virtual void update(SLE::ref sle)=0
Indicate changes to a peeked SLE.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
Definition Change.cpp:412
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
Definition Change.cpp:418
static TER preclaim(PreclaimContext const &ctx)
Definition Change.cpp:76
TER applyUNLModify()
Definition Change.cpp:295
TER doApply() override
Definition Change.cpp:136
TER applyAmendment()
Definition Change.cpp:161
void preCompute() override
Definition Change.cpp:155
TER applyFee()
Definition Change.cpp:255
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition ReadView.h:90
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:97
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:171
bool empty() const
Definition STArray.h:246
void pushBack(STObject const &object)
Definition STArray.h:204
STObject & back()
Definition STArray.h:185
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:591
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:454
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:74
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:633
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:647
static Blob getSignature(STObject const &sigObject)
Definition STTx.cpp:180
TxType getTxnType() const
Definition STTx.h:188
Blob getSigningPubKey() const
Definition STTx.h:194
static NotTEC invokePreflight(PreflightContext const &ctx)
Definition Transactor.h:530
beast::Journal const j_
Definition Transactor.h:118
ApplyView & view()
Definition Transactor.h:136
AccountID const accountID_
Definition Transactor.h:120
ApplyContext & ctx_
Definition Transactor.h:116
T empty(T... args)
T find(T... args)
T make_shared(T... args)
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition Indexes.cpp:228
Keylet const & feeSettings() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:221
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:214
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
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,...
bool isFlagLedger(LedgerIndex seq)
Returns true if the given ledgerIndex is a flag ledgerIndex.
Definition Protocol.cpp:11
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
@ tefALREADY
Definition TER.h:157
@ tefFAILURE
Definition TER.h:156
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
@ temBAD_FEE
Definition TER.h:78
@ temINVALID
Definition TER.h:96
@ temBAD_SEQUENCE
Definition TER.h:90
@ temINVALID_FLAG
Definition TER.h:97
@ temBAD_SRC_ACCOUNT
Definition TER.h:92
@ temMALFORMED
Definition TER.h:73
@ temDISABLED
Definition TER.h:100
@ temUNKNOWN
Definition TER.h:110
@ temBAD_SIGNATURE
Definition TER.h:91
TERSubset< CanCvtToTER > TER
Definition TER.h:634
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid and flags.
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:10
BaseUInt< 256 > uint256
Definition base_uint.h:562
@ tesSUCCESS
Definition TER.h:240
std::enable_if_t< std::is_same_v< T, char >||std::is_same_v< T, unsigned char >, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
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:18
beast::Journal const j
Definition Transactor.h:25
T time_since_epoch(T... args)