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#include <xrpld/ledger/ApplyView.h>
23
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 <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 ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
428 ledgerEntry->setFieldU32(sfSignerListID, defaultSignerListID_);
429 if (flags) // Only set flags if they are non-default (default is zero).
430 ledgerEntry->setFieldU32(sfFlags, flags);
431
432 bool const expandedSignerList =
433 ctx_.view().rules().enabled(featureExpandedSignerList);
434
435 // Create the SignerListArray one SignerEntry at a time.
436 STArray toLedger(signers_.size());
437 for (auto const& entry : signers_)
438 {
439 toLedger.push_back(STObject::makeInnerObject(sfSignerEntry));
440 STObject& obj = toLedger.back();
441 obj.reserve(2);
442 obj[sfAccount] = entry.account;
443 obj[sfSignerWeight] = entry.weight;
444
445 // This is a defensive check to make absolutely sure we will never write
446 // a tag into the ledger while featureExpandedSignerList is not enabled
447 if (expandedSignerList && entry.tag)
448 obj.setFieldH256(sfWalletLocator, *(entry.tag));
449 }
450
451 // Assign the SignerEntries.
452 ledgerEntry->setFieldArray(sfSignerEntries, toLedger);
453}
454
455} // 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()
Definition: ApplyContext.h:55
Application & app
Definition: ApplyContext.h:48
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:141
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
Definition: ApplyView.cpp:190
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:315
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: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:931
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)
TER doApply() override
std::uint32_t quorum_
Definition: SetSignerList.h:44
std::vector< SignerEntries::SignerEntry > signers_
Definition: SetSignerList.h:45
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:93
ApplyView & view()
Definition: Transactor.h:109
beast::Journal const j_
Definition: Transactor.h:91
XRPAmount mPriorBalance
Definition: Transactor.h:94
virtual void preCompute()
Definition: Transactor.cpp:513
ApplyContext & ctx_
Definition: Transactor.h:90
T is_sorted(T... args)
T make_tuple(T... args)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:176
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:366
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition: Indexes.cpp:322
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:114
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:83
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
Definition: SecretKey.cpp:256
@ 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:144
@ tecDIR_FULL
Definition: TER.h:274
@ tecNO_ALTERNATIVE_KEY
Definition: TER.h:283
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:294
@ tesSUCCESS
Definition: TER.h:242
constexpr std::uint32_t tfUniversalMask
Definition: TxFlags.h:62
ApplyFlags
Definition: ApplyView.h:31
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
@ 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.
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:34
beast::Journal const j
Definition: Transactor.h:40