mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Pseudo-account improvements
- Define pseudo-account fields with an sfield flag - Pseudo-account invariant checks rules whenever a pseudo-account is created or modified.
This commit is contained in:
@@ -149,7 +149,10 @@ public:
|
||||
sMD_DeleteFinal = 0x04, // final value when it is deleted
|
||||
sMD_Create = 0x08, // value when it's created
|
||||
sMD_Always = 0x10, // value when node containing it is affected at all
|
||||
sMD_BaseTen = 0x20,
|
||||
sMD_BaseTen =
|
||||
0x20, // value is treated as base 10, overriding default behavior
|
||||
sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT
|
||||
// _only_, then it is a pseudo-account
|
||||
sMD_Default =
|
||||
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
|
||||
};
|
||||
|
||||
@@ -187,7 +187,8 @@ TYPED_SFIELD(sfNFTokenID, UINT256, 10)
|
||||
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
|
||||
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
|
||||
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
|
||||
TYPED_SFIELD(sfAMMID, UINT256, 14)
|
||||
TYPED_SFIELD(sfAMMID, UINT256, 14,
|
||||
SField::sMD_PseudoAccount |SField::sMD_Default)
|
||||
|
||||
// 256-bit (uncommon)
|
||||
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
|
||||
@@ -209,8 +210,10 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
|
||||
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
||||
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
||||
TYPED_SFIELD(sfVaultID, UINT256, 35)
|
||||
TYPED_SFIELD(sfLoanBrokerID, UINT256, 36)
|
||||
TYPED_SFIELD(sfVaultID, UINT256, 35,
|
||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfLoanBrokerID, UINT256, 36,
|
||||
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||
TYPED_SFIELD(sfLoanID, UINT256, 37)
|
||||
|
||||
// number (common)
|
||||
|
||||
@@ -1723,4 +1723,95 @@ NoModifiedUnmodifiableFields::finalize(
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
ValidPseudoAccounts::visitEntry(
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after)
|
||||
{
|
||||
if (isDelete)
|
||||
// Deletion is ignored
|
||||
return;
|
||||
|
||||
if (after && after->getType() == ltACCOUNT_ROOT)
|
||||
{
|
||||
bool const isPseudo = [&]() {
|
||||
// isPseudoAccount checks that any of the pseudo-account fields are
|
||||
// set.
|
||||
if (isPseudoAccount(after))
|
||||
return true;
|
||||
// Not all pseudo-accounts have a zero sequence, but all accounts
|
||||
// with a zero sequence had better be pseudo-accounts.
|
||||
if (after->at(sfSequence) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
if (isPseudo)
|
||||
{
|
||||
// Pseudo accounts must have the following properties:
|
||||
// 1. Exactly one of the pseudo-account fields is set.
|
||||
// 2. The sequence number is not changed.
|
||||
// 3. The lsfDisableMaster, lsfDefaultRipple, and lsfDepositAuth
|
||||
// flags are set.
|
||||
// 4. The RegularKey is not set.
|
||||
{
|
||||
std::vector<SField const*> const& fields =
|
||||
getPseudoAccountFields();
|
||||
|
||||
auto const numFields = std::count_if(
|
||||
fields.begin(),
|
||||
fields.end(),
|
||||
[&after](SField const* sf) -> bool {
|
||||
return after->isFieldPresent(*sf);
|
||||
});
|
||||
if (numFields != 1)
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "pseudo-account has " << numFields
|
||||
<< "pseudo-account fields set";
|
||||
errors_.emplace_back(error.str());
|
||||
}
|
||||
}
|
||||
if (before && before->at(sfSequence) != after->at(sfSequence))
|
||||
{
|
||||
errors_.emplace_back("pseudo-account sequence changed");
|
||||
}
|
||||
if (!after->isFlag(
|
||||
lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth))
|
||||
{
|
||||
errors_.emplace_back(
|
||||
"Invariant failed: pseudo-account flags are not set");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ValidPseudoAccounts::finalize(
|
||||
STTx const& tx,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
bool const enforce = view.rules().enabled(featureLendingProtocol);
|
||||
XRPL_ASSERT(
|
||||
errors_.empty() || enforce,
|
||||
"ripple::ValidPseudoAccounts::finalize : no bad "
|
||||
"changes or enforce invariant");
|
||||
if (!errors_.empty())
|
||||
{
|
||||
for (auto const& error : errors_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: " << error;
|
||||
}
|
||||
if (enforce)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -645,6 +645,34 @@ public:
|
||||
beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Pseudo-accounts have
|
||||
*
|
||||
* Pseudo-accounts have certain properties, and some of those properties are
|
||||
* unique to pseudo-accounts. Check that all pseudo-accounts are following the
|
||||
* rules, and that only pseudo-accounts look like pseudo-accounts.
|
||||
*
|
||||
*/
|
||||
class ValidPseudoAccounts
|
||||
{
|
||||
std::vector<std::string> errors_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(
|
||||
bool,
|
||||
std::shared_ptr<SLE const> const&,
|
||||
std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const&);
|
||||
};
|
||||
|
||||
// additional invariant checks can be declared above and then added to this
|
||||
// tuple
|
||||
using InvariantChecks = std::tuple<
|
||||
@@ -665,7 +693,8 @@ using InvariantChecks = std::tuple<
|
||||
ValidClawback,
|
||||
ValidMPTIssuance,
|
||||
ValidPermissionedDomain,
|
||||
NoModifiedUnmodifiableFields>;
|
||||
NoModifiedUnmodifiableFields,
|
||||
ValidPseudoAccounts>;
|
||||
|
||||
/**
|
||||
* @brief get a tuple of all invariant checks
|
||||
|
||||
@@ -534,6 +534,17 @@ createPseudoAccount(
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(std::shared_ptr<SLE const> sleAcct);
|
||||
|
||||
// Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if
|
||||
// set
|
||||
// Pseudo-account designator fields MUST be maintained by including the
|
||||
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
|
||||
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
|
||||
// since a non-active amendment will not set any field, by definition.
|
||||
// Specific properties of a pseudo-account are NOT checked here, that's what
|
||||
// InvariantCheck is for.
|
||||
[[nodiscard]] std::vector<SField const*> const&
|
||||
getPseudoAccountFields();
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
isPseudoAccount(ReadView const& view, AccountID accountId)
|
||||
{
|
||||
|
||||
@@ -1061,28 +1061,45 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
|
||||
return beast::zero;
|
||||
}
|
||||
|
||||
// Note, the list of the pseudo-account designator fields below MUST be
|
||||
// maintained but it does NOT need to be amendment-gated, since a
|
||||
// non-active amendment will not set any field, by definition. Specific
|
||||
// properties of a pseudo-account are NOT checked here, that's what
|
||||
// Pseudo-account designator fields MUST be maintained by including the
|
||||
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
|
||||
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
|
||||
// since a non-active amendment will not set any field, by definition.
|
||||
// Specific properties of a pseudo-account are NOT checked here, that's what
|
||||
// InvariantCheck is for.
|
||||
static std::array<SField const*, 2> const pseudoAccountOwnerFields = {
|
||||
&sfAMMID, //
|
||||
&sfVaultID, //
|
||||
};
|
||||
[[nodiscard]] std::vector<SField const*> const&
|
||||
getPseudoAccountFields()
|
||||
{
|
||||
static std::vector<SField const*> const pseudoFields = []() {
|
||||
auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
|
||||
if (!ar)
|
||||
LogicError(
|
||||
"ripple::isPseudoAccount : unable to find account root ledger "
|
||||
"format");
|
||||
auto const& soTemplate = ar->getSOTemplate();
|
||||
|
||||
std::vector<SField const*> pseudoFields;
|
||||
for (auto const& field : soTemplate)
|
||||
{
|
||||
if (field.sField().shouldMeta(SField::sMD_PseudoAccount))
|
||||
pseudoFields.emplace_back(&field.sField());
|
||||
}
|
||||
return pseudoFields;
|
||||
}();
|
||||
return pseudoFields;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(std::shared_ptr<SLE const> sleAcct)
|
||||
{
|
||||
// auto const acctFields =
|
||||
// LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
|
||||
std::vector<SField const*> const& fields = getPseudoAccountFields();
|
||||
|
||||
// Intentionally use defensive coding here because it's cheap and makes the
|
||||
// semantics of true return value clean.
|
||||
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
|
||||
std::count_if(
|
||||
pseudoAccountOwnerFields.begin(),
|
||||
pseudoAccountOwnerFields.end(),
|
||||
fields.begin(),
|
||||
fields.end(),
|
||||
[&sleAcct](SField const* sf) -> bool {
|
||||
return sleAcct->isFieldPresent(*sf);
|
||||
}) > 0;
|
||||
@@ -1095,10 +1112,11 @@ createPseudoAccount(
|
||||
uint256 const& pseudoOwnerKey,
|
||||
SField const& ownerField)
|
||||
{
|
||||
std::vector<SField const*> const& fields = getPseudoAccountFields();
|
||||
XRPL_ASSERT(
|
||||
std::count_if(
|
||||
pseudoAccountOwnerFields.begin(),
|
||||
pseudoAccountOwnerFields.end(),
|
||||
fields.begin(),
|
||||
fields.end(),
|
||||
[&ownerField](SField const* sf) -> bool {
|
||||
return *sf == ownerField;
|
||||
}) == 1,
|
||||
|
||||
Reference in New Issue
Block a user