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/ledger/entries/AmendmentsEntry.h>
12#include <xrpl/ledger/entries/FeeSettingsEntry.h>
13#include <xrpl/ledger/entries/NegativeUNLEntry.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/Protocol.h>
16#include <xrpl/protocol/PublicKey.h>
17#include <xrpl/protocol/SField.h>
18#include <xrpl/protocol/STArray.h>
19#include <xrpl/protocol/STLedgerEntry.h>
20#include <xrpl/protocol/STTx.h>
21#include <xrpl/protocol/STVector256.h>
22#include <xrpl/protocol/TER.h>
23#include <xrpl/protocol/TxFlags.h>
24#include <xrpl/protocol/TxFormats.h>
25#include <xrpl/protocol/XRPAmount.h>
26#include <xrpl/server/NetworkOPs.h>
27#include <xrpl/tx/Transactor.h>
28
29#include <algorithm>
30
31namespace xrpl {
32
33template <>
36{
37 // 0 means "Allow any flags"
38 // The check for tfEnableAmendmentMask is gated by LendingProtocol because
39 // that feature introduced this parameter, and it's not worth adding another
40 // amendment just for this.
41 if (auto const ret =
42 preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfEnableAmendmentMask : 0))
43 return ret;
44
45 auto account = ctx.tx.getAccountID(sfAccount);
46 if (account != beast::kZero)
47 {
48 JLOG(ctx.j.warn()) << "Change: Bad source id";
49 return temBAD_SRC_ACCOUNT;
50 }
51
52 // No point in going any further if the transaction fee is malformed.
53 auto const fee = ctx.tx.getFieldAmount(sfFee);
54 if (!fee.native() || fee != beast::kZero)
55 {
56 JLOG(ctx.j.warn()) << "Change: invalid fee";
57 return temBAD_FEE;
58 }
59
60 if (!ctx.tx.getSigningPubKey().empty() || !ctx.tx.getSignature().empty() ||
61 ctx.tx.isFieldPresent(sfSigners))
62 {
63 JLOG(ctx.j.warn()) << "Change: Bad signature";
64 return temBAD_SIGNATURE;
65 }
66
67 if (ctx.tx.getFieldU32(sfSequence) != 0 || ctx.tx.isFieldPresent(sfPreviousTxnID))
68 {
69 JLOG(ctx.j.warn()) << "Change: Bad sequence";
70 return temBAD_SEQUENCE;
71 }
72
73 return tesSUCCESS;
74}
75
76TER
78{
79 // If tapOPEN_LEDGER is resurrected into ApplyFlags,
80 // this block can be moved to preflight.
81 if (ctx.view.open())
82 {
83 JLOG(ctx.j.warn()) << "Change transaction against open ledger";
84 return temINVALID;
85 }
86
87 switch (ctx.tx.getTxnType())
88 {
89 case ttFEE:
90 if (ctx.view.rules().enabled(featureXRPFees))
91 {
92 // The ttFEE transaction format defines these fields as
93 // optional, but once the XRPFees feature is enabled, they are
94 // required.
95 if (!ctx.tx.isFieldPresent(sfBaseFeeDrops) ||
96 !ctx.tx.isFieldPresent(sfReserveBaseDrops) ||
97 !ctx.tx.isFieldPresent(sfReserveIncrementDrops))
98 return temMALFORMED;
99 // The ttFEE transaction format defines these fields as
100 // optional, but once the XRPFees feature is enabled, they are
101 // forbidden.
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 }
108 else
109 {
110 // The ttFEE transaction format formerly defined these fields
111 // as required. When the XRPFees feature was implemented, they
112 // were changed to be optional. Until the feature has been
113 // enabled, they are required.
114 if (!ctx.tx.isFieldPresent(sfBaseFee) ||
115 !ctx.tx.isFieldPresent(sfReferenceFeeUnits) ||
116 !ctx.tx.isFieldPresent(sfReserveBase) ||
117 !ctx.tx.isFieldPresent(sfReserveIncrement))
118 return temMALFORMED;
119 // The ttFEE transaction format defines these fields as
120 // optional, but without the XRPFees feature, they are
121 // forbidden.
122 if (ctx.tx.isFieldPresent(sfBaseFeeDrops) ||
123 ctx.tx.isFieldPresent(sfReserveBaseDrops) ||
124 ctx.tx.isFieldPresent(sfReserveIncrementDrops))
125 return temDISABLED;
126 }
127 // The ttFEE transaction format defines these fields as optional,
128 // but they are unconditionally forbidden until FeeVoteImpl is
129 // updated to populate them (SmartEscrow behavioral port).
130 if (ctx.tx.isFieldPresent(sfGasLimit) || ctx.tx.isFieldPresent(sfBytecodeSizeLimit) ||
131 ctx.tx.isFieldPresent(sfGasPrice))
132 return temDISABLED;
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 // LCOV_EXCL_START
154 default:
155 UNREACHABLE("xrpl::Change::doApply : invalid transaction type");
156 return tefFAILURE;
157 // LCOV_EXCL_STOP
158 }
159}
160
161void
163{
164 XRPL_ASSERT(accountID_ == beast::kZero, "xrpl::Change::preCompute : zero account");
165}
166
167TER
169{
170 UInt256 const amendment(ctx_.tx.getFieldH256(sfAmendment));
171
172 AmendmentsEntryW amendmentObject(view(), j_);
173
174 if (!amendmentObject)
175 {
176 amendmentObject.newSLE();
177 amendmentObject.insert();
178 }
179
180 STVector256 amendments = amendmentObject->getFieldV256(sfAmendments);
181
182 if (std::ranges::find(amendments, amendment) != amendments.end())
183 return tefALREADY;
184
185 bool const gotMajority = ctx_.tx.isFlag(tfGotMajority);
186 bool const lostMajority = ctx_.tx.isFlag(tfLostMajority);
187
188 if (gotMajority && lostMajority)
189 return temINVALID_FLAG;
190
191 STArray newMajorities(sfMajorities);
192
193 bool found = false;
194 if (amendmentObject->isFieldPresent(sfMajorities))
195 {
196 STArray const& oldMajorities = amendmentObject->getFieldArray(sfMajorities);
197 for (auto const& majority : oldMajorities)
198 {
199 if (majority.getFieldH256(sfAmendment) == amendment)
200 {
201 if (gotMajority)
202 return tefALREADY;
203 found = true;
204 }
205 else
206 {
207 // pass through
208 newMajorities.pushBack(majority);
209 }
210 }
211 }
212
213 if (!found && lostMajority)
214 return tefALREADY;
215
216 if (gotMajority)
217 {
218 // This amendment now has a majority
219 newMajorities.pushBack(STObject::makeInnerObject(sfMajority));
220 auto& entry = newMajorities.back();
221 entry[sfAmendment] = amendment;
222 entry[sfCloseTime] = view().parentCloseTime().time_since_epoch().count();
223
224 if (!ctx_.registry.get().getAmendmentTable().isSupported(amendment))
225 {
226 JLOG(j_.warn()) << "Unsupported amendment " << amendment << " received a majority.";
227 }
228 }
229 else if (!lostMajority)
230 {
231 // No flags, enable amendment
232 amendments.pushBack(amendment);
233 amendmentObject->setFieldV256(sfAmendments, amendments);
234
235 ctx_.registry.get().getAmendmentTable().enable(amendment);
236
237 if (!ctx_.registry.get().getAmendmentTable().isSupported(amendment))
238 {
239 JLOG(j_.error()) << "Unsupported amendment " << amendment
240 << " activated: server blocked.";
241 ctx_.registry.get().getOPs().setAmendmentBlocked();
242 }
243 }
244
245 if (newMajorities.empty())
246 {
247 amendmentObject->makeFieldAbsent(sfMajorities);
248 }
249 else
250 {
251 amendmentObject->setFieldArray(sfMajorities, newMajorities);
252 }
253
254 amendmentObject.update();
255
256 return tesSUCCESS;
257}
258
259TER
261{
262 FeeSettingsEntryW feeObject(view());
263
264 if (!feeObject)
265 {
266 feeObject.newSLE();
267 feeObject.insert();
268 }
269 auto set = [](FeeSettingsEntryW& feeObject, STTx const& tx, auto const& field) {
270 feeObject->at(field) = tx[field];
271 };
272 if (view().rules().enabled(featureXRPFees))
273 {
274 set(feeObject, ctx_.tx, sfBaseFeeDrops);
275 set(feeObject, ctx_.tx, sfReserveBaseDrops);
276 set(feeObject, ctx_.tx, sfReserveIncrementDrops);
277 // Ensure the old fields are removed
278 feeObject->makeFieldAbsent(sfBaseFee);
279 feeObject->makeFieldAbsent(sfReferenceFeeUnits);
280 feeObject->makeFieldAbsent(sfReserveBase);
281 feeObject->makeFieldAbsent(sfReserveIncrement);
282 }
283 else
284 {
285 set(feeObject, ctx_.tx, sfBaseFee);
286 set(feeObject, ctx_.tx, sfReferenceFeeUnits);
287 set(feeObject, ctx_.tx, sfReserveBase);
288 set(feeObject, ctx_.tx, sfReserveIncrement);
289 }
290
291 feeObject.update();
292
293 JLOG(j_.warn()) << "Fees have been changed";
294 return tesSUCCESS;
295}
296
297TER
299{
300 if (!isFlagLedger(view().seq()))
301 {
302 JLOG(j_.warn()) << "N-UNL: applyUNLModify, not a flag ledger, seq=" << view().seq();
303 return tefFAILURE;
304 }
305
306 if (!ctx_.tx.isFieldPresent(sfUNLModifyDisabling) ||
307 ctx_.tx.getFieldU8(sfUNLModifyDisabling) > 1 || !ctx_.tx.isFieldPresent(sfLedgerSequence) ||
308 !ctx_.tx.isFieldPresent(sfUNLModifyValidator))
309 {
310 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong Tx format.";
311 return tefFAILURE;
312 }
313
314 bool const disabling = ctx_.tx.getFieldU8(sfUNLModifyDisabling) != 0u;
315 auto const seq = ctx_.tx.getFieldU32(sfLedgerSequence);
316 if (seq != view().seq())
317 {
318 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong ledger seq=" << seq;
319 return tefFAILURE;
320 }
321
322 Blob const validator = ctx_.tx.getFieldVL(sfUNLModifyValidator);
323 if (!publicKeyType(makeSlice(validator)))
324 {
325 JLOG(j_.warn()) << "N-UNL: applyUNLModify, bad validator key";
326 return tefFAILURE;
327 }
328
329 JLOG(j_.info()) << "N-UNL: applyUNLModify, " << (disabling ? "ToDisable" : "ToReEnable")
330 << " seq=" << seq << " validator data:" << strHex(validator);
331
332 NegativeUNLEntryW negUnlObject(view(), j_);
333 if (!negUnlObject)
334 {
335 negUnlObject.newSLE();
336 negUnlObject.insert();
337 }
338
339 bool const found = [&] {
340 if (negUnlObject->isFieldPresent(sfDisabledValidators))
341 {
342 auto const& negUnl = negUnlObject->getFieldArray(sfDisabledValidators);
343 for (auto const& v : negUnl)
344 {
345 if (v.isFieldPresent(sfPublicKey) && v.getFieldVL(sfPublicKey) == validator)
346 return true;
347 }
348 }
349 return false;
350 }();
351
352 if (disabling)
353 {
354 // cannot have more than one toDisable
355 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
356 {
357 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToDisable";
358 return tefFAILURE;
359 }
360
361 // cannot be the same as toReEnable
362 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
363 {
364 if (negUnlObject->getFieldVL(sfValidatorToReEnable) == validator)
365 {
366 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToDisable is same as ToReEnable";
367 return tefFAILURE;
368 }
369 }
370
371 // cannot be in negative UNL already
372 if (found)
373 {
374 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToDisable already in negative UNL";
375 return tefFAILURE;
376 }
377
378 negUnlObject->setFieldVL(sfValidatorToDisable, validator);
379 }
380 else
381 {
382 // cannot have more than one toReEnable
383 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
384 {
385 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToReEnable";
386 return tefFAILURE;
387 }
388
389 // cannot be the same as toDisable
390 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
391 {
392 if (negUnlObject->getFieldVL(sfValidatorToDisable) == validator)
393 {
394 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToReEnable is same as ToDisable";
395 return tefFAILURE;
396 }
397 }
398
399 // must be in negative UNL
400 if (!found)
401 {
402 JLOG(j_.warn()) << "N-UNL: applyUNLModify, ToReEnable is not in negative UNL";
403 return tefFAILURE;
404 }
405
406 negUnlObject->setFieldVL(sfValidatorToReEnable, validator);
407 }
408
409 negUnlObject.update();
410 return tesSUCCESS;
411}
412
413void
415{
416 // No transaction-specific invariants yet (future work).
417}
418
419bool
421{
422 // No transaction-specific invariants yet (future work).
423 return true;
424}
425
426} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream warn() const
Definition Journal.h:356
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:420
static TER preclaim(PreclaimContext const &ctx)
Definition Change.cpp:77
TER applyUNLModify()
Definition Change.cpp:298
TER doApply() override
Definition Change.cpp:143
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
Definition Change.cpp:414
TER applyAmendment()
Definition Change.cpp:168
void preCompute() override
Definition Change.cpp:162
TER applyFee()
Definition Change.cpp:260
A view into a ledger.
Definition ReadView.h:41
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:106
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:115
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:182
bool empty() const
Definition STArray.h:254
void pushBack(STObject const &object)
Definition STArray.h:212
STObject & back()
Definition STArray.h:193
std::shared_ptr< STLedgerEntry const > const & ConstRef
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1078
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:649
void setFieldV256(SField const &field, STVector256 const &v)
Definition STObject.cpp:779
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:601
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:791
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:688
void setFieldArray(SField const &field, STArray const &v)
Definition STObject.cpp:833
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:79
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:643
STVector256 const & getFieldV256(SField const &field) const
Definition STObject.cpp:671
void makeFieldAbsent(SField const &field)
Definition STObject.cpp:550
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:657
static Blob getSignature(STObject const &sigObject)
Definition STTx.cpp:185
TxType getTxnType() const
Definition STTx.h:250
Blob getSigningPubKey() const
Definition STTx.h:256
static NotTEC invokePreflight(PreflightContext const &ctx)
Definition Transactor.h:648
beast::Journal const j_
Definition Transactor.h:164
ApplyView & view()
Definition Transactor.h:184
AccountID const accountID_
Definition Transactor.h:166
ApplyContext & ctx_
Definition Transactor.h:162
T empty(T... args)
T find(T... args)
constexpr Zero kZero
Definition Zero.h:30
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:13
@ tefALREADY
Definition TER.h:162
@ tefFAILURE
Definition TER.h:161
NegativeUNLEntry< ApplyView > NegativeUNLEntryW
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
BaseUInt< 256 > UInt256
Definition base_uint.h:580
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
FeeSettingsEntry< ApplyView > FeeSettingsEntryW
@ temBAD_FEE
Definition TER.h:80
@ temINVALID
Definition TER.h:98
@ temBAD_SEQUENCE
Definition TER.h:92
@ temINVALID_FLAG
Definition TER.h:99
@ temBAD_SRC_ACCOUNT
Definition TER.h:94
@ temMALFORMED
Definition TER.h:75
@ temDISABLED
Definition TER.h:102
@ temUNKNOWN
Definition TER.h:112
@ temBAD_SIGNATURE
Definition TER.h:93
TERSubset< CanCvtToTER > TER
Definition TER.h:654
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:11
@ tesSUCCESS
Definition TER.h:250
AmendmentsEntry< ApplyView > AmendmentsEntryW
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
beast::Journal const j
Definition Transactor.h:100
State information when preflighting a tx.
Definition Transactor.h:39
beast::Journal const j
Definition Transactor.h:46
T time_since_epoch(T... args)