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