Clio’s "accountHolds" implementation checks whether a trustline is ordinarily frozen, but does not check whether it is deep-frozen.
The equivalent logic in "rippled" checks both conditions:
if (isFrozen(view, account, currency, issuer) ||
isDeepFrozen(view, account, currency, issuer))
{
return nullptr;
}
Clio’s nearby "ammAccountHolds" implementation also checks both "isFrozen" and "isDeepFrozen", which suggests the missing check in "accountHolds" is an oversight.
Current Reachability
At present, "rippled" prevents a trustline from being deep-frozen without also being ordinarily frozen. Because of that invariant, the existing "isFrozen" check normally masks this issue on current ledger state.
This issue is therefore a consistency and defensive-correctness gap rather than a currently exploitable Mainnet bug.
If the invariant changes in the future, or ledger data containing only a deep-freeze flag is processed, "accountHolds" could incorrectly return a non-zero balance.
Affected Paths
"accountHolds" is used when calculating balances and offer funding for paths including:
- "book_offers"
- Expanded "ledger" responses
- "amm_info"
- "accountFunds"
Suggested Fix
Update the freeze gate in "accountHolds":
if (isFrozen(backend, sequence, account, currency, issuer, yield) ||
isDeepFrozen(backend, sequence, account, currency, issuer, yield))
{
return false;
}
A unit test should also cover a trustline that has only "lsfLowDeepFreeze" or "lsfHighDeepFreeze" set and verify that "accountHolds" returns zero.
Clio’s "accountHolds" implementation checks whether a trustline is ordinarily frozen, but does not check whether it is deep-frozen.
The equivalent logic in "rippled" checks both conditions:
if (isFrozen(view, account, currency, issuer) ||
isDeepFrozen(view, account, currency, issuer))
{
return nullptr;
}
Clio’s nearby "ammAccountHolds" implementation also checks both "isFrozen" and "isDeepFrozen", which suggests the missing check in "accountHolds" is an oversight.
Current Reachability
At present, "rippled" prevents a trustline from being deep-frozen without also being ordinarily frozen. Because of that invariant, the existing "isFrozen" check normally masks this issue on current ledger state.
This issue is therefore a consistency and defensive-correctness gap rather than a currently exploitable Mainnet bug.
If the invariant changes in the future, or ledger data containing only a deep-freeze flag is processed, "accountHolds" could incorrectly return a non-zero balance.
Affected Paths
"accountHolds" is used when calculating balances and offer funding for paths including:
Suggested Fix
Update the freeze gate in "accountHolds":
if (isFrozen(backend, sequence, account, currency, issuer, yield) ||
isDeepFrozen(backend, sequence, account, currency, issuer, yield))
{
return false;
}
A unit test should also cover a trustline that has only "lsfLowDeepFreeze" or "lsfHighDeepFreeze" set and verify that "accountHolds" returns zero.