rippled
Loading...
Searching...
No Matches
SetSignerList.cpp
1#include <xrpld/app/ledger/Ledger.h>
2#include <xrpld/app/tx/detail/SetSignerList.h>
3
4#include <xrpl/basics/Log.h>
5#include <xrpl/ledger/ApplyView.h>
6#include <xrpl/protocol/Feature.h>
7#include <xrpl/protocol/Indexes.h>
8#include <xrpl/protocol/STArray.h>
9#include <xrpl/protocol/STObject.h>
10#include <xrpl/protocol/STTx.h>
11#include <xrpl/protocol/TxFlags.h>
12
13#include <algorithm>
14#include <cstdint>
15
16namespace ripple {
17
18// We're prepared for there to be multiple signer lists in the future,
19// but we don't need them yet. So for the time being we're manually
20// setting the sfSignerListID to zero in all cases.
22
24 NotTEC,
29 STTx const& tx,
30 ApplyFlags flags,
32{
33 // Check the quorum. A non-zero quorum means we're creating or replacing
34 // the list. A zero quorum means we're destroying the list.
35 auto const quorum = tx[sfSignerQuorum];
37 Operation op = unknown;
38
39 bool const hasSignerEntries(tx.isFieldPresent(sfSignerEntries));
40 if (quorum && hasSignerEntries)
41 {
42 auto signers = SignerEntries::deserialize(tx, j, "transaction");
43
44 if (!signers)
45 return std::make_tuple(signers.error(), quorum, sign, op);
46
47 std::sort(signers->begin(), signers->end());
48
49 // Save deserialized list for later.
50 sign = std::move(*signers);
51 op = set;
52 }
53 else if ((quorum == 0) && !hasSignerEntries)
54 {
55 op = destroy;
56 }
57
58 return std::make_tuple(tesSUCCESS, quorum, sign, op);
59}
60
63{
64 // 0 means "Allow any flags"
65 return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
66}
67
70{
71 auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
72
73 if (std::get<0>(result) != tesSUCCESS)
74 return std::get<0>(result);
75
76 if (std::get<3>(result) == unknown)
77 {
78 // Neither a set nor a destroy. Malformed.
79 JLOG(ctx.j.trace())
80 << "Malformed transaction: Invalid signer set list format.";
81 return temMALFORMED;
82 }
83
84 if (std::get<3>(result) == set)
85 {
86 // Validate our settings.
87 auto const account = ctx.tx.getAccountID(sfAccount);
89 std::get<1>(result),
90 std::get<2>(result),
91 account,
92 ctx.j,
93 ctx.rules);
94 if (ter != tesSUCCESS)
95 {
96 return ter;
97 }
98 }
99
100 return tesSUCCESS;
101}
102
103TER
105{
106 // Perform the operation preCompute() decided on.
107 switch (do_)
108 {
109 case set:
110 return replaceSignerList();
111
112 case destroy:
113 return destroySignerList();
114
115 default:
116 break;
117 }
118 // LCOV_EXCL_START
119 UNREACHABLE("ripple::SetSignerList::doApply : invalid operation");
120 return temMALFORMED;
121 // LCOV_EXCL_STOP
122}
123
124void
126{
127 // Get the quorum and operation info.
128 auto result = determineOperation(ctx_.tx, view().flags(), j_);
129 XRPL_ASSERT(
130 std::get<0>(result) == tesSUCCESS,
131 "ripple::SetSignerList::preCompute : result is tesSUCCESS");
132 XRPL_ASSERT(
133 std::get<3>(result) != unknown,
134 "ripple::SetSignerList::preCompute : result is known operation");
135
136 quorum_ = std::get<1>(result);
137 signers_ = std::get<2>(result);
138 do_ = std::get<3>(result);
139
140 return Transactor::preCompute();
141}
142
143// The return type is signed so it is compatible with the 3rd argument
144// of adjustOwnerCount() (which must be signed).
145//
146// NOTE: This way of computing the OwnerCount associated with a SignerList
147// is valid until the featureMultiSignReserve amendment passes. Once it
148// passes then just 1 OwnerCount is associated with a SignerList.
149static int
151{
152 // We always compute the full change in OwnerCount, taking into account:
153 // o The fact that we're adding/removing a SignerList and
154 // o Accounting for the number of entries in the list.
155 // We can get away with that because lists are not adjusted incrementally;
156 // we add or remove an entire list.
157 //
158 // The rule is:
159 // o Simply having a SignerList costs 2 OwnerCount units.
160 // o And each signer in the list costs 1 more OwnerCount unit.
161 // So, at a minimum, adding a SignerList with 1 entry costs 3 OwnerCount
162 // units. A SignerList with 8 entries would cost 10 OwnerCount units.
163 //
164 // The static_cast should always be safe since entryCount should always
165 // be in the range from 1 to 8 (or 32 if ExpandedSignerList is enabled).
166 // We've got a lot of room to grow.
167 XRPL_ASSERT(
168 entryCount >= STTx::minMultiSigners,
169 "ripple::signerCountBasedOwnerCountDelta : minimum signers");
170 XRPL_ASSERT(
171 entryCount <= STTx::maxMultiSigners(&rules),
172 "ripple::signerCountBasedOwnerCountDelta : maximum signers");
173 return 2 + static_cast<int>(entryCount);
174}
175
176static TER
178 Application& app,
179 ApplyView& view,
180 Keylet const& accountKeylet,
181 Keylet const& ownerDirKeylet,
182 Keylet const& signerListKeylet,
184{
185 // We have to examine the current SignerList so we know how much to
186 // reduce the OwnerCount.
187 SLE::pointer signers = view.peek(signerListKeylet);
188
189 // If the signer list doesn't exist we've already succeeded in deleting it.
190 if (!signers)
191 return tesSUCCESS;
192
193 // There are two different ways that the OwnerCount could be managed.
194 // If the lsfOneOwnerCount bit is set then remove just one owner count.
195 // Otherwise use the pre-MultiSignReserve amendment calculation.
196 int removeFromOwnerCount = -1;
197 if ((signers->getFlags() & lsfOneOwnerCount) == 0)
198 {
199 STArray const& actualList = signers->getFieldArray(sfSignerEntries);
200 removeFromOwnerCount =
201 signerCountBasedOwnerCountDelta(actualList.size(), view.rules()) *
202 -1;
203 }
204
205 // Remove the node from the account directory.
206 auto const hint = (*signers)[sfOwnerNode];
207
208 if (!view.dirRemove(ownerDirKeylet, hint, signerListKeylet.key, false))
209 {
210 // LCOV_EXCL_START
211 JLOG(j.fatal()) << "Unable to delete SignerList from owner.";
212 return tefBAD_LEDGER;
213 // LCOV_EXCL_STOP
214 }
215
217 view,
218 view.peek(accountKeylet),
219 removeFromOwnerCount,
220 app.journal("View"));
221
222 view.erase(signers);
223
224 return tesSUCCESS;
225}
226
227TER
229 Application& app,
230 ApplyView& view,
231 AccountID const& account,
233{
234 auto const accountKeylet = keylet::account(account);
235 auto const ownerDirKeylet = keylet::ownerDir(account);
236 auto const signerListKeylet = keylet::signers(account);
237
239 app, view, accountKeylet, ownerDirKeylet, signerListKeylet, j);
240}
241
242NotTEC
244 std::uint32_t quorum,
246 AccountID const& account,
248 Rules const& rules)
249{
250 // Reject if there are too many or too few entries in the list.
251 {
252 std::size_t const signerCount = signers.size();
253 if ((signerCount < STTx::minMultiSigners) ||
254 (signerCount > STTx::maxMultiSigners(&rules)))
255 {
256 JLOG(j.trace()) << "Too many or too few signers in signer list.";
257 return temMALFORMED;
258 }
259 }
260
261 // Make sure there are no duplicate signers.
262 XRPL_ASSERT(
263 std::is_sorted(signers.begin(), signers.end()),
264 "ripple::SetSignerList::validateQuorumAndSignerEntries : sorted "
265 "signers");
266 if (std::adjacent_find(signers.begin(), signers.end()) != signers.end())
267 {
268 JLOG(j.trace()) << "Duplicate signers in signer list";
269 return temBAD_SIGNER;
270 }
271
272 // Is the ExpandedSignerList amendment active?
273 bool const expandedSignerList = rules.enabled(featureExpandedSignerList);
274
275 // Make sure no signers reference this account. Also make sure the
276 // quorum can be reached.
277 std::uint64_t allSignersWeight(0);
278 for (auto const& signer : signers)
279 {
280 std::uint32_t const weight = signer.weight;
281 if (weight <= 0)
282 {
283 JLOG(j.trace()) << "Every signer must have a positive weight.";
284 return temBAD_WEIGHT;
285 }
286
287 allSignersWeight += signer.weight;
288
289 if (signer.account == account)
290 {
291 JLOG(j.trace()) << "A signer may not self reference account.";
292 return temBAD_SIGNER;
293 }
294
295 if (signer.tag && !expandedSignerList)
296 {
297 JLOG(j.trace()) << "Malformed transaction: sfWalletLocator "
298 "specified in SignerEntry "
299 << "but featureExpandedSignerList is not enabled.";
300 return temMALFORMED;
301 }
302
303 // Don't verify that the signer accounts exist. Non-existent accounts
304 // may be phantom accounts (which are permitted).
305 }
306 if ((quorum <= 0) || (allSignersWeight < quorum))
307 {
308 JLOG(j.trace()) << "Quorum is unreachable";
309 return temBAD_QUORUM;
310 }
311 return tesSUCCESS;
312}
313
314TER
316{
317 auto const accountKeylet = keylet::account(account_);
318 auto const ownerDirKeylet = keylet::ownerDir(account_);
319 auto const signerListKeylet = keylet::signers(account_);
320
321 // This may be either a create or a replace. Preemptively remove any
322 // old signer list. May reduce the reserve, so this is done before
323 // checking the reserve.
324 if (TER const ter = removeSignersFromLedger(
325 ctx_.app,
326 view(),
327 accountKeylet,
328 ownerDirKeylet,
329 signerListKeylet,
330 j_))
331 return ter;
332
333 auto const sle = view().peek(accountKeylet);
334 if (!sle)
335 return tefINTERNAL; // LCOV_EXCL_LINE
336
337 // Compute new reserve. Verify the account has funds to meet the reserve.
338 std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]};
339
340 // The required reserve changes based on featureMultiSignReserve...
341 int addedOwnerCount{1};
343 if (!ctx_.view().rules().enabled(featureMultiSignReserve))
344 {
345 addedOwnerCount = signerCountBasedOwnerCountDelta(
346 signers_.size(), ctx_.view().rules());
347 flags = 0;
348 }
349
350 XRPAmount const newReserve{
351 view().fees().accountReserve(oldOwnerCount + addedOwnerCount)};
352
353 // We check the reserve against the starting balance because we want to
354 // allow dipping into the reserve to pay fees. This behavior is consistent
355 // with CreateTicket.
356 if (mPriorBalance < newReserve)
358
359 // Everything's ducky. Add the ltSIGNER_LIST to the ledger.
360 auto signerList = std::make_shared<SLE>(signerListKeylet);
361 view().insert(signerList);
362 writeSignersToSLE(signerList, flags);
363
364 auto viewJ = ctx_.app.journal("View");
365 // Add the signer list to the account's directory.
366 auto const page = ctx_.view().dirInsert(
367 ownerDirKeylet, signerListKeylet, describeOwnerDir(account_));
368
369 JLOG(j_.trace()) << "Create signer list for account " << toBase58(account_)
370 << ": " << (page ? "success" : "failure");
371
372 if (!page)
373 return tecDIR_FULL; // LCOV_EXCL_LINE
374
375 signerList->setFieldU64(sfOwnerNode, *page);
376
377 // If we succeeded, the new entry counts against the
378 // creator's reserve.
379 adjustOwnerCount(view(), sle, addedOwnerCount, viewJ);
380 return tesSUCCESS;
381}
382
383TER
385{
386 auto const accountKeylet = keylet::account(account_);
387 // Destroying the signer list is only allowed if either the master key
388 // is enabled or there is a regular key.
389 SLE::pointer ledgerEntry = view().peek(accountKeylet);
390 if (!ledgerEntry)
391 return tefINTERNAL; // LCOV_EXCL_LINE
392
393 if ((ledgerEntry->isFlag(lsfDisableMaster)) &&
394 (!ledgerEntry->isFieldPresent(sfRegularKey)))
396
397 auto const ownerDirKeylet = keylet::ownerDir(account_);
398 auto const signerListKeylet = keylet::signers(account_);
400 ctx_.app, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
401}
402
403void
405 SLE::pointer const& ledgerEntry,
406 std::uint32_t flags) const
407{
408 // Assign the quorum, default SignerListID, and flags.
409 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
410 {
411 ledgerEntry->setAccountID(sfOwner, account_);
412 }
413 ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
414 ledgerEntry->setFieldU32(sfSignerListID, DEFAULT_SIGNER_LIST_ID);
415 if (flags) // Only set flags if they are non-default (default is zero).
416 ledgerEntry->setFieldU32(sfFlags, flags);
417
418 bool const expandedSignerList =
419 ctx_.view().rules().enabled(featureExpandedSignerList);
420
421 // Create the SignerListArray one SignerEntry at a time.
422 STArray toLedger(signers_.size());
423 for (auto const& entry : signers_)
424 {
425 toLedger.push_back(STObject::makeInnerObject(sfSignerEntry));
426 STObject& obj = toLedger.back();
427 obj.reserve(2);
428 obj[sfAccount] = entry.account;
429 obj[sfSignerWeight] = entry.weight;
430
431 // This is a defensive check to make absolutely sure we will never write
432 // a tag into the ledger while featureExpandedSignerList is not enabled
433 if (expandedSignerList && entry.tag)
434 obj.setFieldH256(sfWalletLocator, *(entry.tag));
435 }
436
437 // Assign the SignerEntries.
438 ledgerEntry->setFieldArray(sfSignerEntries, toLedger);
439}
440
441} // namespace ripple
T adjacent_find(T... args)
A generic endpoint for log messages.
Definition Journal.h:41
Stream fatal() const
Definition Journal.h:333
Stream trace() const
Severity stream access functions.
Definition Journal.h:303
virtual beast::Journal journal(std::string const &name)=0
ApplyView & view()
Application & app
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:124
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:300
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual Rules const & rules() const =0
Returns the tx processing rules.
Rules controlling protocol behavior.
Definition Rules.h:19
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
STObject & back()
Definition STArray.h:174
size_type size() const
Definition STArray.h:229
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:638
void setFieldH256(SField const &field, uint256 const &)
Definition STObject.cpp:756
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:465
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:76
void reserve(std::size_t n)
Definition STObject.h:932
static std::size_t const minMultiSigners
Definition STTx.h:34
static std::size_t maxMultiSigners(Rules const *rules=0)
Definition STTx.h:38
void writeSignersToSLE(SLE::pointer const &ledgerEntry, std::uint32_t flags) const
static NotTEC preflight(PreflightContext const &ctx)
static NotTEC validateQuorumAndSignerEntries(std::uint32_t quorum, std::vector< SignerEntries::SignerEntry > const &signers, AccountID const &account, beast::Journal j, Rules const &)
void preCompute() override
static TER removeFromLedger(Application &app, ApplyView &view, AccountID const &account, beast::Journal j)
std::uint32_t quorum_
std::vector< SignerEntries::SignerEntry > signers_
static std::tuple< NotTEC, std::uint32_t, std::vector< SignerEntries::SignerEntry >, Operation > determineOperation(STTx const &tx, ApplyFlags flags, beast::Journal j)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static Expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string_view annotation)
AccountID const account_
Definition Transactor.h:128
ApplyView & view()
Definition Transactor.h:144
beast::Journal const j_
Definition Transactor.h:126
XRPAmount mPriorBalance
Definition Transactor.h:129
virtual void preCompute()
ApplyContext & ctx_
Definition Transactor.h:124
T is_same_v
T is_sorted(T... args)
T make_tuple(T... args)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:355
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:311
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
static int signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const &rules)
static TER removeSignersFromLedger(Application &app, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet, beast::Journal j)
@ lsfOneOwnerCount
@ lsfDisableMaster
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1013
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1031
static std::uint32_t const DEFAULT_SIGNER_LIST_ID
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
@ tefBAD_LEDGER
Definition TER.h:151
@ tefINTERNAL
Definition TER.h:154
@ tecDIR_FULL
Definition TER.h:269
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:278
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tesSUCCESS
Definition TER.h:226
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:44
TERSubset< CanCvtToTER > TER
Definition TER.h:630
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:590
@ temBAD_SIGNER
Definition TER.h:96
@ temMALFORMED
Definition TER.h:68
@ temBAD_QUORUM
Definition TER.h:97
@ temBAD_WEIGHT
Definition TER.h:98
T sort(T... args)
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
State information when preflighting a tx.
Definition Transactor.h:16
beast::Journal const j
Definition Transactor.h:23