mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-22 03:55:53 +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_DeleteFinal = 0x04, // final value when it is deleted
|
||||||
sMD_Create = 0x08, // value when it's created
|
sMD_Create = 0x08, // value when it's created
|
||||||
sMD_Always = 0x10, // value when node containing it is affected at all
|
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_Default =
|
||||||
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
|
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -187,7 +187,8 @@ TYPED_SFIELD(sfNFTokenID, UINT256, 10)
|
|||||||
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
|
TYPED_SFIELD(sfEmitParentTxnID, UINT256, 11)
|
||||||
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
|
TYPED_SFIELD(sfEmitNonce, UINT256, 12)
|
||||||
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
|
TYPED_SFIELD(sfEmitHookHash, UINT256, 13)
|
||||||
TYPED_SFIELD(sfAMMID, UINT256, 14)
|
TYPED_SFIELD(sfAMMID, UINT256, 14,
|
||||||
|
SField::sMD_PseudoAccount |SField::sMD_Default)
|
||||||
|
|
||||||
// 256-bit (uncommon)
|
// 256-bit (uncommon)
|
||||||
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
|
TYPED_SFIELD(sfBookDirectory, UINT256, 16)
|
||||||
@@ -209,8 +210,10 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
|
|||||||
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
||||||
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||||
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
TYPED_SFIELD(sfDomainID, UINT256, 34)
|
||||||
TYPED_SFIELD(sfVaultID, UINT256, 35)
|
TYPED_SFIELD(sfVaultID, UINT256, 35,
|
||||||
TYPED_SFIELD(sfLoanBrokerID, UINT256, 36)
|
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||||
|
TYPED_SFIELD(sfLoanBrokerID, UINT256, 36,
|
||||||
|
SField::sMD_PseudoAccount | SField::sMD_Default)
|
||||||
TYPED_SFIELD(sfLoanID, UINT256, 37)
|
TYPED_SFIELD(sfLoanID, UINT256, 37)
|
||||||
|
|
||||||
// number (common)
|
// number (common)
|
||||||
|
|||||||
@@ -1723,4 +1723,95 @@ NoModifiedUnmodifiableFields::finalize(
|
|||||||
return true;
|
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
|
} // namespace ripple
|
||||||
|
|||||||
@@ -645,6 +645,34 @@ public:
|
|||||||
beast::Journal const&);
|
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
|
// additional invariant checks can be declared above and then added to this
|
||||||
// tuple
|
// tuple
|
||||||
using InvariantChecks = std::tuple<
|
using InvariantChecks = std::tuple<
|
||||||
@@ -665,7 +693,8 @@ using InvariantChecks = std::tuple<
|
|||||||
ValidClawback,
|
ValidClawback,
|
||||||
ValidMPTIssuance,
|
ValidMPTIssuance,
|
||||||
ValidPermissionedDomain,
|
ValidPermissionedDomain,
|
||||||
NoModifiedUnmodifiableFields>;
|
NoModifiedUnmodifiableFields,
|
||||||
|
ValidPseudoAccounts>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief get a tuple of all invariant checks
|
* @brief get a tuple of all invariant checks
|
||||||
|
|||||||
@@ -534,6 +534,17 @@ createPseudoAccount(
|
|||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isPseudoAccount(std::shared_ptr<SLE const> sleAcct);
|
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
|
[[nodiscard]] inline bool
|
||||||
isPseudoAccount(ReadView const& view, AccountID accountId)
|
isPseudoAccount(ReadView const& view, AccountID accountId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1061,28 +1061,45 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
|
|||||||
return beast::zero;
|
return beast::zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note, the list of the pseudo-account designator fields below MUST be
|
// Pseudo-account designator fields MUST be maintained by including the
|
||||||
// maintained but it does NOT need to be amendment-gated, since a
|
// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
|
||||||
// non-active amendment will not set any field, by definition. Specific
|
// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
|
||||||
// properties of a pseudo-account are NOT checked here, that's what
|
// 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.
|
// InvariantCheck is for.
|
||||||
static std::array<SField const*, 2> const pseudoAccountOwnerFields = {
|
[[nodiscard]] std::vector<SField const*> const&
|
||||||
&sfAMMID, //
|
getPseudoAccountFields()
|
||||||
&sfVaultID, //
|
{
|
||||||
};
|
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
|
[[nodiscard]] bool
|
||||||
isPseudoAccount(std::shared_ptr<SLE const> sleAcct)
|
isPseudoAccount(std::shared_ptr<SLE const> sleAcct)
|
||||||
{
|
{
|
||||||
// auto const acctFields =
|
std::vector<SField const*> const& fields = getPseudoAccountFields();
|
||||||
// LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
|
|
||||||
|
|
||||||
// Intentionally use defensive coding here because it's cheap and makes the
|
// Intentionally use defensive coding here because it's cheap and makes the
|
||||||
// semantics of true return value clean.
|
// semantics of true return value clean.
|
||||||
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
|
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
|
||||||
std::count_if(
|
std::count_if(
|
||||||
pseudoAccountOwnerFields.begin(),
|
fields.begin(),
|
||||||
pseudoAccountOwnerFields.end(),
|
fields.end(),
|
||||||
[&sleAcct](SField const* sf) -> bool {
|
[&sleAcct](SField const* sf) -> bool {
|
||||||
return sleAcct->isFieldPresent(*sf);
|
return sleAcct->isFieldPresent(*sf);
|
||||||
}) > 0;
|
}) > 0;
|
||||||
@@ -1095,10 +1112,11 @@ createPseudoAccount(
|
|||||||
uint256 const& pseudoOwnerKey,
|
uint256 const& pseudoOwnerKey,
|
||||||
SField const& ownerField)
|
SField const& ownerField)
|
||||||
{
|
{
|
||||||
|
std::vector<SField const*> const& fields = getPseudoAccountFields();
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
std::count_if(
|
std::count_if(
|
||||||
pseudoAccountOwnerFields.begin(),
|
fields.begin(),
|
||||||
pseudoAccountOwnerFields.end(),
|
fields.end(),
|
||||||
[&ownerField](SField const* sf) -> bool {
|
[&ownerField](SField const* sf) -> bool {
|
||||||
return *sf == ownerField;
|
return *sf == ownerField;
|
||||||
}) == 1,
|
}) == 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user