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 // 0 means "Allow any flags"
84 return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
85}
86
89{
90 auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
91
92 if (std::get<0>(result) != tesSUCCESS)
93 return std::get<0>(result);
94
95 if (std::get<3>(result) == unknown)
96 {
97 // Neither a set nor a destroy. Malformed.
98 JLOG(ctx.j.trace())
99 << "Malformed transaction: Invalid signer set list format.";
100 return temMALFORMED;
101 }
102
103 if (std::get<3>(result) == set)
104 {
105 // Validate our settings.
106 auto const account = ctx.tx.getAccountID(sfAccount);
108 std::get<1>(result),
109 std::get<2>(result),
110 account,
111 ctx.j,
112 ctx.rules);
113 if (ter != tesSUCCESS)
114 {
115 return ter;
116 }
117 }
118
119 return tesSUCCESS;
120}
121
122TER
124{
125 // Perform the operation preCompute() decided on.
126 switch (do_)
127 {
128 case set:
129 return replaceSignerList();
130
131 case destroy:
132 return destroySignerList();
133
134 default:
135 break;
136 }
137 // LCOV_EXCL_START
138 UNREACHABLE("ripple::SetSignerList::doApply : invalid operation");
139 return temMALFORMED;
140 // LCOV_EXCL_STOP
141}
142
143void
145{
146 // Get the quorum and operation info.
147 auto result = determineOperation(ctx_.tx, view().flags(), j_);
148 XRPL_ASSERT(
149 std::get<0>(result) == tesSUCCESS,
150 "ripple::SetSignerList::preCompute : result is tesSUCCESS");
151 XRPL_ASSERT(
152 std::get<3>(result) != unknown,
153 "ripple::SetSignerList::preCompute : result is known operation");
154
155 quorum_ = std::get<1>(result);
156 signers_ = std::get<2>(result);
157 do_ = std::get<3>(result);
158
159 return Transactor::preCompute();
160}
161
162// The return type is signed so it is compatible with the 3rd argument
163// of adjustOwnerCount() (which must be signed).
164//
165// NOTE: This way of computing the OwnerCount associated with a SignerList
166// is valid until the featureMultiSignReserve amendment passes. Once it
167// passes then just 1 OwnerCount is associated with a SignerList.
168static int
170{
171 // We always compute the full change in OwnerCount, taking into account:
172 // o The fact that we're adding/removing a SignerList and
173 // o Accounting for the number of entries in the list.
174 // We can get away with that because lists are not adjusted incrementally;
175 // we add or remove an entire list.
176 //
177 // The rule is:
178 // o Simply having a SignerList costs 2 OwnerCount units.
179 // o And each signer in the list costs 1 more OwnerCount unit.
180 // So, at a minimum, adding a SignerList with 1 entry costs 3 OwnerCount
181 // units. A SignerList with 8 entries would cost 10 OwnerCount units.
182 //
183 // The static_cast should always be safe since entryCount should always
184 // be in the range from 1 to 8 (or 32 if ExpandedSignerList is enabled).
185 // We've got a lot of room to grow.
186 XRPL_ASSERT(
187 entryCount >= STTx::minMultiSigners,
188 "ripple::signerCountBasedOwnerCountDelta : minimum signers");
189 XRPL_ASSERT(
190 entryCount <= STTx::maxMultiSigners(&rules),
191 "ripple::signerCountBasedOwnerCountDelta : maximum signers");
192 return 2 + static_cast<int>(entryCount);
193}
194
195static TER
197 Application& app,
198 ApplyView& view,
199 Keylet const& accountKeylet,
200 Keylet const& ownerDirKeylet,
201 Keylet const& signerListKeylet,
203{
204 // We have to examine the current SignerList so we know how much to
205 // reduce the OwnerCount.
206 SLE::pointer signers = view.peek(signerListKeylet);
207
208 // If the signer list doesn't exist we've already succeeded in deleting it.
209 if (!signers)
210 return tesSUCCESS;
211
212 // There are two different ways that the OwnerCount could be managed.
213 // If the lsfOneOwnerCount bit is set then remove just one owner count.
214 // Otherwise use the pre-MultiSignReserve amendment calculation.
215 int removeFromOwnerCount = -1;
216 if ((signers->getFlags() & lsfOneOwnerCount) == 0)
217 {
218 STArray const& actualList = signers->getFieldArray(sfSignerEntries);
219 removeFromOwnerCount =
220 signerCountBasedOwnerCountDelta(actualList.size(), view.rules()) *
221 -1;
222 }
223
224 // Remove the node from the account directory.
225 auto const hint = (*signers)[sfOwnerNode];
226
227 if (!view.dirRemove(ownerDirKeylet, hint, signerListKeylet.key, false))
228 {
229 JLOG(j.fatal()) << "Unable to delete SignerList from owner.";
230 return tefBAD_LEDGER;
231 }
232
234 view,
235 view.peek(accountKeylet),
236 removeFromOwnerCount,
237 app.journal("View"));
238
239 view.erase(signers);
240
241 return tesSUCCESS;
242}
243
244TER
246 Application& app,
247 ApplyView& view,
248 AccountID const& account,
250{
251 auto const accountKeylet = keylet::account(account);
252 auto const ownerDirKeylet = keylet::ownerDir(account);
253 auto const signerListKeylet = keylet::signers(account);
254
256 app, view, accountKeylet, ownerDirKeylet, signerListKeylet, j);
257}
258
259NotTEC
261 std::uint32_t quorum,
263 AccountID const& account,
265 Rules const& rules)
266{
267 // Reject if there are too many or too few entries in the list.
268 {
269 std::size_t const signerCount = signers.size();
270 if ((signerCount < STTx::minMultiSigners) ||
271 (signerCount > STTx::maxMultiSigners(&rules)))
272 {
273 JLOG(j.trace()) << "Too many or too few signers in signer list.";
274 return temMALFORMED;
275 }
276 }
277
278 // Make sure there are no duplicate signers.
279 XRPL_ASSERT(
280 std::is_sorted(signers.begin(), signers.end()),
281 "ripple::SetSignerList::validateQuorumAndSignerEntries : sorted "
282 "signers");
283 if (std::adjacent_find(signers.begin(), signers.end()) != signers.end())
284 {
285 JLOG(j.trace()) << "Duplicate signers in signer list";
286 return temBAD_SIGNER;
287 }
288
289 // Is the ExpandedSignerList amendment active?
290 bool const expandedSignerList = rules.enabled(featureExpandedSignerList);
291
292 // Make sure no signers reference this account. Also make sure the
293 // quorum can be reached.
294 std::uint64_t allSignersWeight(0);
295 for (auto const& signer : signers)
296 {
297 std::uint32_t const weight = signer.weight;
298 if (weight <= 0)
299 {
300 JLOG(j.trace()) << "Every signer must have a positive weight.";
301 return temBAD_WEIGHT;
302 }
303
304 allSignersWeight += signer.weight;
305
306 if (signer.account == account)
307 {
308 JLOG(j.trace()) << "A signer may not self reference account.";
309 return temBAD_SIGNER;
310 }
311
312 if (signer.tag && !expandedSignerList)
313 {
314 JLOG(j.trace()) << "Malformed transaction: sfWalletLocator "
315 "specified in SignerEntry "
316 << "but featureExpandedSignerList is not enabled.";
317 return temMALFORMED;
318 }
319
320 // Don't verify that the signer accounts exist. Non-existent accounts
321 // may be phantom accounts (which are permitted).
322 }
323 if ((quorum <= 0) || (allSignersWeight < quorum))
324 {
325 JLOG(j.trace()) << "Quorum is unreachable";
326 return temBAD_QUORUM;
327 }
328 return tesSUCCESS;
329}
330
331TER
333{
334 auto const accountKeylet = keylet::account(account_);
335 auto const ownerDirKeylet = keylet::ownerDir(account_);
336 auto const signerListKeylet = keylet::signers(account_);
337
338 // This may be either a create or a replace. Preemptively remove any
339 // old signer list. May reduce the reserve, so this is done before
340 // checking the reserve.
341 if (TER const ter = removeSignersFromLedger(
342 ctx_.app,
343 view(),
344 accountKeylet,
345 ownerDirKeylet,
346 signerListKeylet,
347 j_))
348 return ter;
349
350 auto const sle = view().peek(accountKeylet);
351 if (!sle)
352 return tefINTERNAL;
353
354 // Compute new reserve. Verify the account has funds to meet the reserve.
355 std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]};
356
357 // The required reserve changes based on featureMultiSignReserve...
358 int addedOwnerCount{1};
360 if (!ctx_.view().rules().enabled(featureMultiSignReserve))
361 {
362 addedOwnerCount = signerCountBasedOwnerCountDelta(
363 signers_.size(), ctx_.view().rules());
364 flags = 0;
365 }
366
367 XRPAmount const newReserve{
368 view().fees().accountReserve(oldOwnerCount + addedOwnerCount)};
369
370 // We check the reserve against the starting balance because we want to
371 // allow dipping into the reserve to pay fees. This behavior is consistent
372 // with CreateTicket.
373 if (mPriorBalance < newReserve)
375
376 // Everything's ducky. Add the ltSIGNER_LIST to the ledger.
377 auto signerList = std::make_shared<SLE>(signerListKeylet);
378 view().insert(signerList);
379 writeSignersToSLE(signerList, flags);
380
381 auto viewJ = ctx_.app.journal("View");
382 // Add the signer list to the account's directory.
383 auto const page = ctx_.view().dirInsert(
384 ownerDirKeylet, signerListKeylet, describeOwnerDir(account_));
385
386 JLOG(j_.trace()) << "Create signer list for account " << toBase58(account_)
387 << ": " << (page ? "success" : "failure");
388
389 if (!page)
390 return tecDIR_FULL;
391
392 signerList->setFieldU64(sfOwnerNode, *page);
393
394 // If we succeeded, the new entry counts against the
395 // creator's reserve.
396 adjustOwnerCount(view(), sle, addedOwnerCount, viewJ);
397 return tesSUCCESS;
398}
399
400TER
402{
403 auto const accountKeylet = keylet::account(account_);
404 // Destroying the signer list is only allowed if either the master key
405 // is enabled or there is a regular key.
406 SLE::pointer ledgerEntry = view().peek(accountKeylet);
407 if (!ledgerEntry)
408 return tefINTERNAL;
409
410 if ((ledgerEntry->isFlag(lsfDisableMaster)) &&
411 (!ledgerEntry->isFieldPresent(sfRegularKey)))
413
414 auto const ownerDirKeylet = keylet::ownerDir(account_);
415 auto const signerListKeylet = keylet::signers(account_);
417 ctx_.app, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
418}
419
420void
422 SLE::pointer const& ledgerEntry,
423 std::uint32_t flags) const
424{
425 // Assign the quorum, default SignerListID, and flags.
426 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
427 {
428 ledgerEntry->setAccountID(sfOwner, account_);
429 }
430 ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
431 ledgerEntry->setFieldU32(sfSignerListID, DEFAULT_SIGNER_LIST_ID);
432 if (flags) // Only set flags if they are non-default (default is zero).
433 ledgerEntry->setFieldU32(sfFlags, flags);
434
435 bool const expandedSignerList =
436 ctx_.view().rules().enabled(featureExpandedSignerList);
437
438 // Create the SignerListArray one SignerEntry at a time.
439 STArray toLedger(signers_.size());
440 for (auto const& entry : signers_)
441 {
442 toLedger.push_back(STObject::makeInnerObject(sfSignerEntry));
443 STObject& obj = toLedger.back();
444 obj.reserve(2);
445 obj[sfAccount] = entry.account;
446 obj[sfSignerWeight] = entry.weight;
447
448 // This is a defensive check to make absolutely sure we will never write
449 // a tag into the ledger while featureExpandedSignerList is not enabled
450 if (expandedSignerList && entry.tag)
451 obj.setFieldH256(sfWalletLocator, *(entry.tag));
452 }
453
454 // Assign the SignerEntries.
455 ledgerEntry->setFieldArray(sfSignerEntries, toLedger);
456}
457
458} // 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 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:319
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:657
void setFieldH256(SField const &field, uint256 const &)
Definition STObject.cpp:765
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:946
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 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:147
ApplyView & view()
Definition Transactor.h:163
beast::Journal const j_
Definition Transactor.h:145
XRPAmount mPriorBalance
Definition Transactor.h:148
virtual void preCompute()
ApplyContext & ctx_
Definition Transactor.h:143
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:1032
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1050
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:170
@ tefINTERNAL
Definition TER.h:173
@ tecDIR_FULL
Definition TER.h:287
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:296
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tesSUCCESS
Definition TER.h:244
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
@ 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:35
beast::Journal const j
Definition Transactor.h:42