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