xrpld
Loading...
Searching...
No Matches
ConfidentialMPTHolderKeyUpdate.cpp
1#include <xrpl/tx/transactors/token/ConfidentialMPTHolderKeyUpdate.h>
2
3#include <xrpl/basics/Slice.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/ReadView.h>
8#include <xrpl/protocol/ConfidentialTransfer.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/LedgerFormats.h>
12#include <xrpl/protocol/MPTIssue.h>
13#include <xrpl/protocol/Protocol.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/TER.h>
16#include <xrpl/protocol/TxFlags.h>
17#include <xrpl/protocol/XRPAmount.h>
18#include <xrpl/tx/Transactor.h>
19
20#include <bit>
21#include <cstdint>
22
23namespace xrpl {
24
25bool
27{
28 // Holder key update is only meaningful when confidential transfers are enabled.
29 return ctx.rules.enabled(featureConfidentialTransfer);
30}
31
34{
35 return tfConfidentialMPTHolderKeyUpdateMask;
36}
37
40{
41 bool const rotation = ctx.tx.isFlag(tfHolderKeyRotation);
42 bool const recovery = ctx.tx.isFlag(tfHolderKeyRecovery);
43 bool const cancel = ctx.tx.isFlag(tfCancelRecovery);
44
45 // Exactly one of the three mode flags must be set.
46 static constexpr auto modeFlags = tfHolderKeyRotation | tfHolderKeyRecovery | tfCancelRecovery;
47 if (std::popcount(ctx.tx.getFlags() & modeFlags) != 1)
48 return temINVALID_FLAG;
49
50 // The issuer cannot hold confidential balances.
51 if (ctx.tx[sfAccount] == MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer())
52 return temMALFORMED;
53
54 bool const hasHolderKey = ctx.tx.isFieldPresent(sfHolderEncryptionKey);
55 bool const hasSpending = ctx.tx.isFieldPresent(sfConfidentialBalanceSpending);
56 bool const hasInbox = ctx.tx.isFieldPresent(sfConfidentialBalanceInbox);
57 bool const hasProof = ctx.tx.isFieldPresent(sfZKProof);
58
59 if (cancel)
60 {
61 // Cancel mode only revokes a pending recovery authorization; it
62 // carries no key material, balances, or proof.
63 if (hasHolderKey || hasSpending || hasInbox || hasProof)
64 return temMALFORMED;
65
66 return tesSUCCESS;
67 }
68
69 // Rotation and Recovery both require a holder key and a proof.
70 if (!hasHolderKey || !hasProof)
71 return temMALFORMED;
72
73 // Rotation mode requires re-encrypted balances; Recovery mode must not
74 // provide them since the holder cannot decrypt the current ones.
75 if (rotation && (!hasSpending || !hasInbox))
76 return temMALFORMED;
77
78 if (recovery && (hasSpending || hasInbox))
79 return temMALFORMED;
80
81 if (hasSpending &&
82 ctx.tx[sfConfidentialBalanceSpending].length() != kEcGamalEncryptedTotalLength)
83 return temBAD_CIPHERTEXT;
84
85 if (hasInbox && ctx.tx[sfConfidentialBalanceInbox].length() != kEcGamalEncryptedTotalLength)
86 return temBAD_CIPHERTEXT;
87
88 if (!isValidCompressedECPoint(ctx.tx[sfHolderEncryptionKey]))
89 return temMALFORMED;
90
91 if (hasSpending && !isValidCiphertext(ctx.tx[sfConfidentialBalanceSpending]))
92 return temBAD_CIPHERTEXT;
93
94 if (hasInbox && !isValidCiphertext(ctx.tx[sfConfidentialBalanceInbox]))
95 return temBAD_CIPHERTEXT;
96
97 return tesSUCCESS;
98}
99
105
106TER
108{
109 auto const account = ctx.tx[sfAccount];
110 auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
111
112 auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
113 if (!sleIssuance)
114 return tecOBJECT_NOT_FOUND;
115
116 if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
117 return tecNO_PERMISSION;
118
119 auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
120 if (!sleMptoken)
121 return tecOBJECT_NOT_FOUND;
122
123 if (!sleMptoken->isFieldPresent(sfHolderEncryptionKey) ||
124 !sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
125 !sleMptoken->isFieldPresent(sfConfidentialBalanceInbox))
126 {
127 return tecNO_PERMISSION;
128 }
129
130 if (ctx.tx.isFlag(tfCancelRecovery))
131 {
132 // Nothing to cancel if no recovery is pending.
133 if (!sleMptoken->isFieldPresent(sfRecoveryKey))
134 return tecNO_PERMISSION;
135
136 return tesSUCCESS;
137 }
138
139 if (ctx.tx[sfHolderEncryptionKey] == (*sleMptoken)[sfHolderEncryptionKey])
140 return tecDUPLICATE;
141
142 // Recovery mode: reject if a recovery is already pending
143 if (ctx.tx.isFlag(tfHolderKeyRecovery) && sleMptoken->isFieldPresent(sfRecoveryKey))
144 return tecNO_PERMISSION;
145
146 return tesSUCCESS;
147}
148
149TER
151{
152 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
153 auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
154 if (!sleMptoken)
155 {
156 // LCOV_EXCL_START
157 UNREACHABLE(
158 "xrpl::ConfidentialMPTHolderKeyUpdate::doApply : preclaim already validated the "
159 "MPToken exists");
160 return tecINTERNAL;
161 // LCOV_EXCL_STOP
162 }
163
164 if (ctx_.tx.isFlag(tfCancelRecovery))
165 {
166 // The holder revokes their pending recovery authorization; the
167 // current key and balances are left untouched.
168 sleMptoken->makeFieldAbsent(sfRecoveryKey);
169 view().update(sleMptoken);
170 return tesSUCCESS;
171 }
172
173 auto const newPubKey = ctx_.tx[sfHolderEncryptionKey];
174
175 if (ctx_.tx.isFlag(tfHolderKeyRotation))
176 {
177 // Replace the key and balances in rotation mode. Rotation proves the
178 // holder still has the old key, so any pending recovery is cancelled.
179 (*sleMptoken)[sfHolderEncryptionKey] = newPubKey;
180 (*sleMptoken)[sfConfidentialBalanceSpending] = ctx_.tx[sfConfidentialBalanceSpending];
181 (*sleMptoken)[sfConfidentialBalanceInbox] = ctx_.tx[sfConfidentialBalanceInbox];
182 sleMptoken->makeFieldAbsent(sfRecoveryKey);
183 incrementConfidentialVersion(*sleMptoken);
184 }
185 else if (ctx_.tx.isFlag(tfHolderKeyRecovery))
186 {
187 // Recovery mode: the holder cannot decrypt their current balances,
188 // so only the pending recovery key is recorded. The balances are
189 // rewritten separately by the issuer via ConfidentialMPTRecoverBalance.
190 (*sleMptoken)[sfRecoveryKey] = newPubKey;
191 }
192 else
193 {
194 // LCOV_EXCL_START
195 UNREACHABLE("xrpl::ConfidentialMPTHolderKeyUpdate::doApply : invalid mode");
196 return tecINTERNAL;
197 // LCOV_EXCL_STOP
198 }
199
200 view().update(sleMptoken);
201 return tesSUCCESS;
202}
203
204void
208
209bool
211 STTx const&,
212 TER,
213 XRPAmount,
214 ReadView const&,
215 beast::Journal const&)
216{
217 return true;
218}
219
220} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void update(SLE::Ref sle)=0
Indicate changes to a peeked SLE.
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
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 XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
AccountID const & getIssuer() const
Definition MPTIssue.cpp:29
A view into a ledger.
Definition ReadView.h:41
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:182
std::shared_ptr< STLedgerEntry const > const & ConstRef
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
std::uint32_t getFlags() const
Definition STObject.cpp:517
ApplyView & view()
Definition Transactor.h:184
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
AccountID const accountID_
Definition Transactor.h:166
ApplyContext & ctx_
Definition Transactor.h:162
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:573
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr std::uint32_t kConfidentialFeeMultiplier
Extra base fee multiplier charged to confidential MPT transactions.
Definition Protocol.h:551
bool isValidCompressedECPoint(Slice const &buffer)
Verifies that a buffer contains a valid, parsable compressed EC point.
constexpr std::size_t kEcGamalEncryptedTotalLength
EC ElGamal ciphertext length: two compressed EC points concatenated.
Definition Protocol.h:480
bool isValidCiphertext(Slice const &buffer)
Verifies that a buffer contains two valid, parsable EC public keys.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
@ temBAD_CIPHERTEXT
Definition TER.h:134
@ temINVALID_FLAG
Definition TER.h:99
@ temMALFORMED
Definition TER.h:75
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecINTERNAL
Definition TER.h:318
@ tecNO_PERMISSION
Definition TER.h:313
@ tecDUPLICATE
Definition TER.h:323
void incrementConfidentialVersion(STObject &mptoken)
Increments the confidential balance version counter on an MPToken.
@ tesSUCCESS
Definition TER.h:250
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
State information when preflighting a tx.
Definition Transactor.h:39