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