-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(store,world): add ResourceType, ResourceId
- Loading branch information
Showing
58 changed files
with
1,028 additions
and
844 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
|
||
library ResourceType { | ||
function encode(bytes30 resourceName, bytes2 resourceType) internal pure returns (bytes32) { | ||
return bytes32(resourceName) | (bytes32(resourceType) >> (8 * 30)); | ||
} | ||
|
||
function getType(bytes32 resourceId) internal pure returns (bytes2) { | ||
return bytes2(resourceId << (30 * 8)); | ||
} | ||
|
||
function isType(bytes32 resourceId, bytes2 resourceType) internal pure returns (bool) { | ||
return resourceId & bytes32(resourceType >> (30 * 8)) == bytes32(resourceType >> (30 * 8)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
|
||
bytes2 constant RESOURCE_TABLE = "ta"; | ||
bytes2 constant RESOURCE_OFFCHAIN_TABLE = "of"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
|
||
import { Test, console } from "forge-std/Test.sol"; | ||
import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; | ||
import { ResourceType } from "../src/ResourceType.sol"; | ||
import { RESOURCE_TABLE } from "../src/storeResourceTypes.sol"; | ||
|
||
contract ResourceTypeTest is Test, GasReporter { | ||
using ResourceType for bytes32; | ||
|
||
function testEncode() public { | ||
startGasReport("encode table ID with name and type"); | ||
bytes32 tableId = ResourceType.encode("name", RESOURCE_TABLE); | ||
endGasReport(); | ||
|
||
assertEq(tableId, bytes32(abi.encodePacked(bytes30("name"), RESOURCE_TABLE))); | ||
} | ||
|
||
function getGetType() public { | ||
bytes32 tableId = ResourceType.encode("name", "tb"); | ||
|
||
startGasReport("get type from a table ID"); | ||
bytes2 resourceType = tableId.getType(); | ||
endGasReport(); | ||
|
||
assertEq(resourceType, "tb"); | ||
} | ||
|
||
function testIsType() public { | ||
bytes32 tableId = ResourceType.encode("name", RESOURCE_TABLE); | ||
|
||
startGasReport("check if a table ID is of type tb"); | ||
bool isType = tableId.isType(RESOURCE_TABLE); | ||
endGasReport(); | ||
|
||
assertTrue(isType); | ||
} | ||
|
||
function testFuzz(bytes30 name, bytes2 resourceType) public { | ||
bytes32 tableId = ResourceType.encode(name, resourceType); | ||
assertEq(tableId.getType(), resourceType); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,43 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
|
||
import { ResourceSelector } from "./ResourceSelector.sol"; | ||
import { ResourceId } from "./ResourceId.sol"; | ||
import { IWorldErrors } from "./interfaces/IWorldErrors.sol"; | ||
|
||
import { ResourceAccess } from "./tables/ResourceAccess.sol"; | ||
import { NamespaceOwner } from "./tables/NamespaceOwner.sol"; | ||
|
||
library AccessControl { | ||
using ResourceSelector for bytes32; | ||
using ResourceId for bytes32; | ||
|
||
/** | ||
* Returns true if the caller has access to the namespace or name, false otherwise. | ||
*/ | ||
function hasAccess(bytes32 resourceSelector, address caller) internal view returns (bool) { | ||
function hasAccess(bytes32 resourceId, address caller) internal view returns (bool) { | ||
return | ||
address(this) == caller || // First check if the World is calling itself | ||
ResourceAccess._get(resourceSelector.getNamespace(), caller) || // Then check access based on the namespace | ||
ResourceAccess._get(resourceSelector, caller); // If caller has no namespace access, check access on the name | ||
address(this) == caller || // First check if the World is calling itself // TODO: remove this check? | ||
ResourceAccess._get(resourceId.getNamespaceId(), caller) || // Then check access based on the namespace | ||
ResourceAccess._get(resourceId, caller); // If caller has no namespace access, check access on the name | ||
} | ||
|
||
/** | ||
* Check for access at the given namespace or name. | ||
* Reverts with AccessDenied if the caller has no access. | ||
*/ | ||
function requireAccess(bytes32 resourceSelector, address caller) internal view { | ||
function requireAccess(bytes32 resourceId, address caller) internal view { | ||
// Check if the given caller has access to the given namespace or name | ||
if (!hasAccess(resourceSelector, caller)) { | ||
revert IWorldErrors.AccessDenied(resourceSelector.toString(), caller); | ||
if (!hasAccess(resourceId, caller)) { | ||
revert IWorldErrors.AccessDenied(resourceId.toString(), caller); | ||
} | ||
} | ||
|
||
/** | ||
* Check for ownership of the namespace of the given resource selector. | ||
* Check for ownership of the namespace of the given resource ID. | ||
* Reverts with AccessDenied if the check fails. | ||
*/ | ||
function requireOwner(bytes32 resourceSelector, address caller) internal view { | ||
if (NamespaceOwner._get(resourceSelector.getNamespace()) != caller) { | ||
revert IWorldErrors.AccessDenied(resourceSelector.toString(), caller); | ||
function requireOwner(bytes32 resourceId, address caller) internal view { | ||
if (NamespaceOwner._get(resourceId.getNamespace()) != caller) { | ||
revert IWorldErrors.AccessDenied(resourceId.toString(), caller); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
import { ROOT_NAMESPACE, ROOT_NAME } from "./constants.sol"; | ||
import { Bytes } from "@latticexyz/store/src/Bytes.sol"; | ||
import { ResourceType } from "@latticexyz/store/src/ResourceType.sol"; | ||
import { RESOURCE_NAMESPACE, MASK_RESOURCE_NAMESPACE } from "./worldResourceTypes.sol"; | ||
|
||
bytes16 constant ROOT_NAMESPACE_STRING = bytes16("ROOT_NAMESPACE"); | ||
bytes16 constant ROOT_NAME_STRING = bytes16("ROOT_NAME"); | ||
|
||
bytes32 constant NAMESPACE_MASK = bytes32(~bytes14("")); | ||
|
||
// TODO: change all namespace functions to namespace id functions (don't use bytes14 but bytes32 for ownership and access control) | ||
// TODO: should resource id be a user type to avoid type errors of eg passing a namespace (bytes14) instead of a namespace id (bytes32) | ||
// (I ran into a bunch of runtime errors while refactoring this where bytes14 was automatically casted to bytes32) | ||
|
||
library ResourceId { | ||
/** | ||
* Create a 32-byte resource ID from a namespace, name and type. | ||
* | ||
* A resource ID is a 32-byte value that uniquely identifies a resource. | ||
* The first 14 bytes represent the namespace, | ||
* the next 16 bytes represent the name, | ||
* the last 2 bytes represent the type. | ||
* | ||
* Note: the location of the resource type needs to stay aligned with `ResourceType` | ||
*/ | ||
function encode( | ||
bytes14 resourceNamespace, | ||
bytes16 resourceName, | ||
bytes2 resourceType | ||
) internal pure returns (bytes32) { | ||
return bytes32(resourceNamespace) | ((bytes32(resourceName) >> (14 * 8)) | (bytes32(resourceType) >> (30 * 8))); | ||
} | ||
|
||
/** | ||
* Create a 32-byte resource ID from a namespace. | ||
*/ | ||
function encodeNamespace(bytes14 resourceNamespace) internal pure returns (bytes32) { | ||
return bytes32(resourceNamespace) | (bytes32(RESOURCE_NAMESPACE) >> (30 * 8)); | ||
} | ||
|
||
/** | ||
* Get the namespace of a resource ID. | ||
*/ | ||
function getNamespace(bytes32 resourceId) internal pure returns (bytes14) { | ||
return bytes14(resourceId); | ||
} | ||
|
||
/** | ||
* Get the namespace resource ID corresponding to the namespace of a resource ID. | ||
*/ | ||
function getNamespaceId(bytes32 resourceId) internal pure returns (bytes32) { | ||
return (resourceId & NAMESPACE_MASK) | MASK_RESOURCE_NAMESPACE; | ||
} | ||
|
||
/** | ||
* Get the name of a resource ID. | ||
*/ | ||
function getName(bytes32 resourceId) internal pure returns (bytes16) { | ||
return bytes16(resourceId << (14 * 8)); | ||
} | ||
|
||
/** | ||
* Convert a resource ID to a string for more readable logs | ||
*/ | ||
function toString(bytes32 resourceId) internal pure returns (string memory) { | ||
bytes14 resourceNamespace = getNamespace(resourceId); | ||
bytes16 resourceName = getName(resourceId); | ||
bytes2 resourceType = ResourceType.getType(resourceId); | ||
return | ||
string( | ||
abi.encodePacked( | ||
resourceNamespace == ROOT_NAMESPACE ? ROOT_NAMESPACE_STRING : resourceNamespace, | ||
"_", | ||
resourceName == ROOT_NAME ? ROOT_NAME_STRING : resourceName, | ||
".", | ||
resourceType | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Convert a selector to a trimmed string (no trailing `null` ASCII characters) | ||
*/ | ||
function toTrimmedString(bytes16 paddedString) internal pure returns (string memory) { | ||
uint256 length; | ||
for (; length < 16; length++) if (Bytes.slice1(paddedString, length) == 0) break; | ||
bytes memory packedSelector = abi.encodePacked(paddedString); | ||
return string(Bytes.setLength(packedSelector, length)); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.