account for new state objects and owner reserves better

This commit is contained in:
Richard Holland
2022-06-23 12:55:03 +00:00
parent 043bdaf379
commit 006524a10a
3 changed files with 78 additions and 43 deletions

View File

@@ -29,11 +29,13 @@ namespace hook
using HookStateMap = using HookStateMap =
std::map< std::map<
ripple::AccountID, // account that owns the state ripple::AccountID, // account that owns the state
std::pair<
int64_t, // remaining available ownercount
std::map<ripple::uint256, // namespace std::map<ripple::uint256, // namespace
std::map<ripple::uint256, // key std::map<ripple::uint256, // key
std::pair< std::pair<
bool, // is modified from ledger value bool, // is modified from ledger value
ripple::Blob>>>>; // the value ripple::Blob>>>>>; // the value
using namespace ripple; using namespace ripple;

View File

@@ -932,14 +932,10 @@ lookup_state_cache(
if (stateMap.find(acc) == stateMap.end()) if (stateMap.find(acc) == stateMap.end())
return std::nullopt; return std::nullopt;
printf("here1\n"); auto& stateMapAcc = stateMap[acc].second;
auto& stateMapAcc = stateMap[acc];
if (stateMapAcc.find(ns) == stateMapAcc.end()) if (stateMapAcc.find(ns) == stateMapAcc.end())
return std::nullopt; return std::nullopt;
printf("here2\n");
auto& stateMapNs = stateMapAcc[ns]; auto& stateMapNs = stateMapAcc[ns];
auto const& ret = stateMapNs.find(key); auto const& ret = stateMapNs.find(key);
@@ -947,8 +943,6 @@ lookup_state_cache(
if (ret == stateMapNs.end()) if (ret == stateMapNs.end())
return std::nullopt; return std::nullopt;
printf("here3\n");
return std::cref(ret->second); return std::cref(ret->second);
} }
@@ -957,29 +951,57 @@ lookup_state_cache(
// RH TODO: add an accumulator for newly reserved state to the state map so canReserveNew can be computed // RH TODO: add an accumulator for newly reserved state to the state map so canReserveNew can be computed
// correctly when more than one new state is reserved. // correctly when more than one new state is reserved.
inline inline
bool // true unless a new hook state was required and canReserveNew = false bool // true unless a new hook state was required and the acc has insufficent reserve
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)
// if this is the first time this account has been interacted with
// we will compute how many available reserve positions there are
auto const& fees = hookCtx.applyCtx.view().fees();
auto const accSLE = hookCtx.applyCtx.view().read(ripple::keylet::account(acc));
if (!accSLE)
return false;
STAmount bal = accSLE->getFieldAmount(sfBalance);
int64_t availableForReserves =
bal.xrp().drops() - fees.accountReserve(accSLE->getFieldU32(sfOwnerCount)).drops();
int64_t increment = fees.increment.drops();
if (increment <= 0)
increment = 1;
availableForReserves /= increment;
if (availableForReserves < 1 && modified)
return false; return false;
stateMap[acc] = stateMap[acc] =
{ {
{ ns, availableForReserves - 1,
{ {
{ key, {
{ modified, data } ns,
{
{
key,
{
modified,
data
}
}
} }
} }
} }
@@ -987,11 +1009,19 @@ set_state_cache(
return true; return true;
} }
auto& stateMapAcc = stateMap[acc]; auto& stateMapAcc = stateMap[acc].second;
auto& availableForReserves = stateMap[acc].first;
bool const canReserveNew =
availableForReserves > 0;
if (stateMapAcc.find(ns) == stateMapAcc.end()) if (stateMapAcc.find(ns) == stateMapAcc.end())
{ {
if (modified && !canReserveNew) if (modified)
{
if (!canReserveNew)
return false; return false;
availableForReserves--;
}
stateMapAcc[ns] = stateMapAcc[ns] =
{ {
@@ -999,14 +1029,19 @@ set_state_cache(
{ modified, data } { modified, data }
} }
}; };
return true; 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) if (modified)
{
if (!canReserveNew)
return false; return false;
availableForReserves--;
}
stateMapNs[key] = { modified, data }; stateMapNs[key] = { modified, data };
hookCtx.result.changedStateCount++; hookCtx.result.changedStateCount++;
@@ -1104,22 +1139,10 @@ 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)
{ {
if (!set_state_cache(hookCtx, acc, ns, *key, data, true, canReserveNew)) if (!set_state_cache(hookCtx, acc, ns, *key, data, true))
return RESERVE_INSUFFICIENT; return RESERVE_INSUFFICIENT;
return read_len; return read_len;
@@ -1134,7 +1157,7 @@ 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
if (!set_state_cache(hookCtx, acc, ns, *key, data, true, canReserveNew)) if (!set_state_cache(hookCtx, acc, ns, *key, data, true))
return RESERVE_INSUFFICIENT; return RESERVE_INSUFFICIENT;
return read_len; return read_len;
@@ -1200,7 +1223,7 @@ DEFINE_HOOK_FUNCTION(
ns, ns,
*key, *key,
data, data,
true, canReserveNew)) true))
return RESERVE_INSUFFICIENT; return RESERVE_INSUFFICIENT;
return read_len; return read_len;
@@ -1221,7 +1244,7 @@ finalizeHookState(
for (const auto& accEntry : stateMap) for (const auto& accEntry : stateMap)
{ {
const auto& acc = accEntry.first; const auto& acc = accEntry.first;
for (const auto& nsEntry : accEntry.second) for (const auto& nsEntry : accEntry.second.second)
{ {
const auto& ns = nsEntry.first; const auto& ns = nsEntry.first;
for (const auto& cacheEntry : nsEntry.second) for (const auto& cacheEntry : nsEntry.second)
@@ -1528,7 +1551,7 @@ 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
if (!set_state_cache(hookCtx, acc, ns, *key, b, false, false)) if (!set_state_cache(hookCtx, acc, ns, *key, b, false))
return INTERNAL_ERROR; // should never happen return INTERNAL_ERROR; // should never happen
if (write_ptr == 0) if (write_ptr == 0)
@@ -2795,7 +2818,17 @@ DEFINE_HOOK_FUNCTION(
<< "HookEmit[" << HC_ACC() << "]: tpTrans->getStatus() != NEW"; << "HookEmit[" << HC_ACC() << "]: tpTrans->getStatus() != NEW";
return EMISSION_FAILURE; return EMISSION_FAILURE;
} }
/*
auto preflightResult =
ripple::preflight(applyCtx.app, applyCtx.view().rules(), *stpTrans, ripple::ApplyFlags::tapNONE, j);
if (preflightResult.ter != tesSUCCESS)
{
JLOG(j.trace())
<< "HookEmit[" << HC_ACC() << "]: Transaction preflight failure: " << preflightResult.ter;
return EMISSION_FAILURE;
}
*/
hookCtx.result.emittedTxn.push(tpTrans); hookCtx.result.emittedTxn.push(tpTrans);
auto const& txID = auto const& txID =

View File

@@ -530,7 +530,7 @@ extern SF_ACCOUNT const sfEmitCallback;
// account (uncommon) // account (uncommon)
extern SF_ACCOUNT const sfHookAccount; extern SF_ACCOUNT const sfHookAccount;
extern SF_ACCOUNT const sfMinter; extern SF_ACCOUNT const sfNFTokenMinter;
// path set // path set
extern SField const sfPaths; extern SField const sfPaths;