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