state reserve counting

This commit is contained in:
Richard Holland
2022-05-23 10:19:44 +00:00
parent 88d747a7f7
commit f74cc56acd
3 changed files with 42 additions and 14 deletions

View File

@@ -212,7 +212,8 @@ namespace hook_api
NOT_AUTHORIZED = -34, NOT_AUTHORIZED = -34,
PREVIOUS_FAILURE_PREVENTS_RETRY = -35, PREVIOUS_FAILURE_PREVENTS_RETRY = -35,
TOO_MANY_PARAMS = -36, TOO_MANY_PARAMS = -36,
INVALID_TXN = -37 INVALID_TXN = -37,
RESERVE_INSUFFICIENT = -38 // setting a new state object would exceed account reserve
}; };
enum ExitType : uint8_t enum ExitType : uint8_t

View File

@@ -19,7 +19,6 @@ using GuardLog = std::optional<std::reference_wrapper<std::basic_ostream<char>>>
else\ else\
(*guardLog).get() << "HookSet(" << logCode << ")[" << guardLogAccStr << "]: " (*guardLog).get() << "HookSet(" << logCode << ")[" << guardLogAccStr << "]: "
// RH TODO test overflow on leb128 detection
// web assembly contains a lot of run length encoding in LEB128 format // web assembly contains a lot of run length encoding in LEB128 format
inline uint64_t inline uint64_t
parseLeb128( parseLeb128(

View File

@@ -953,19 +953,23 @@ lookup_state_cache(
// update the state cache // update the state cache
inline inline
void bool // true unless a new hook state was required and canReserveNew = false
set_state_cache( set_state_cache(
hook::HookContext& hookCtx, hook::HookContext& hookCtx,
ripple::AccountID const& acc, ripple::AccountID const& acc,
ripple::uint256 const& ns, ripple::uint256 const& ns,
ripple::uint256 const& key, ripple::uint256 const& key,
ripple::Blob& data, ripple::Blob& data,
bool modified) bool modified,
bool canReserveNew) // true iff there's sufficient xrp to reserve a new hook state
{ {
auto& stateMap = hookCtx.result.stateMap; auto& stateMap = hookCtx.result.stateMap;
if (stateMap.find(acc) == stateMap.end()) if (stateMap.find(acc) == stateMap.end())
{ {
if (modified && !canReserveNew)
return false;
stateMap[acc] = stateMap[acc] =
{ {
{ ns, { ns,
@@ -976,27 +980,33 @@ set_state_cache(
} }
} }
}; };
return; return true;
} }
auto& stateMapAcc = stateMap[acc]; auto& stateMapAcc = stateMap[acc];
if (stateMapAcc.find(ns) == stateMapAcc.end()) if (stateMapAcc.find(ns) == stateMapAcc.end())
{ {
if (modified && !canReserveNew)
return false;
stateMapAcc[ns] = stateMapAcc[ns] =
{ {
{ key, { key,
{ modified, data } { modified, data }
} }
}; };
return; return true;
} }
auto& stateMapNs = stateMapAcc[ns]; auto& stateMapNs = stateMapAcc[ns];
if (stateMapNs.find(key) == stateMapNs.end()) if (stateMapNs.find(key) == stateMapNs.end())
{ {
if (modified && !canReserveNew)
return false;
stateMapNs[key] = { modified, data }; stateMapNs[key] = { modified, data };
hookCtx.result.changedStateCount++; hookCtx.result.changedStateCount++;
return; return true;
} }
if (modified) if (modified)
@@ -1008,7 +1018,7 @@ set_state_cache(
} }
stateMapNs[key].second = data; stateMapNs[key].second = data;
return; return true;
} }
DEFINE_HOOK_FUNCTION( DEFINE_HOOK_FUNCTION(
@@ -1090,10 +1100,24 @@ DEFINE_HOOK_FUNCTION(
ripple::Blob data {memory + read_ptr, memory + read_ptr + read_len}; ripple::Blob data {memory + read_ptr, memory + read_ptr + read_len};
bool canReserveNew = false;
{
auto const accSLE = view.read(ripple::keylet::account(acc));
if (!accSLE)
return INVALID_ACCOUNT;
STAmount bal = accSLE->getFieldAmount(sfBalance);
uint32_t oldOwnerCount = accSLE->getFieldU32(sfOwnerCount);
canReserveNew = (bal >= view.fees().accountReserve(oldOwnerCount + 1));
}
// local modifications are always allowed // local modifications are always allowed
if (aread_len == 0 || acc == hookCtx.result.account) if (aread_len == 0 || acc == hookCtx.result.account)
{ {
set_state_cache(hookCtx, acc, ns, *key, data, true); if (!set_state_cache(hookCtx, acc, ns, *key, data, true, canReserveNew))
return RESERVE_INSUFFICIENT;
return read_len; return read_len;
} }
@@ -1106,13 +1130,15 @@ DEFINE_HOOK_FUNCTION(
if (cacheEntry && cacheEntry->get().first) if (cacheEntry && cacheEntry->get().first)
{ {
// if a cache entry already exists and it has already been modified don't check grants again // if a cache entry already exists and it has already been modified don't check grants again
set_state_cache(hookCtx, acc, ns, *key, data, true); if (!set_state_cache(hookCtx, acc, ns, *key, data, true, canReserveNew))
return RESERVE_INSUFFICIENT;
return read_len; return read_len;
} }
// cache miss or cache was present but entry was not marked as previously modified // cache miss or cache was present but entry was not marked as previously modified
// therefore before continuing we need to check grants // therefore before continuing we need to check grants
auto const sle = view.peek(ripple::keylet::hook(acc)); auto const sle = view.read(ripple::keylet::hook(acc));
if (!sle) if (!sle)
return INTERNAL_ERROR; return INTERNAL_ERROR;
@@ -1164,13 +1190,14 @@ DEFINE_HOOK_FUNCTION(
return NOT_AUTHORIZED; return NOT_AUTHORIZED;
} }
set_state_cache( if (!set_state_cache(
hookCtx, hookCtx,
acc, acc,
ns, ns,
*key, *key,
data, data,
true); true, canReserveNew))
return RESERVE_INSUFFICIENT;
return read_len; return read_len;
} }
@@ -1497,7 +1524,8 @@ DEFINE_HOOK_FUNCTION(
Blob b = hsSLE->getFieldVL(sfHookStateData); Blob b = hsSLE->getFieldVL(sfHookStateData);
// it exists add it to cache and return it // it exists add it to cache and return it
set_state_cache(hookCtx, acc, ns, *key, b, false); if (!set_state_cache(hookCtx, acc, ns, *key, b, false, false))
return INTERNAL_ERROR; // should never happen
if (write_ptr == 0) if (write_ptr == 0)
return data_as_int64(b.data(), b.size()); return data_as_int64(b.data(), b.size());