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