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