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