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