add ExtendedHookState

This commit is contained in:
tequ
2024-12-11 22:38:50 +09:00
parent 532a471a35
commit 798366c9e0
6 changed files with 75 additions and 24 deletions

View File

@@ -1,3 +1,5 @@
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Rules.h>
#include <map>
#include <set>
#include <string>
@@ -28,6 +30,8 @@ enum HookEmissionFlags : uint16_t {
};
} // namespace ripple
using namespace ripple;
namespace hook {
// RH TODO: put these somewhere better, and allow rules to be fed in
inline uint32_t
@@ -43,8 +47,12 @@ maxHookParameterValueSize(void)
}
inline uint32_t
maxHookStateDataSize(void)
maxHookStateDataSize(Rules const& rules)
{
if (rules.enabled(featureExtendedHookState))
{
return 2048U;
}
return 256U;
}

View File

@@ -461,7 +461,9 @@ apply(
struct HookContext;
uint32_t
computeHookStateOwnerCount(uint32_t hookStateCount);
computeHookStateOwnerCount(Blob hookStateData);
uint32_t
computeHookStateOwnerCount(Slice hookStateData);
int64_t
computeExecutionFee(uint64_t instructionCount);

View File

@@ -853,9 +853,15 @@ parseCurrency(uint8_t* cu_ptr, uint32_t cu_len)
}
uint32_t
hook::computeHookStateOwnerCount(uint32_t hookStateCount)
hook::computeHookStateOwnerCount(Blob hookStateData)
{
return hookStateCount;
return std::floor((hookStateData.size() - 1) / 256) + 1;
}
uint32_t
hook::computeHookStateOwnerCount(Slice hookStateData)
{
return std::floor((hookStateData.size() - 1) / 256) + 1;
}
inline int64_t
@@ -1047,14 +1053,14 @@ hook::setHookState(
return tefINTERNAL;
// if the blob is too large don't set it
if (data.size() > hook::maxHookStateDataSize())
if (data.size() > hook::maxHookStateDataSize(view.rules()))
return temHOOK_DATA_TOO_LARGE;
auto hookStateKeylet = ripple::keylet::hookState(acc, key, ns);
auto hookStateDirKeylet = ripple::keylet::hookStateDir(acc, ns);
uint32_t stateCount = sleAccount->getFieldU32(sfHookStateCount);
uint32_t oldStateReserve = computeHookStateOwnerCount(stateCount);
uint32_t newHookStateCount = sleAccount->getFieldU32(sfHookStateCount);
uint32_t oldHookStateCount = newHookStateCount;
auto hookState = view.peek(hookStateKeylet);
@@ -1082,16 +1088,22 @@ hook::setHookState(
view.erase(hookState);
// adjust state object count
if (stateCount > 0)
--stateCount; // guard this because in the "impossible" event it is
// already 0 we'll wrap back to int_max
Blob deletedStateData = hookState->getFieldVL(sfHookStateData);
uint32_t deleteOwnerCount =
computeHookStateOwnerCount(deletedStateData);
if (newHookStateCount >= deleteOwnerCount)
{
newHookStateCount -=
deleteOwnerCount; // guard this because in the "impossible"
// event it is already 0 we'll wrap back to
// int_max
}
// if removing this state entry would destroy the allotment then reduce
// the owner count
if (computeHookStateOwnerCount(stateCount) < oldStateReserve)
adjustOwnerCount(view, sleAccount, -1, j);
if (newHookStateCount < oldHookStateCount)
adjustOwnerCount(view, sleAccount, -deleteOwnerCount, j);
sleAccount->setFieldU32(sfHookStateCount, stateCount);
sleAccount->setFieldU32(sfHookStateCount, newHookStateCount);
if (nsDestroyed)
hook::removeHookNamespaceEntry(*sleAccount, ns);
@@ -1116,30 +1128,54 @@ hook::setHookState(
if (createNew)
{
++stateCount;
uint32_t createdOwnerCount = computeHookStateOwnerCount(data);
newHookStateCount += createdOwnerCount;
if (computeHookStateOwnerCount(stateCount) > oldStateReserve)
if (newHookStateCount > oldHookStateCount)
{
// the hook used its allocated allotment of state entries for its
// previous ownercount increment ownercount and give it another
// allotment
++ownerCount;
ownerCount += createdOwnerCount;
XRPAmount const newReserve{view.fees().accountReserve(ownerCount)};
if (STAmount((*sleAccount)[sfBalance]).xrp() < newReserve)
return tecINSUFFICIENT_RESERVE;
adjustOwnerCount(view, sleAccount, 1, j);
adjustOwnerCount(view, sleAccount, createdOwnerCount, j);
}
// update state count
sleAccount->setFieldU32(sfHookStateCount, stateCount);
sleAccount->setFieldU32(sfHookStateCount, newHookStateCount);
view.update(sleAccount);
// create an entry
hookState = std::make_shared<SLE>(hookStateKeylet);
}
else
{
// on Update
uint32_t oldOwnerCount =
computeHookStateOwnerCount((*hookState)[sfHookStateData]);
uint32_t newOwnerCount = computeHookStateOwnerCount(data);
uint32_t changedOwnerCount = newOwnerCount - oldOwnerCount;
newHookStateCount += changedOwnerCount;
if (changedOwnerCount != 0)
{
ownerCount += changedOwnerCount;
XRPAmount const newReserve{view.fees().accountReserve(ownerCount)};
if (STAmount((*sleAccount)[sfBalance]).xrp() < newReserve)
return tecINSUFFICIENT_RESERVE;
adjustOwnerCount(view, sleAccount, changedOwnerCount, j);
sleAccount->setFieldU32(sfHookStateCount, newHookStateCount);
view.update(sleAccount);
}
}
hookState->setFieldVL(sfHookStateData, data);
hookState->setFieldH256(sfHookStateKey, key);
@@ -1634,7 +1670,7 @@ DEFINE_HOOK_FUNCTION(
(aread_len && NOT_IN_BOUNDS(aread_ptr, aread_len, memory_length)))
return OUT_OF_BOUNDS;
uint32_t maxSize = hook::maxHookStateDataSize();
uint32_t maxSize = hook::maxHookStateDataSize(view.rules());
if (read_len > maxSize)
return TOO_BIG;

View File

@@ -877,6 +877,7 @@ SetHook::destroyNamespace(
} while (cdirNext(view, dirKeylet.key, sleDirNode, uDirEntry, dirEntry));
uint32_t toDeleteOwnerCount = 0;
// delete it!
for (auto const& itemKey : toDelete)
{
@@ -892,6 +893,9 @@ SetHook::destroyNamespace(
continue;
}
toDeleteOwnerCount +=
hook::computeHookStateOwnerCount((*sleItem)[sfHookStateData]);
auto const hint = (*sleItem)[sfOwnerNode];
if (!view.dirRemove(dirKeylet, hint, itemKey, false))
{
@@ -905,7 +909,7 @@ SetHook::destroyNamespace(
view.erase(sleItem);
}
uint32_t stateCount = oldStateCount - toDelete.size();
uint32_t stateCount = oldStateCount - toDeleteOwnerCount;
if (stateCount > oldStateCount)
{
JLOG(ctx.j.fatal()) << "HookSet(" << hook::log::NSDELETE_COUNT << ")["
@@ -921,7 +925,7 @@ SetHook::destroyNamespace(
sleAccount->setFieldU32(sfHookStateCount, stateCount);
if (ctx.rules.enabled(fixNSDelete))
adjustOwnerCount(view, sleAccount, -toDelete.size(), ctx.j);
adjustOwnerCount(view, sleAccount, -toDeleteOwnerCount, ctx.j);
if (!partialDelete && sleAccount->isFieldPresent(sfHookNamespaces))
hook::removeHookNamespaceEntry(*sleAccount, ns);

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 75;
static constexpr std::size_t numFeatures = 76;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -363,7 +363,7 @@ extern uint256 const fixPageCap;
extern uint256 const fix240911;
extern uint256 const fixFloatDivide;
extern uint256 const fixReduceImport;
extern uint256 const featureExtendedHookState;
} // namespace ripple
#endif

View File

@@ -469,6 +469,7 @@ REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::De
REGISTER_FIX (fix240911, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixFloatDivide, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixReduceImport, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.