Skip to content

Commit

Permalink
feat(world): prevent invalid namespace strings [M-05] (#2169)
Browse files Browse the repository at this point in the history
  • Loading branch information
alvrs committed Jan 22, 2024
1 parent 865253d commit c642ff3
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 40 deletions.
9 changes: 9 additions & 0 deletions .changeset/tidy-stingrays-think.md
@@ -0,0 +1,9 @@
---
"@latticexyz/world": major
---

Namespaces are not allowed to contain double underscores ("\_\_") anymore, as this sequence of characters is used to [separate the namespace and function selector](https://github.com/latticexyz/mud/pull/2168) in namespaced systems.
This is to prevent signature clashes of functions in different namespaces.

(Example: If namespaces were allowed to contain this separator string, a function "function" in namespace "namespace\_\_my" would result in the namespaced function selector "namespace\_\_my\_\_function",
and would clash with a function "my\_\_function" in namespace "namespace".)
12 changes: 6 additions & 6 deletions packages/world-modules/gas-report.json
Expand Up @@ -135,7 +135,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testGetKeysWithValueGas",
"name": "install keys with value module",
"gasUsed": 683899
"gasUsed": 687430
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -153,7 +153,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testInstall",
"name": "install keys with value module",
"gasUsed": 683899
"gasUsed": 687430
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -165,7 +165,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetAndDeleteRecordHook",
"name": "install keys with value module",
"gasUsed": 683899
"gasUsed": 687430
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -183,7 +183,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetField",
"name": "install keys with value module",
"gasUsed": 683899
"gasUsed": 687430
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand Down Expand Up @@ -303,7 +303,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstall",
"name": "install unique entity module",
"gasUsed": 701756
"gasUsed": 705287
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand All @@ -315,7 +315,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstallRoot",
"name": "installRoot unique entity module",
"gasUsed": 670623
"gasUsed": 674154
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand Down
10 changes: 5 additions & 5 deletions packages/world/gas-report.json
Expand Up @@ -63,7 +63,7 @@
"file": "test/Factories.t.sol",
"test": "testWorldFactory",
"name": "deploy world via WorldFactory",
"gasUsed": 13041829
"gasUsed": 13045360
},
{
"file": "test/World.t.sol",
Expand Down Expand Up @@ -111,7 +111,7 @@
"file": "test/World.t.sol",
"test": "testRegisterNamespace",
"name": "Register a new namespace",
"gasUsed": 120951
"gasUsed": 124482
},
{
"file": "test/World.t.sol",
Expand All @@ -129,13 +129,13 @@
"file": "test/World.t.sol",
"test": "testRegisterTable",
"name": "Register a new table in the namespace",
"gasUsed": 536881
"gasUsed": 536849
},
{
"file": "test/World.t.sol",
"test": "testRenounceNamespace",
"name": "Renounce namespace ownership",
"gasUsed": 36773
"gasUsed": 40304
},
{
"file": "test/World.t.sol",
Expand All @@ -153,7 +153,7 @@
"file": "test/World.t.sol",
"test": "testUnregisterNamespaceDelegation",
"name": "unregister a namespace delegation",
"gasUsed": 28360
"gasUsed": 31891
},
{
"file": "test/World.t.sol",
Expand Down
6 changes: 6 additions & 0 deletions packages/world/src/IWorldErrors.sol
Expand Up @@ -42,6 +42,12 @@ interface IWorldErrors {
*/
error World_InvalidResourceId(ResourceId resourceId, string resourceIdString);

/**
* @notice Raised when an namespace contains an invalid sequence of characters ("__").
* @param namespace The invalid namespace.
*/
error World_InvalidNamespace(bytes14 namespace);

/**
* @notice Raised when trying to register a system that already exists.
* @param system The address of the system.
Expand Down
1 change: 1 addition & 0 deletions packages/world/src/WorldResourceId.sol
Expand Up @@ -7,6 +7,7 @@ import { ResourceId, ResourceIdInstance, TYPE_BITS } from "@latticexyz/store/src
import { ROOT_NAMESPACE, ROOT_NAME } from "./constants.sol";
import { RESOURCE_NAMESPACE, MASK_RESOURCE_NAMESPACE } from "./worldResourceTypes.sol";

uint256 constant NAMESPACE_BYTES = 14;
uint256 constant NAMESPACE_BITS = 14 * 8; // 14 bytes * 8 bits per byte
uint256 constant NAME_BITS = 16 * 8; // 16 bytes * 8 bits per byte

Expand Down
Expand Up @@ -6,7 +6,7 @@ import { AccessControl } from "../../../AccessControl.sol";
import { ResourceId } from "../../../WorldResourceId.sol";
import { ResourceAccess } from "../../../codegen/tables/ResourceAccess.sol";
import { NamespaceOwner } from "../../../codegen/tables/NamespaceOwner.sol";
import { requireNamespace } from "../../../requireNamespace.sol";
import { validateNamespace } from "../../../validateNamespace.sol";

import { LimitedCallContext } from "../LimitedCallContext.sol";

Expand Down Expand Up @@ -54,7 +54,7 @@ contract AccessManagementSystem is System, LimitedCallContext {
*/
function transferOwnership(ResourceId namespaceId, address newOwner) public virtual onlyDelegatecall {
// Require the namespace ID to be a valid namespace
requireNamespace(namespaceId);
validateNamespace(namespaceId);

// Require the namespace to exist
AccessControl.requireExistence(namespaceId);
Expand All @@ -79,7 +79,7 @@ contract AccessManagementSystem is System, LimitedCallContext {
*/
function renounceOwnership(ResourceId namespaceId) public virtual onlyDelegatecall {
// Require the namespace ID to be a valid namespace
requireNamespace(namespaceId);
validateNamespace(namespaceId);

// Require the namespace to exist
AccessControl.requireExistence(namespaceId);
Expand Down
Expand Up @@ -11,7 +11,7 @@ import { RESOURCE_NAMESPACE } from "../../../worldResourceTypes.sol";
import { IWorldErrors } from "../../../IWorldErrors.sol";

import { Balances } from "../../../codegen/tables/Balances.sol";
import { requireNamespace } from "../../../requireNamespace.sol";
import { validateNamespace } from "../../../validateNamespace.sol";

import { LimitedCallContext } from "../LimitedCallContext.sol";

Expand All @@ -35,9 +35,9 @@ contract BalanceTransferSystem is System, IWorldErrors, LimitedCallContext {
uint256 amount
) public virtual onlyDelegatecall {
// Require the from namespace to be a valid namespace ID
requireNamespace(fromNamespaceId);
validateNamespace(fromNamespaceId);
// Require the to namespace to be a valid namespace ID
requireNamespace(toNamespaceId);
validateNamespace(toNamespaceId);

// Require the namespace to exist
AccessControl.requireExistence(toNamespaceId);
Expand Down
Expand Up @@ -27,7 +27,7 @@ import { SystemRegistry } from "../../../codegen/tables/SystemRegistry.sol";
import { Systems } from "../../../codegen/tables/Systems.sol";
import { FunctionSelectors } from "../../../codegen/tables/FunctionSelectors.sol";
import { FunctionSignatures } from "../../../codegen/tables/FunctionSignatures.sol";
import { requireNamespace } from "../../../requireNamespace.sol";
import { validateNamespace } from "../../../validateNamespace.sol";

import { LimitedCallContext } from "../LimitedCallContext.sol";

Expand All @@ -45,7 +45,7 @@ contract WorldRegistrationSystem is System, IWorldErrors, LimitedCallContext {
*/
function registerNamespace(ResourceId namespaceId) public virtual onlyDelegatecall {
// Require namespace ID to be a valid namespace
requireNamespace(namespaceId);
validateNamespace(namespaceId);

// Require namespace to not exist yet
if (ResourceIds._getExists(namespaceId)) {
Expand Down Expand Up @@ -300,7 +300,7 @@ contract WorldRegistrationSystem is System, IWorldErrors, LimitedCallContext {
bytes memory initCallData
) public onlyDelegatecall {
// Require namespace ID to be a valid namespace
requireNamespace(namespaceId);
validateNamespace(namespaceId);

// Require the delegation to not be unlimited
if (!Delegation.isLimited(delegationControlId)) {
Expand Down Expand Up @@ -335,7 +335,7 @@ contract WorldRegistrationSystem is System, IWorldErrors, LimitedCallContext {
*/
function unregisterNamespaceDelegation(ResourceId namespaceId) public onlyDelegatecall {
// Require namespace ID to be a valid namespace
requireNamespace(namespaceId);
validateNamespace(namespaceId);

// Require the namespace to exist
AccessControl.requireExistence(namespaceId);
Expand Down
19 changes: 0 additions & 19 deletions packages/world/src/requireNamespace.sol

This file was deleted.

31 changes: 31 additions & 0 deletions packages/world/src/validateNamespace.sol
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

import { Bytes } from "@latticexyz/store/src/Bytes.sol";

import { ResourceId, WorldResourceIdInstance, NAMESPACE_BYTES } from "./WorldResourceId.sol";
import { RESOURCE_NAMESPACE } from "./worldResourceTypes.sol";
import { IWorldErrors } from "./IWorldErrors.sol";

using WorldResourceIdInstance for ResourceId;

/**
* @notice Checks if a given `resourceId` is a valid namespace.
* @dev Reverts with IWorldErrors.World_InvalidResourceType if the ID does not have the correct components.
* @dev Reverts with IWorldErrors.World_InvalidNamespace if the namespace includes the reserved separator string ("__").
* @param resourceId The resource ID to verify.
*/
function validateNamespace(ResourceId resourceId) pure {
// Require the resourceId to have the namespace type
if (ResourceId.unwrap(resourceId) != ResourceId.unwrap(resourceId.getNamespaceId())) {
revert IWorldErrors.World_InvalidResourceType(RESOURCE_NAMESPACE, resourceId, resourceId.toString());
}

// Require the namespace to not include the reserved separator
bytes14 namespace = resourceId.getNamespace();
for (uint256 i; i < NAMESPACE_BYTES - 1; i++) {
if (Bytes.slice1(namespace, i) == bytes1("_") && Bytes.slice1(namespace, i + 1) == bytes1("_")) {
revert IWorldErrors.World_InvalidNamespace(namespace);
}
}
}
17 changes: 17 additions & 0 deletions packages/world/test/World.t.sol
Expand Up @@ -348,6 +348,23 @@ contract WorldTest is Test, GasReporter {
world.registerNamespace(namespaceId);
}

function testRegisterInvalidNamespace() public {
ResourceId invalidNamespaceId = WorldResourceIdLib.encode(RESOURCE_SYSTEM, "namespace", "system");
vm.expectRevert(
abi.encodeWithSelector(
IWorldErrors.World_InvalidResourceType.selector,
RESOURCE_NAMESPACE,
invalidNamespaceId,
invalidNamespaceId.toString()
)
);
world.registerNamespace(invalidNamespaceId);

bytes14 invalidNamespace = "invld__nmsp";
vm.expectRevert(abi.encodeWithSelector(IWorldErrors.World_InvalidNamespace.selector, invalidNamespace));
world.registerNamespace(WorldResourceIdLib.encodeNamespace(invalidNamespace));
}

function testRegisterCoreNamespacesRevert() public {
vm.expectRevert(
abi.encodeWithSelector(
Expand Down

0 comments on commit c642ff3

Please sign in to comment.