fix: Check credential for LoanBrokerCoverWithdraw and VaultWithdraw (#7107)

Co-authored-by: Peter Chen <ychen@ripple.com>
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
This commit is contained in:
Shawn Xie
2026-08-20 15:33:46 +00:00
committed by GitHub
parent 959a186a0f
commit e3ba569187
12 changed files with 445 additions and 13 deletions

View File

@@ -24,6 +24,7 @@
#include <optional>
#include <set>
#include <utility>
#include <vector>
namespace xrpl {
@@ -198,7 +199,10 @@ dirLink(
* if withdrawing to self.
* - If withdrawing to self, succeed.
* - If not, checks if the receiver requires deposit authorization, and if
* the sender has it.
* the sender has it (account-based or credential-based).
* - Expects any credentials passed in to already exist in the ledger, and
* returns an internal error otherwise. Validate them beforehand with
* credentials::valid().
* - Checks that the receiver will not exceed the limit (IOU trustline limit
* or MPT MaximumAmount).
*/
@@ -209,7 +213,8 @@ canWithdraw(
AccountID const& to,
SLE::const_ref toSle,
STAmount const& amount,
bool hasDestinationTag);
bool hasDestinationTag,
std::optional<std::vector<uint256>> const& credentialIDs = std::nullopt);
/**
* Checks that can withdraw funds from an object to itself or a destination.
@@ -222,7 +227,10 @@ canWithdraw(
* if withdrawing to self.
* - If withdrawing to self, succeed.
* - If not, checks if the receiver requires deposit authorization, and if
* the sender has it.
* the sender has it (account-based or credential-based).
* - Expects any credentials passed in to already exist in the ledger, and
* returns an internal error otherwise. Validate them beforehand with
* credentials::valid().
* - Checks that the receiver will not exceed the limit (IOU trustline limit
* or MPT MaximumAmount).
*/
@@ -232,20 +240,25 @@ canWithdraw(
AccountID const& from,
AccountID const& to,
STAmount const& amount,
bool hasDestinationTag);
bool hasDestinationTag,
std::optional<std::vector<uint256>> const& credentialIDs = std::nullopt);
/**
* Checks that can withdraw funds from an object to itself or a destination.
*
* The receiver may be either the submitting account (sfAccount) or a different
* destination account (sfDestination).
* destination account (sfDestination). Credentials, if any, are taken from the
* transaction's sfCredentialIDs field.
*
* - Checks that the receiver account exists.
* - If the receiver requires a destination tag, check that one exists, even
* if withdrawing to self.
* - If withdrawing to self, succeed.
* - If not, checks if the receiver requires deposit authorization, and if
* the sender has it.
* the sender has it (account-based or credential-based).
* - Expects any credentials in sfCredentialIDs to already exist in the
* ledger, and returns an internal error otherwise. Validate them
* beforehand with credentials::valid().
* - Checks that the receiver will not exceed the limit (IOU trustline limit
* or MPT MaximumAmount).
*/

View File

@@ -921,6 +921,7 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
{sfAmount, SoeRequired, SoeMptSupported},
{sfDestination, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfCredentialIDs, SoeOptional},
}))
/** This transaction claws back tokens from a vault. */
@@ -1004,6 +1005,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
{sfAmount, SoeRequired, SoeMptSupported},
{sfDestination, SoeOptional},
{sfDestinationTag, SoeOptional},
{sfCredentialIDs, SoeOptional},
}))
/** This transaction claws back First Loss Capital from a Loan Broker to

View File

@@ -121,6 +121,32 @@ public:
{
return this->tx_->isFieldPresent(sfDestinationTag);
}
/**
* @brief Get sfCredentialIDs (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
getCredentialIDs() const
{
if (hasCredentialIDs())
{
return this->tx_->at(sfCredentialIDs);
}
return std::nullopt;
}
/**
* @brief Check if sfCredentialIDs is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasCredentialIDs() const
{
return this->tx_->isFieldPresent(sfCredentialIDs);
}
};
/**
@@ -214,6 +240,17 @@ public:
return *this;
}
/**
* @brief Set sfCredentialIDs (SoeOptional)
* @return Reference to this builder for method chaining.
*/
LoanBrokerCoverWithdrawBuilder&
setCredentialIDs(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfCredentialIDs] = value;
return *this;
}
/**
* @brief Build and return the LoanBrokerCoverWithdraw wrapper.
* @param publicKey The public key for signing.

View File

@@ -121,6 +121,32 @@ public:
{
return this->tx_->isFieldPresent(sfDestinationTag);
}
/**
* @brief Get sfCredentialIDs (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VECTOR256::type::value_type>
getCredentialIDs() const
{
if (hasCredentialIDs())
{
return this->tx_->at(sfCredentialIDs);
}
return std::nullopt;
}
/**
* @brief Check if sfCredentialIDs is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasCredentialIDs() const
{
return this->tx_->isFieldPresent(sfCredentialIDs);
}
};
/**
@@ -214,6 +240,17 @@ public:
return *this;
}
/**
* @brief Set sfCredentialIDs (SoeOptional)
* @return Reference to this builder for method chaining.
*/
VaultWithdrawBuilder&
setCredentialIDs(std::decay_t<typename SF_VECTOR256::type::value_type> const& value)
{
object_[sfCredentialIDs] = value;
return *this;
}
/**
* @brief Build and return the VaultWithdraw wrapper.
* @param publicKey The public key for signing.

View File

@@ -20,6 +20,9 @@ public:
{
}
static bool
checkExtraFeatures(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);