xrpld
Loading...
Searching...
No Matches
SignerListSet.cpp
1#include <xrpl/tx/transactors/account/SignerListSet.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/Journal.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/ApplyView.h>
8#include <xrpl/ledger/ReadView.h>
9#include <xrpl/ledger/helpers/AccountRootHelpers.h>
10#include <xrpl/ledger/helpers/DirectoryHelpers.h>
11#include <xrpl/ledger/helpers/SponsorHelpers.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/Indexes.h>
15#include <xrpl/protocol/Keylet.h>
16#include <xrpl/protocol/LedgerFormats.h>
17#include <xrpl/protocol/SField.h>
18#include <xrpl/protocol/STArray.h>
19#include <xrpl/protocol/STLedgerEntry.h>
20#include <xrpl/protocol/STObject.h>
21#include <xrpl/protocol/STTx.h>
22#include <xrpl/protocol/TER.h>
23#include <xrpl/protocol/TxFlags.h>
24#include <xrpl/protocol/XRPAmount.h>
25#include <xrpl/tx/SignerEntries.h>
26#include <xrpl/tx/Transactor.h>
27
28#include <algorithm>
29#include <cstddef>
30#include <cstdint>
31#include <memory>
32#include <tuple>
33#include <utility>
34#include <vector>
35
36namespace xrpl {
37
38// We're prepared for there to be multiple signer lists in the future,
39// but we don't need them yet. So for the time being we're manually
40// setting the sfSignerListID to zero in all cases.
42
45{
46 // Check the quorum. A non-zero quorum means we're creating or replacing
47 // the list. A zero quorum means we're destroying the list.
48 auto const quorum = tx[sfSignerQuorum];
51
52 bool const hasSignerEntries(tx.isFieldPresent(sfSignerEntries));
53 if ((quorum != 0u) && hasSignerEntries)
54 {
55 auto signers = SignerEntries::deserialize(tx, j, "transaction");
56
57 if (!signers)
58 return std::make_tuple(signers.error(), quorum, sign, op);
59
60 std::sort(signers->begin(), signers->end());
61
62 // Save deserialized list for later.
63 sign = std::move(*signers);
64 op = Operation::Set;
65 }
66 else if ((quorum == 0) && !hasSignerEntries)
67 {
69 }
70
71 return std::make_tuple(tesSUCCESS, quorum, sign, op);
72}
73
76{
77 // 0 means "Allow any flags"
78 return ctx.rules.enabled(fixInvalidTxFlags) ? tfUniversalMask : 0;
79}
80
83{
84 auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
85
86 if (!isTesSuccess(std::get<0>(result)))
87 return std::get<0>(result);
88
89 if (std::get<3>(result) == Operation::Unknown)
90 {
91 // Neither a set nor a destroy. Malformed.
92 JLOG(ctx.j.trace()) << "Malformed transaction: Invalid signer set list format.";
93 return temMALFORMED;
94 }
95
96 if (std::get<3>(result) == Operation::Set)
97 {
98 // Validate our settings.
99 auto const account = ctx.tx.getAccountID(sfAccount);
101 std::get<1>(result), std::get<2>(result), account, ctx.j, ctx.rules);
102 if (!isTesSuccess(ter))
103 {
104 return ter;
105 }
106 }
107
108 return tesSUCCESS;
109}
110
111TER
113{
114 // Perform the operation preCompute() decided on.
115 switch (do_)
116 {
117 case Operation::Set:
118 return replaceSignerList();
119
121 return destroySignerList();
122
123 default:
124 break;
125 }
126 // LCOV_EXCL_START
127 UNREACHABLE("xrpl::SignerListSet::doApply : invalid operation");
128 return temMALFORMED;
129 // LCOV_EXCL_STOP
130}
131
132void
134{
135 // Get the quorum and operation info.
136 auto result = determineOperation(ctx_.tx, view().flags(), j_);
137 XRPL_ASSERT(
138 isTesSuccess(std::get<0>(result)),
139 "xrpl::SignerListSet::preCompute : result is tesSUCCESS");
140 XRPL_ASSERT(
141 std::get<3>(result) != Operation::Unknown,
142 "xrpl::SignerListSet::preCompute : result is known operation");
143
144 quorum_ = std::get<1>(result);
145 signers_ = std::get<2>(result);
146 do_ = std::get<3>(result);
147
149}
150
151static std::uint32_t
153{
154 // We always compute the full change in OwnerCount, taking into account:
155 // o The fact that we're adding/removing a SignerList and
156 // o Accounting for the number of entries in the list.
157 // We can get away with that because lists are not adjusted incrementally;
158 // we add or remove an entire list.
159 //
160 // The rule is:
161 // o Simply having a SignerList costs 2 OwnerCount units.
162 // o And each signer in the list costs 1 more OwnerCount unit.
163 // So, at a minimum, adding a SignerList with 1 entry costs 3 OwnerCount
164 // units. A SignerList with 8 entries would cost 10 OwnerCount units.
165 //
166 // The static_cast should always be safe since entryCount should always
167 // be in the range from 1 to 32, so the result is always positive.
168 // We've got a lot of room to grow.
169 XRPL_ASSERT(
170 entryCount >= STTx::kMinMultiSigners,
171 "xrpl::signerCountBasedOwnerCountDelta : minimum signers");
172 XRPL_ASSERT(
173 entryCount <= STTx::kMaxMultiSigners,
174 "xrpl::signerCountBasedOwnerCountDelta : maximum signers");
175 return 2 + static_cast<int>(entryCount);
176}
177
178static TER
180 ServiceRegistry& registry,
181 ApplyView& view,
182 Keylet const& accountKeylet,
183 Keylet const& ownerDirKeylet,
184 Keylet const& signerListKeylet,
186{
187 // We have to examine the current SignerList so we know how much to
188 // reduce the OwnerCount.
189 SLE::pointer const signers = view.peek(signerListKeylet);
190
191 // If the signer list doesn't exist we've already succeeded in deleting it.
192 if (!signers)
193 return tesSUCCESS;
194
195 // There are two different ways that the OwnerCount could be managed.
196 // If the lsfOneOwnerCount bit is set then remove just one owner count.
197 // Otherwise use the pre-MultiSignReserve amendment calculation.
198 std::uint32_t removeFromOwnerCount = 1;
199 if (!signers->isFlag(lsfOneOwnerCount))
200 {
201 STArray const& actualList = signers->getFieldArray(sfSignerEntries);
202 removeFromOwnerCount = signerCountBasedOwnerCountDelta(actualList.size(), view.rules());
203 }
204
205 // Remove the node from the account directory.
206 auto const hint = (*signers)[sfOwnerNode];
207
208 if (!view.dirRemove(ownerDirKeylet, hint, signerListKeylet.key, false))
209 {
210 // LCOV_EXCL_START
211 JLOG(j.fatal()) << "Unable to delete SignerList from owner.";
212 return tefBAD_LEDGER;
213 // LCOV_EXCL_STOP
214 }
215
217 view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View"));
218
219 view.erase(signers);
220
221 return tesSUCCESS;
222}
223
224TER
226 ServiceRegistry& registry,
228 AccountID const& account,
230{
231 auto const accountKeylet = keylet::account(account);
232 auto const ownerDirKeylet = keylet::ownerDir(account);
233 auto const signerListKeylet = keylet::signerList(account);
234
236 registry, view, accountKeylet, ownerDirKeylet, signerListKeylet, j);
237}
238
239NotTEC
241 std::uint32_t quorum,
243 AccountID const& account,
245 Rules const& rules)
246{
247 // Reject if there are too many or too few entries in the list.
248 {
249 std::size_t const signerCount = signers.size();
250 if (signerCount < STTx::kMinMultiSigners || signerCount > STTx::kMaxMultiSigners)
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::ranges::is_sorted(signers),
260 "xrpl::SignerListSet::validateQuorumAndSignerEntries : sorted "
261 "signers");
262 if (std::ranges::adjacent_find(signers) != 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(accountID_);
302 auto const ownerDirKeylet = keylet::ownerDir(accountID_);
303 auto const signerListKeylet = keylet::signerList(accountID_);
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_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_))
310 return ter;
311
312 auto const sle = view().peek(accountKeylet);
313 if (!sle)
314 return tefINTERNAL; // LCOV_EXCL_LINE
315
316 static constexpr int kAddedOwnerCount = 1;
317 std::uint32_t const flags{lsfOneOwnerCount};
318
319 // We check the reserve against the starting balance because we want to
320 // allow dipping into the reserve to pay fees. This behavior is consistent
321 // with TicketCreate.
322 if (auto const ret = checkReserve(
323 ctx_.getApplyViewContext(),
324 sle,
326 {.ownerCountDelta = kAddedOwnerCount},
327 ctx_.journal);
328 !isTesSuccess(ret))
329 return ret;
330
331 // Everything's ducky. Add the ltSIGNER_LIST to the ledger.
332 auto signerList = std::make_shared<SLE>(signerListKeylet);
333 view().insert(signerList);
334 writeSignersToSLE(signerList, flags);
335
336 auto viewJ = ctx_.registry.get().getJournal("View");
337 // Add the signer list to the account's directory.
338 auto const page =
339 ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
340
341 JLOG(j_.trace()) << "Create signer list for account " << toBase58(accountID_) << ": "
342 << (page ? "success" : "failure");
343
344 if (!page)
345 return tecDIR_FULL; // LCOV_EXCL_LINE
346
347 signerList->setFieldU64(sfOwnerNode, *page);
348
349 // If we succeeded, the new entry counts against the
350 // creator's reserve.
351 increaseOwnerCount(ctx_.getApplyViewContext(), sle, kAddedOwnerCount, viewJ);
352 addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList);
353 return tesSUCCESS;
354}
355
356TER
358{
359 auto const accountKeylet = keylet::account(accountID_);
360 // Destroying the signer list is only allowed if either the master key
361 // is enabled or there is a regular key.
362 SLE::pointer const ledgerEntry = view().peek(accountKeylet);
363 if (!ledgerEntry)
364 return tefINTERNAL; // LCOV_EXCL_LINE
365
366 if ((ledgerEntry->isFlag(lsfDisableMaster)) && (!ledgerEntry->isFieldPresent(sfRegularKey)))
368
369 auto const ownerDirKeylet = keylet::ownerDir(accountID_);
370 auto const signerListKeylet = keylet::signerList(accountID_);
372 ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_);
373}
374
375void
377{
378 // Assign the quorum, default SignerListID, and flags.
379 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
380 {
381 ledgerEntry->setAccountID(sfOwner, accountID_);
382 }
383 ledgerEntry->setFieldU32(sfSignerQuorum, quorum_);
384 ledgerEntry->setFieldU32(sfSignerListID, kDefaultSignerListId);
385 if (flags != 0u) // Only set flags if they are non-default (default is zero).
386 ledgerEntry->setFieldU32(sfFlags, flags);
387
388 // Create the SignerListArray one SignerEntry at a time.
389 STArray toLedger(signers_.size());
390 for (auto const& entry : signers_)
391 {
392 toLedger.pushBack(STObject::makeInnerObject(sfSignerEntry));
393 STObject& obj = toLedger.back();
394 obj.reserve(2);
395 obj[sfAccount] = entry.account;
396 obj[sfSignerWeight] = entry.weight;
397
398 // This is a defensive check to make absolutely sure we will never write
399 // a tag into the ledger.
400 if (entry.tag)
401 obj.setFieldH256(sfWalletLocator, *(entry.tag));
402 }
403
404 // Assign the SignerEntries.
405 ledgerEntry->setFieldArray(sfSignerEntries, toLedger);
406}
407
408void
410{
411 // No transaction-specific invariants yet (future work).
412}
413
414bool
416 STTx const&,
417 TER,
418 XRPAmount,
419 ReadView const&,
420 beast::Journal const&)
421{
422 // No transaction-specific invariants yet (future work).
423 return true;
424}
425
426} // namespace xrpl
T adjacent_find(T... args)
A generic endpoint for log messages.
Definition Journal.h:44
Stream fatal() const
Definition Journal.h:368
Stream trace() const
Severity stream access functions.
Definition Journal.h:338
const_iterator begin() const
UInt size() const
Number of values in array or object.
const_iterator end() const
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:134
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void insert(SLE::ref sle)=0
Insert a new state SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void erase(SLE::ref sle)=0
Remove a peeked SLE.
A view into a ledger.
Definition ReadView.h:41
virtual Rules const & rules() const =0
Returns the tx processing rules.
Rules controlling protocol behavior.
Definition Rules.h:40
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:180
size_type size() const
Definition STArray.h:248
void pushBack(STObject const &object)
Definition STArray.h:212
STObject & back()
Definition STArray.h:193
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
void reserve(std::size_t n)
Definition STObject.h:982
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:79
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:643
void setFieldH256(SField const &field, uint256 const &)
Definition STObject.cpp:767
static constexpr std::size_t kMinMultiSigners
Definition STTx.h:46
static constexpr std::size_t kMaxMultiSigners
Definition STTx.h:47
Service registry for dependency injection.
virtual beast::Journal getJournal(std::string const &name)=0
static std::expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string_view annotation)
static NotTEC preflight(PreflightContext const &ctx)
void preCompute() override
static std::tuple< NotTEC, std::uint32_t, std::vector< SignerEntries::SignerEntry >, Operation > determineOperation(STTx const &tx, ApplyFlags flags, beast::Journal j)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static NotTEC validateQuorumAndSignerEntries(std::uint32_t quorum, std::vector< SignerEntries::SignerEntry > const &signers, AccountID const &account, beast::Journal j, Rules const &)
TER doApply() override
std::vector< SignerEntries::SignerEntry > signers_
static TER removeFromLedger(ServiceRegistry &registry, ApplyView &view, AccountID const &account, beast::Journal j)
void writeSignersToSLE(SLE::pointer const &ledgerEntry, std::uint32_t flags) const
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
beast::Journal const j_
Definition Transactor.h:155
ApplyView & view()
Definition Transactor.h:175
AccountID const accountID_
Definition Transactor.h:157
XRPAmount preFeeBalance_
Definition Transactor.h:158
virtual void preCompute()
ApplyContext & ctx_
Definition Transactor.h:153
T is_sorted(T... args)
T make_shared(T... args)
T make_tuple(T... args)
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:326
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:373
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void decreaseOwnerCountForObject(ApplyView &view, SLE::ref accountSle, SLE::ref objectSle, std::uint32_t count, beast::Journal j)
Decrease owner-count fields for an existing ledger object.
void increaseOwnerCount(ApplyView &view, SLE::ref accountSle, SLE::ref sponsorSle, std::uint32_t count, beast::Journal j)
Increase owner-count fields when the caller supplies the sponsor.
static std::uint32_t const kDefaultSignerListId
@ tefBAD_LEDGER
Definition TER.h:162
@ tefINTERNAL
Definition TER.h:165
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
void addSponsorToLedgerEntry(SLE::ref sle, SLE::const_ref sponsorSle, SF_ACCOUNT const &field=sfSponsor)
Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
static std::uint32_t signerCountBasedOwnerCountDelta(std::size_t entryCount, Rules const &rules)
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
ApplyFlags
Definition ApplyView.h:27
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
TER checkReserve(ApplyViewContext ctx, SLE::const_ref accSle, XRPAmount accBalance, SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j, TER insufReserveCode=tecINSUFFICIENT_RESERVE)
Check if an account has sufficient reserve.
@ temMALFORMED
Definition TER.h:75
@ temBAD_QUORUM
Definition TER.h:104
@ temBAD_SIGNER
Definition TER.h:103
@ temBAD_WEIGHT
Definition TER.h:105
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecDIR_FULL
Definition TER.h:290
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:299
constexpr FlagValue tfUniversalMask
Definition TxFlags.h:46
static TER removeSignersFromLedger(ServiceRegistry &registry, ApplyView &view, Keylet const &accountKeylet, Keylet const &ownerDirKeylet, Keylet const &signerListKeylet, beast::Journal j)
@ tesSUCCESS
Definition TER.h:245
T sort(T... args)
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:38
beast::Journal const j
Definition Transactor.h:45