mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-29 23:15:49 +00:00
FXV1: Namespace Limit (#220)
This commit is contained in:
@@ -45,5 +45,7 @@
|
||||
#define INVALID_KEY -41
|
||||
#define NOT_A_STRING -42
|
||||
#define MEM_OVERLAP -43
|
||||
#define TOO_MANY_STATE_MODIFICATIONS -44
|
||||
#define TOO_MANY_NAMESPACES -45
|
||||
#define HOOK_ERROR_CODES
|
||||
#endif //HOOK_ERROR_CODES
|
||||
@@ -54,6 +54,12 @@ maxHookChainLength(void)
|
||||
return 10;
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
maxNamespaces(void)
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
|
||||
enum TSHFlags : uint8_t {
|
||||
tshNONE = 0b000,
|
||||
tshROLLBACK = 0b001,
|
||||
@@ -313,6 +319,7 @@ enum hook_return_code : int64_t {
|
||||
MEM_OVERLAP = -43, // one or more specified buffers are the same memory
|
||||
TOO_MANY_STATE_MODIFICATIONS = -44, // more than 5000 modified state
|
||||
// entires in the combined hook chains
|
||||
TOO_MANY_NAMESPACES = -45
|
||||
};
|
||||
|
||||
enum ExitType : uint8_t {
|
||||
|
||||
@@ -29,8 +29,9 @@ isEmittedTxn(ripple::STTx const& tx);
|
||||
// only upon tesSuccess for the otxn.
|
||||
class HookStateMap : public std::map<
|
||||
ripple::AccountID, // account that owns the state
|
||||
std::pair<
|
||||
std::tuple<
|
||||
int64_t, // remaining available ownercount
|
||||
int64_t, // total namespace count
|
||||
std::map<
|
||||
ripple::uint256, // namespace
|
||||
std::map<
|
||||
|
||||
@@ -1287,7 +1287,7 @@ lookup_state_cache(
|
||||
if (stateMap.find(acc) == stateMap.end())
|
||||
return std::nullopt;
|
||||
|
||||
auto& stateMapAcc = stateMap[acc].second;
|
||||
auto& stateMapAcc = std::get<2>(stateMap[acc]);
|
||||
if (stateMapAcc.find(ns) == stateMapAcc.end())
|
||||
return std::nullopt;
|
||||
|
||||
@@ -1312,18 +1312,22 @@ set_state_cache(
|
||||
bool modified)
|
||||
{
|
||||
auto& stateMap = hookCtx.result.stateMap;
|
||||
auto& view = hookCtx.applyCtx.view();
|
||||
|
||||
if (modified && stateMap.modified_entry_count >= max_state_modifications)
|
||||
return TOO_MANY_STATE_MODIFICATIONS;
|
||||
|
||||
bool const createNamespace = view.rules().enabled(fixXahauV1) &&
|
||||
!view.exists(keylet::hookStateDir(acc, ns));
|
||||
|
||||
if (stateMap.find(acc) == stateMap.end())
|
||||
{
|
||||
// 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));
|
||||
auto const accSLE = view.read(ripple::keylet::account(acc));
|
||||
|
||||
if (!accSLE)
|
||||
return DOESNT_EXIST;
|
||||
|
||||
@@ -1342,15 +1346,32 @@ set_state_cache(
|
||||
if (availableForReserves < 1 && modified)
|
||||
return RESERVE_INSUFFICIENT;
|
||||
|
||||
int64_t namespaceCount = accSLE->isFieldPresent(sfHookNamespaces)
|
||||
? accSLE->getFieldV256(sfHookNamespaces).size()
|
||||
: 0;
|
||||
|
||||
if (createNamespace)
|
||||
{
|
||||
// overflow should never ever happen but check anyway
|
||||
if (namespaceCount + 1 < namespaceCount)
|
||||
return INTERNAL_ERROR;
|
||||
|
||||
if (++namespaceCount > hook::maxNamespaces())
|
||||
return TOO_MANY_NAMESPACES;
|
||||
}
|
||||
|
||||
stateMap.modified_entry_count++;
|
||||
|
||||
stateMap[acc] = {
|
||||
availableForReserves - 1, {{ns, {{key, {modified, data}}}}}};
|
||||
availableForReserves - 1,
|
||||
namespaceCount,
|
||||
{{ns, {{key, {modified, data}}}}}};
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto& stateMapAcc = stateMap[acc].second;
|
||||
auto& availableForReserves = stateMap[acc].first;
|
||||
auto& availableForReserves = std::get<0>(stateMap[acc]);
|
||||
auto& namespaceCount = std::get<1>(stateMap[acc]);
|
||||
auto& stateMapAcc = std::get<2>(stateMap[acc]);
|
||||
bool const canReserveNew = availableForReserves > 0;
|
||||
|
||||
if (stateMapAcc.find(ns) == stateMapAcc.end())
|
||||
@@ -1360,6 +1381,18 @@ set_state_cache(
|
||||
if (!canReserveNew)
|
||||
return RESERVE_INSUFFICIENT;
|
||||
|
||||
if (createNamespace)
|
||||
{
|
||||
// overflow should never ever happen but check anyway
|
||||
if (namespaceCount + 1 < namespaceCount)
|
||||
return INTERNAL_ERROR;
|
||||
|
||||
if (namespaceCount + 1 > hook::maxNamespaces())
|
||||
return TOO_MANY_NAMESPACES;
|
||||
|
||||
namespaceCount++;
|
||||
}
|
||||
|
||||
availableForReserves--;
|
||||
stateMap.modified_entry_count++;
|
||||
}
|
||||
@@ -1488,6 +1521,13 @@ DEFINE_HOOK_FUNCTION(
|
||||
auto const key = make_state_key(
|
||||
std::string_view{(const char*)(memory + kread_ptr), (size_t)kread_len});
|
||||
|
||||
if (view.rules().enabled(fixXahauV1))
|
||||
{
|
||||
auto const sleAccount = view.peek(hookCtx.result.accountKeylet);
|
||||
if (!sleAccount)
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
if (!key)
|
||||
return INTERNAL_ERROR;
|
||||
|
||||
@@ -1610,7 +1650,7 @@ hook::finalizeHookState(
|
||||
for (const auto& accEntry : stateMap)
|
||||
{
|
||||
const auto& acc = accEntry.first;
|
||||
for (const auto& nsEntry : accEntry.second.second)
|
||||
for (const auto& nsEntry : std::get<2>(accEntry.second))
|
||||
{
|
||||
const auto& ns = nsEntry.first;
|
||||
for (const auto& cacheEntry : nsEntry.second)
|
||||
|
||||
@@ -7826,6 +7826,134 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_state_foreign_set_max()
|
||||
{
|
||||
testcase("Test state_foreign_set max");
|
||||
using namespace jtx;
|
||||
|
||||
static const std::vector<uint8_t> ns_maxHook = {
|
||||
0x00U, 0x61U, 0x73U, 0x6dU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
|
||||
0x36U, 0x07U, 0x60U, 0x02U, 0x7fU, 0x7fU, 0x01U, 0x7fU, 0x60U,
|
||||
0x02U, 0x7fU, 0x7fU, 0x01U, 0x7eU, 0x60U, 0x03U, 0x7fU, 0x7fU,
|
||||
0x7eU, 0x01U, 0x7eU, 0x60U, 0x04U, 0x7fU, 0x7fU, 0x7fU, 0x7fU,
|
||||
0x01U, 0x7eU, 0x60U, 0x05U, 0x7fU, 0x7fU, 0x7fU, 0x7fU, 0x7fU,
|
||||
0x01U, 0x7eU, 0x60U, 0x08U, 0x7fU, 0x7fU, 0x7fU, 0x7fU, 0x7fU,
|
||||
0x7fU, 0x7fU, 0x7fU, 0x01U, 0x7eU, 0x60U, 0x01U, 0x7fU, 0x01U,
|
||||
0x7eU, 0x02U, 0x79U, 0x08U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x02U,
|
||||
0x5fU, 0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x0cU,
|
||||
0x68U, 0x6fU, 0x6fU, 0x6bU, 0x5fU, 0x61U, 0x63U, 0x63U, 0x6fU,
|
||||
0x75U, 0x6eU, 0x74U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6eU, 0x76U,
|
||||
0x08U, 0x72U, 0x6fU, 0x6cU, 0x6cU, 0x62U, 0x61U, 0x63U, 0x6bU,
|
||||
0x00U, 0x02U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x05U, 0x73U, 0x74U,
|
||||
0x61U, 0x74U, 0x65U, 0x00U, 0x03U, 0x03U, 0x65U, 0x6eU, 0x76U,
|
||||
0x05U, 0x74U, 0x72U, 0x61U, 0x63U, 0x65U, 0x00U, 0x04U, 0x03U,
|
||||
0x65U, 0x6eU, 0x76U, 0x11U, 0x73U, 0x74U, 0x61U, 0x74U, 0x65U,
|
||||
0x5fU, 0x66U, 0x6fU, 0x72U, 0x65U, 0x69U, 0x67U, 0x6eU, 0x5fU,
|
||||
0x73U, 0x65U, 0x74U, 0x00U, 0x05U, 0x03U, 0x65U, 0x6eU, 0x76U,
|
||||
0x09U, 0x73U, 0x74U, 0x61U, 0x74U, 0x65U, 0x5fU, 0x73U, 0x65U,
|
||||
0x74U, 0x00U, 0x03U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x06U, 0x61U,
|
||||
0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U, 0x03U, 0x02U,
|
||||
0x01U, 0x06U, 0x05U, 0x03U, 0x01U, 0x00U, 0x02U, 0x06U, 0x2bU,
|
||||
0x07U, 0x7fU, 0x01U, 0x41U, 0xc0U, 0x88U, 0x04U, 0x0bU, 0x7fU,
|
||||
0x00U, 0x41U, 0x80U, 0x08U, 0x0bU, 0x7fU, 0x00U, 0x41U, 0xb2U,
|
||||
0x08U, 0x0bU, 0x7fU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0bU, 0x7fU,
|
||||
0x00U, 0x41U, 0xc0U, 0x88U, 0x04U, 0x0bU, 0x7fU, 0x00U, 0x41U,
|
||||
0x00U, 0x0bU, 0x7fU, 0x00U, 0x41U, 0x01U, 0x0bU, 0x07U, 0x08U,
|
||||
0x01U, 0x04U, 0x68U, 0x6fU, 0x6fU, 0x6bU, 0x00U, 0x08U, 0x0aU,
|
||||
0xd4U, 0x81U, 0x00U, 0x01U, 0xd0U, 0x81U, 0x00U, 0x02U, 0x02U,
|
||||
0x7fU, 0x01U, 0x7eU, 0x23U, 0x00U, 0x41U, 0xe0U, 0x00U, 0x6bU,
|
||||
0x22U, 0x01U, 0x24U, 0x00U, 0x20U, 0x01U, 0x20U, 0x00U, 0x36U,
|
||||
0x02U, 0x5cU, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x00U, 0x1aU,
|
||||
0x20U, 0x01U, 0x41U, 0x40U, 0x6bU, 0x41U, 0x14U, 0x10U, 0x01U,
|
||||
0x42U, 0x14U, 0x52U, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U,
|
||||
0x42U, 0x0cU, 0x10U, 0x02U, 0x1aU, 0x0bU, 0x20U, 0x01U, 0x41U,
|
||||
0x38U, 0x6aU, 0x41U, 0x08U, 0x20U, 0x01U, 0x41U, 0x40U, 0x6bU,
|
||||
0x22U, 0x00U, 0x41U, 0x14U, 0x10U, 0x03U, 0x1aU, 0x20U, 0x01U,
|
||||
0x20U, 0x01U, 0x29U, 0x03U, 0x38U, 0x42U, 0x01U, 0x7cU, 0x37U,
|
||||
0x03U, 0x38U, 0x20U, 0x01U, 0x41U, 0xabU, 0x01U, 0x3aU, 0x00U,
|
||||
0x19U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x38U, 0x3cU,
|
||||
0x00U, 0x1aU, 0x41U, 0x80U, 0x08U, 0x41U, 0x02U, 0x20U, 0x01U,
|
||||
0x41U, 0x10U, 0x6aU, 0x22U, 0x02U, 0x41U, 0x20U, 0x41U, 0x01U,
|
||||
0x10U, 0x04U, 0x1aU, 0x20U, 0x01U, 0x41U, 0xa9U, 0x08U, 0x41U,
|
||||
0x09U, 0x41U, 0xa4U, 0x08U, 0x41U, 0x05U, 0x20U, 0x02U, 0x41U,
|
||||
0x20U, 0x20U, 0x00U, 0x41U, 0x14U, 0x10U, 0x05U, 0x37U, 0x03U,
|
||||
0x08U, 0x20U, 0x01U, 0x29U, 0x03U, 0x08U, 0x42U, 0x00U, 0x53U,
|
||||
0x04U, 0x40U, 0x41U, 0x83U, 0x08U, 0x41U, 0x21U, 0x20U, 0x01U,
|
||||
0x29U, 0x03U, 0x08U, 0x10U, 0x02U, 0x1aU, 0x0bU, 0x20U, 0x01U,
|
||||
0x41U, 0x38U, 0x6aU, 0x41U, 0x08U, 0x20U, 0x01U, 0x41U, 0x40U,
|
||||
0x6bU, 0x41U, 0x14U, 0x10U, 0x06U, 0x1aU, 0x41U, 0x00U, 0x41U,
|
||||
0x00U, 0x20U, 0x01U, 0x29U, 0x03U, 0x08U, 0x10U, 0x07U, 0x20U,
|
||||
0x01U, 0x41U, 0xe0U, 0x00U, 0x6aU, 0x24U, 0x00U, 0x0bU, 0x0bU,
|
||||
0x38U, 0x01U, 0x00U, 0x41U, 0x80U, 0x08U, 0x0bU, 0x31U, 0x6eU,
|
||||
0x73U, 0x00U, 0x6eU, 0x73U, 0x5fU, 0x6dU, 0x61U, 0x78U, 0x2eU,
|
||||
0x63U, 0x3aU, 0x20U, 0x4dU, 0x61U, 0x78U, 0x20U, 0x4eU, 0x61U,
|
||||
0x6dU, 0x65U, 0x73U, 0x70U, 0x61U, 0x63U, 0x65U, 0x73U, 0x20U,
|
||||
0x52U, 0x65U, 0x61U, 0x63U, 0x68U, 0x65U, 0x64U, 0x00U, 0x6bU,
|
||||
0x65U, 0x79U, 0x32U, 0x00U, 0x63U, 0x6fU, 0x6eU, 0x74U, 0x65U,
|
||||
0x6eU, 0x74U, 0x32U};
|
||||
|
||||
// fixXahauV1
|
||||
{
|
||||
for (bool const withfixXahauV1 : {true, false})
|
||||
{
|
||||
auto const amend = withfixXahauV1
|
||||
? supported_amendments()
|
||||
: supported_amendments() - fixXahauV1;
|
||||
|
||||
Env env{*this, amend};
|
||||
|
||||
// Env env{*this, envconfig(), amend, nullptr,
|
||||
// // beast::severities::kWarning
|
||||
// beast::severities::kTrace
|
||||
// };
|
||||
|
||||
auto const bob = Account{"bob"};
|
||||
auto const alice = Account{"alice"};
|
||||
env.fund(XRP(10000000), alice);
|
||||
env.fund(XRP(10000000), bob);
|
||||
|
||||
// install the hook on alice
|
||||
env(ripple::test::jtx::hook(
|
||||
alice, {{hso(ns_maxHook, overrideFlag)}}, 0),
|
||||
M("set state_foreign_set_max"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
// invoke the hook
|
||||
for (uint32_t i = 0; i < 255; ++i)
|
||||
{
|
||||
env(pay(bob, alice, XRP(1)),
|
||||
M("test state_foreign_set_max"),
|
||||
fee(XRP(1)));
|
||||
}
|
||||
|
||||
auto const txResult =
|
||||
withfixXahauV1 ? ter(tecHOOK_REJECTED) : ter(tesSUCCESS);
|
||||
env(pay(bob, alice, XRP(1)),
|
||||
M("test state_foreign_set_max"),
|
||||
fee(XRP(1)),
|
||||
ter(txResult));
|
||||
env.close();
|
||||
|
||||
// verify hook result
|
||||
std::string const hookResult = withfixXahauV1
|
||||
? "800000000000002d" // TOO_MANY_NAMESPACES / -45
|
||||
: "9";
|
||||
|
||||
Json::Value params;
|
||||
params[jss::transaction] =
|
||||
env.tx()->getJson(JsonOptions::none)[jss::hash];
|
||||
auto const jrr = env.rpc("json", "tx", to_string(params));
|
||||
auto const meta = jrr[jss::result][jss::meta];
|
||||
auto const executions = meta[sfHookExecutions.jsonName];
|
||||
auto const execution = executions[0u][sfHookExecution.jsonName];
|
||||
BEAST_EXPECT(
|
||||
execution[sfHookReturnCode.jsonName] == hookResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_state_foreign_set()
|
||||
{
|
||||
@@ -11583,10 +11711,11 @@ public:
|
||||
test_slot_subfield(); //
|
||||
test_slot_type(); //
|
||||
|
||||
test_state(); //
|
||||
test_state_foreign(); //
|
||||
test_state_foreign_set(); //
|
||||
test_state_set(); //
|
||||
test_state(); //
|
||||
test_state_foreign(); //
|
||||
test_state_foreign_set(); //
|
||||
test_state_foreign_set_max(); //
|
||||
test_state_set(); //
|
||||
|
||||
test_sto_emplace(); //
|
||||
test_sto_erase(); //
|
||||
|
||||
Reference in New Issue
Block a user