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 ripple {
16
17template <>
19Transactor::invokePreflight<Change>(PreflightContext const& ctx)
20{
21 // 0 means "Allow any flags"
22 if (auto const ret = preflight0(ctx, 0))
23 return ret;
24
25 auto account = ctx.tx.getAccountID(sfAccount);
26 if (account != beast::zero)
27 {
28 JLOG(ctx.j.warn()) << "Change: Bad source id";
29 return temBAD_SRC_ACCOUNT;
30 }
31
32 // No point in going any further if the transaction fee is malformed.
33 auto const fee = ctx.tx.getFieldAmount(sfFee);
34 if (!fee.native() || fee != beast::zero)
35 {
36 JLOG(ctx.j.warn()) << "Change: invalid fee";
37 return temBAD_FEE;
38 }
39
40 if (!ctx.tx.getSigningPubKey().empty() || !ctx.tx.getSignature().empty() ||
41 ctx.tx.isFieldPresent(sfSigners))
42 {
43 JLOG(ctx.j.warn()) << "Change: Bad signature";
44 return temBAD_SIGNATURE;
45 }
46
47 if (ctx.tx.getFieldU32(sfSequence) != 0 ||
48 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("ripple::Change::doApply : invalid transaction type");
131 return tefFAILURE;
132 // LCOV_EXCL_STOP
133 }
134}
135
136void
138{
139 XRPL_ASSERT(
140 account_ == beast::zero, "ripple::Change::preCompute : zero account");
141}
142
143TER
145{
146 uint256 amendment(ctx_.tx.getFieldH256(sfAmendment));
147
148 auto const k = keylet::amendments();
149
150 SLE::pointer amendmentObject = view().peek(k);
151
152 if (!amendmentObject)
153 {
154 amendmentObject = std::make_shared<SLE>(k);
155 view().insert(amendmentObject);
156 }
157
158 STVector256 amendments = amendmentObject->getFieldV256(sfAmendments);
159
160 if (std::find(amendments.begin(), amendments.end(), amendment) !=
161 amendments.end())
162 return tefALREADY;
163
164 auto flags = ctx_.tx.getFlags();
165
166 bool const gotMajority = (flags & tfGotMajority) != 0;
167 bool const lostMajority = (flags & tfLostMajority) != 0;
168
169 if (gotMajority && lostMajority)
170 return temINVALID_FLAG;
171
172 STArray newMajorities(sfMajorities);
173
174 bool found = false;
175 if (amendmentObject->isFieldPresent(sfMajorities))
176 {
177 STArray const& oldMajorities =
178 amendmentObject->getFieldArray(sfMajorities);
179 for (auto const& majority : oldMajorities)
180 {
181 if (majority.getFieldH256(sfAmendment) == amendment)
182 {
183 if (gotMajority)
184 return tefALREADY;
185 found = true;
186 }
187 else
188 {
189 // pass through
190 newMajorities.push_back(majority);
191 }
192 }
193 }
194
195 if (!found && lostMajority)
196 return tefALREADY;
197
198 if (gotMajority)
199 {
200 // This amendment now has a majority
201 newMajorities.push_back(STObject::makeInnerObject(sfMajority));
202 auto& entry = newMajorities.back();
203 entry[sfAmendment] = amendment;
204 entry[sfCloseTime] =
206
207 if (!ctx_.app.getAmendmentTable().isSupported(amendment))
208 {
209 JLOG(j_.warn()) << "Unsupported amendment " << amendment
210 << " received a majority.";
211 }
212 }
213 else if (!lostMajority)
214 {
215 // No flags, enable amendment
216 amendments.push_back(amendment);
217 amendmentObject->setFieldV256(sfAmendments, amendments);
218
219 ctx_.app.getAmendmentTable().enable(amendment);
220
221 if (!ctx_.app.getAmendmentTable().isSupported(amendment))
222 {
223 JLOG(j_.error()) << "Unsupported amendment " << amendment
224 << " activated: server blocked.";
226 }
227 }
228
229 if (newMajorities.empty())
230 amendmentObject->makeFieldAbsent(sfMajorities);
231 else
232 amendmentObject->setFieldArray(sfMajorities, newMajorities);
233
234 view().update(amendmentObject);
235
236 return tesSUCCESS;
237}
238
239TER
241{
242 auto const k = keylet::fees();
243
244 SLE::pointer feeObject = view().peek(k);
245
246 if (!feeObject)
247 {
248 feeObject = std::make_shared<SLE>(k);
249 view().insert(feeObject);
250 }
251 auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) {
252 feeObject->at(field) = tx[field];
253 };
254 if (view().rules().enabled(featureXRPFees))
255 {
256 set(feeObject, ctx_.tx, sfBaseFeeDrops);
257 set(feeObject, ctx_.tx, sfReserveBaseDrops);
258 set(feeObject, ctx_.tx, sfReserveIncrementDrops);
259 // Ensure the old fields are removed
260 feeObject->makeFieldAbsent(sfBaseFee);
261 feeObject->makeFieldAbsent(sfReferenceFeeUnits);
262 feeObject->makeFieldAbsent(sfReserveBase);
263 feeObject->makeFieldAbsent(sfReserveIncrement);
264 }
265 else
266 {
267 set(feeObject, ctx_.tx, sfBaseFee);
268 set(feeObject, ctx_.tx, sfReferenceFeeUnits);
269 set(feeObject, ctx_.tx, sfReserveBase);
270 set(feeObject, ctx_.tx, sfReserveIncrement);
271 }
272
273 view().update(feeObject);
274
275 JLOG(j_.warn()) << "Fees have been changed";
276 return tesSUCCESS;
277}
278
279TER
281{
282 if (!isFlagLedger(view().seq()))
283 {
284 JLOG(j_.warn()) << "N-UNL: applyUNLModify, not a flag ledger, seq="
285 << view().seq();
286 return tefFAILURE;
287 }
288
289 if (!ctx_.tx.isFieldPresent(sfUNLModifyDisabling) ||
290 ctx_.tx.getFieldU8(sfUNLModifyDisabling) > 1 ||
291 !ctx_.tx.isFieldPresent(sfLedgerSequence) ||
292 !ctx_.tx.isFieldPresent(sfUNLModifyValidator))
293 {
294 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong Tx format.";
295 return tefFAILURE;
296 }
297
298 bool const disabling = ctx_.tx.getFieldU8(sfUNLModifyDisabling);
299 auto const seq = ctx_.tx.getFieldU32(sfLedgerSequence);
300 if (seq != view().seq())
301 {
302 JLOG(j_.warn()) << "N-UNL: applyUNLModify, wrong ledger seq=" << seq;
303 return tefFAILURE;
304 }
305
306 Blob const validator = ctx_.tx.getFieldVL(sfUNLModifyValidator);
307 if (!publicKeyType(makeSlice(validator)))
308 {
309 JLOG(j_.warn()) << "N-UNL: applyUNLModify, bad validator key";
310 return tefFAILURE;
311 }
312
313 JLOG(j_.info()) << "N-UNL: applyUNLModify, "
314 << (disabling ? "ToDisable" : "ToReEnable")
315 << " seq=" << seq
316 << " validator data:" << strHex(validator);
317
318 auto const k = keylet::negativeUNL();
319 SLE::pointer negUnlObject = view().peek(k);
320 if (!negUnlObject)
321 {
322 negUnlObject = std::make_shared<SLE>(k);
323 view().insert(negUnlObject);
324 }
325
326 bool const found = [&] {
327 if (negUnlObject->isFieldPresent(sfDisabledValidators))
328 {
329 auto const& negUnl =
330 negUnlObject->getFieldArray(sfDisabledValidators);
331 for (auto const& v : negUnl)
332 {
333 if (v.isFieldPresent(sfPublicKey) &&
334 v.getFieldVL(sfPublicKey) == validator)
335 return true;
336 }
337 }
338 return false;
339 }();
340
341 if (disabling)
342 {
343 // cannot have more than one toDisable
344 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
345 {
346 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToDisable";
347 return tefFAILURE;
348 }
349
350 // cannot be the same as toReEnable
351 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
352 {
353 if (negUnlObject->getFieldVL(sfValidatorToReEnable) == validator)
354 {
355 JLOG(j_.warn())
356 << "N-UNL: applyUNLModify, ToDisable is same as ToReEnable";
357 return tefFAILURE;
358 }
359 }
360
361 // cannot be in negative UNL already
362 if (found)
363 {
364 JLOG(j_.warn())
365 << "N-UNL: applyUNLModify, ToDisable already in negative UNL";
366 return tefFAILURE;
367 }
368
369 negUnlObject->setFieldVL(sfValidatorToDisable, validator);
370 }
371 else
372 {
373 // cannot have more than one toReEnable
374 if (negUnlObject->isFieldPresent(sfValidatorToReEnable))
375 {
376 JLOG(j_.warn()) << "N-UNL: applyUNLModify, already has ToReEnable";
377 return tefFAILURE;
378 }
379
380 // cannot be the same as toDisable
381 if (negUnlObject->isFieldPresent(sfValidatorToDisable))
382 {
383 if (negUnlObject->getFieldVL(sfValidatorToDisable) == validator)
384 {
385 JLOG(j_.warn())
386 << "N-UNL: applyUNLModify, ToReEnable is same as ToDisable";
387 return tefFAILURE;
388 }
389 }
390
391 // must be in negative UNL
392 if (!found)
393 {
394 JLOG(j_.warn())
395 << "N-UNL: applyUNLModify, ToReEnable is not in negative UNL";
396 return tefFAILURE;
397 }
398
399 negUnlObject->setFieldVL(sfValidatorToReEnable, validator);
400 }
401
402 view().update(negUnlObject);
403 return tesSUCCESS;
404}
405
406} // namespace ripple
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 NetworkOPs & getOPs()=0
virtual AmendmentTable & getAmendmentTable()=0
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.
TER applyUNLModify()
Definition Change.cpp:280
void preCompute() override
Definition Change.cpp:137
static TER preclaim(PreclaimContext const &ctx)
Definition Change.cpp:58
TER applyAmendment()
Definition Change.cpp:144
TER doApply() override
Definition Change.cpp:118
virtual void setAmendmentBlocked()=0
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
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:111
bool empty() const
Definition STArray.h:235
void push_back(STObject const &object)
Definition STArray.h:193
STObject & back()
Definition STArray.h:174
unsigned char getFieldU8(SField const &field) const
Definition STObject.cpp:584
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:644
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:638
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:596
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:652
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:465
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:76
std::uint32_t getFlags() const
Definition STObject.cpp:518
uint256 getFieldH256(SField const &field) const
Definition STObject.cpp:626
static Blob getSignature(STObject const &sigObject)
Definition STTx.cpp:184
Blob getSigningPubKey() const
Definition STTx.h:215
TxType getTxnType() const
Definition STTx.h:209
AccountID const account_
Definition Transactor.h:128
ApplyView & view()
Definition Transactor.h:144
beast::Journal const j_
Definition Transactor.h:126
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:211
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:195
Keylet const & fees() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:203
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t tfGotMajority
Definition TxFlags.h:109
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,...
@ tefFAILURE
Definition TER.h:147
@ tefALREADY
Definition TER.h:148
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
bool isFlagLedger(LedgerIndex seq)
Returns true if the given ledgerIndex is a flag ledgerIndex.
Definition Ledger.cpp:942
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
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
constexpr std::uint32_t tfLostMajority
Definition TxFlags.h:110
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
@ temBAD_SRC_ACCOUNT
Definition TER.h:87
@ temUNKNOWN
Definition TER.h:105
@ temBAD_FEE
Definition TER.h:73
@ temBAD_SEQUENCE
Definition TER.h:85
@ temMALFORMED
Definition TER.h:68
@ temINVALID
Definition TER.h:91
@ temINVALID_FLAG
Definition TER.h:92
@ temDISABLED
Definition TER.h:95
@ temBAD_SIGNATURE
Definition TER.h:86
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)