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 // LCOV_EXCL_START
230 JLOG(j.fatal()) << "Unable to delete SignerList from owner.";
231 return tefBAD_LEDGER;
232 // LCOV_EXCL_STOP
233 }
234
236 view,
237 view.peek(accountKeylet),
238 removeFromOwnerCount,
239 app.journal("View"));
240
241 view.erase(signers);
242
243 return tesSUCCESS;
244}
245
246TER
248 Application& app,
249 ApplyView& view,
250 AccountID const& account,
252{
253 auto const accountKeylet = keylet::account(account);
254 auto const ownerDirKeylet = keylet::ownerDir(account);
255 auto const signerListKeylet = keylet::signers(account);
256
258 app, view, accountKeylet, ownerDirKeylet, signerListKeylet, j);
259}
260
261NotTEC
263 std::uint32_t quorum,
265 AccountID const& account,
267 Rules const& rules)
268{
269 // Reject if there are too many or too few entries in the list.
270 {
271 std::size_t const signerCount = signers.size();
272 if ((signerCount < STTx::minMultiSigners) ||
273 (signerCount > STTx::maxMultiSigners(&rules)))
274 {
275 JLOG(j.trace()) << "Too many or too few signers in signer list.";
276 return temMALFORMED;
277 }
278 }
279
280 // Make sure there are no duplicate signers.
281 XRPL_ASSERT(
282 std::is_sorted(signers.begin(), signers.end()),
283 "ripple::SetSignerList::validateQuorumAndSignerEntries : sorted "
284 "signers");
285 if (std::adjacent_find(signers.begin(), signers.end()) != signers.end())
286 {
287 JLOG(j.trace()) << "Duplicate signers in signer list";
288 return temBAD_SIGNER;
289 }
290
291 // Is the ExpandedSignerList amendment active?
292 bool const expandedSignerList = rules.enabled(featureExpandedSignerList);
293
294 // Make sure no signers reference this account. Also make sure the
295 // quorum can be reached.
296 std::uint64_t allSignersWeight(0);
297 for (auto const& signer : signers)
298 {
299 std::uint32_t const weight = signer.weight;
300 if (weight <= 0)
301 {
302 JLOG(j.trace()) << "Every signer must have a positive weight.";
303 return temBAD_WEIGHT;
304 }
305
306 allSignersWeight += signer.weight;
307
308 if (signer.account == account)
309 {
310 JLOG(j.trace()) << "A signer may not self reference account.";
311 return temBAD_SIGNER;
312 }
313
314 if (signer.tag && !expandedSignerList)
315 {
316 JLOG(j.trace()) << "Malformed transaction: sfWalletLocator "
317 "specified in SignerEntry "
318 << "but featureExpandedSignerList is not enabled.";
319 return temMALFORMED;
320 }
321
322 // Don't verify that the signer accounts exist. Non-existent accounts
323 // may be phantom accounts (which are permitted).
324 }
325 if ((quorum <= 0) || (allSignersWeight < quorum))
326 {
327 JLOG(j.trace()) << "Quorum is unreachable";
328 return temBAD_QUORUM;
329 }
330 return tesSUCCESS;
331}
332
333TER
335{
336 auto const accountKeylet = keylet::account(account_);
337 auto const ownerDirKeylet = keylet::ownerDir(account_);
338 auto const signerListKeylet = keylet::signers(account_);
339
340 // This may be either a create or a replace. Preemptively remove any
341 // old signer list. May reduce the reserve, so this is done before
342 // checking the reserve.
343 if (TER const ter = removeSignersFromLedger(
344 ctx_.app,
345 view(),
346 accountKeylet,
347 ownerDirKeylet,
348 signerListKeylet,
349 j_))
350 return ter;
351
352 auto const sle = view().peek(accountKeylet);
353 if (!sle)
354 return tefINTERNAL; // LCOV_EXCL_LINE
355
356 // Compute new reserve. Verify the account has funds to meet the reserve.
357 std::uint32_t const oldOwnerCount{(*sle)[sfOwnerCount]};
358
359 // The required reserve changes based on featureMultiSignReserve...
360 int addedOwnerCount{1};
362 if (!ctx_.view().rules().enabled(featureMultiSignReserve))
363 {
364 addedOwnerCount = signerCountBasedOwnerCountDelta(
365 signers_.size(), ctx_.view().rules());
366 flags = 0;
367 }
368
369 XRPAmount const newReserve{
370 view().fees().accountReserve(oldOwnerCount + addedOwnerCount)};
371
372 // We check the reserve against the starting balance because we want to
373 // allow dipping into the reserve to pay fees. This behavior is consistent
374 // with CreateTicket.
375 if (mPriorBalance < newReserve)
377
378 // Everything's ducky. Add the ltSIGNER_LIST to the ledger.
379 auto signerList = std::make_shared<SLE>(signerListKeylet);
380 view().insert(signerList);
381 writeSignersToSLE(signerList, flags);
382
383 auto viewJ = ctx_.app.journal("View");
384 // Add the signer list to the account's directory.
385 auto const page = ctx_.view().dirInsert(
386 ownerDirKeylet, signerListKeylet, describeOwnerDir(account_));
387
388 JLOG(j_.trace()) << "Create signer list for account " << toBase58(account_)
389 << ": " << (page ? "success" : "failure");
390
391 if (!page)
392 return tecDIR_FULL; // LCOV_EXCL_LINE
393
394 signerList->setFieldU64(sfOwnerNode, *page);
395
396 // If we succeeded, the new entry counts against the
397 // creator's reserve.
398 adjustOwnerCount(view(), sle, addedOwnerCount, viewJ);
399 return tesSUCCESS;
400}
401
402TER
404{
405 auto const accountKeylet = keylet::account(account_);
406 // Destroying the signer list is only allowed if either the master key
407 // is enabled or there is a regular key.
408 SLE::pointer ledgerEntry = view().peek(accountKeylet);
409 if (!ledgerEntry)
410 return tefINTERNAL; // LCOV_EXCL_LINE
411
412 if ((ledgerEntry->isFlag(lsfDisableMaster)) &&
413 (!ledgerEntry->isFieldPresent(sfRegularKey)))
415
416 auto const ownerDirKeylet = keylet::ownerDir(account_);
417 auto const signerListKeylet = keylet::signers(account_);
419 ctx_.app, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
420}
421
422void
424 SLE::pointer const& ledgerEntry,
425 std::uint32_t flags) const
426{
427 // Assign the quorum, default SignerListID, and flags.
428 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
429 {
430 ledgerEntry->setAccountID(sfOwner, account_);
431 }
432 ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
433 ledgerEntry->setFieldU32(sfSignerListID, DEFAULT_SIGNER_LIST_ID);
434 if (flags) // Only set flags if they are non-default (default is zero).
435 ledgerEntry->setFieldU32(sfFlags, flags);
436
437 bool const expandedSignerList =
438 ctx_.view().rules().enabled(featureExpandedSignerList);
439
440 // Create the SignerListArray one SignerEntry at a time.
441 STArray toLedger(signers_.size());
442 for (auto const& entry : signers_)
443 {
444 toLedger.push_back(STObject::makeInnerObject(sfSignerEntry));
445 STObject& obj = toLedger.back();
446 obj.reserve(2);
447 obj[sfAccount] = entry.account;
448 obj[sfSignerWeight] = entry.weight;
449
450 // This is a defensive check to make absolutely sure we will never write
451 // a tag into the ledger while featureExpandedSignerList is not enabled
452 if (expandedSignerList && entry.tag)
453 obj.setFieldH256(sfWalletLocator, *(entry.tag));
454 }
455
456 // Assign the SignerEntries.
457 ledgerEntry->setFieldArray(sfSignerEntries, toLedger);
458}
459
460} // 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:775
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:951
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:288
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:297
@ tecINSUFFICIENT_RESERVE
Definition TER.h:308
@ tesSUCCESS
Definition TER.h:245
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
TERSubset< CanCvtToTER > TER
Definition TER.h:649
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:609
@ 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