-
Notifications
You must be signed in to change notification settings - Fork 197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(world): allow registration of function selectors in the World, split out RegisterSystem from World #481
Changes from all commits
194a5af
b185818
b580108
eeb0343
b3dd86a
19cf507
f522663
c24ba17
a81bf8f
9bb01e6
31e1d7f
4232dce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
(test/World.t.sol) | Delete record [world.deleteRecord(namespace, file, singletonKey)]: 16038 | ||
(test/World.t.sol) | Push data to the table [world.pushToField(namespace, file, keyTuple, 0, encodedData)]: 96365 | ||
(test/World.t.sol) | Register a fallback system [bytes4 funcSelector1 = world.registerFunctionSelector(namespace, file, "", "")]: 80896 | ||
(test/World.t.sol) | Register a root fallback system [bytes4 funcSelector2 = world.registerRootFunctionSelector(namespace, file, worldFunc, 0)]: 72123 | ||
(test/World.t.sol) | Register a function selector [bytes4 functionSelector = world.registerFunctionSelector(namespace, file, "msgSender", "()")]: 101493 | ||
(test/World.t.sol) | Register a new namespace [world.registerNamespace("test")]: 151546 | ||
(test/World.t.sol) | Register a root function selector [bytes4 functionSelector = world.registerRootFunctionSelector(namespace, file, worldFunc, sysFunc)]: 96029 | ||
(test/World.t.sol) | Register a new table in the namespace [bytes32 tableSelector = world.registerTable(namespace, table, schema, defaultKeySchema)]: 252073 | ||
(test/World.t.sol) | Write data to a table field [world.setField(namespace, file, singletonKey, 0, abi.encodePacked(true))]: 44704 | ||
(test/World.t.sol) | Set metadata [world.setMetadata(namespace, file, tableName, fieldNames)]: 277121 | ||
(test/World.t.sol) | Write data to the table [Bool.set(tableId, world, true)]: 42576 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
|
||
import { ResourceSelector } from "./ResourceSelector.sol"; | ||
import { IErrors } from "./interfaces/IErrors.sol"; | ||
|
||
import { ResourceAccess } from "./tables/ResourceAccess.sol"; | ||
import { NamespaceOwner } from "./tables/NamespaceOwner.sol"; | ||
|
||
library AccessControl { | ||
using ResourceSelector for bytes32; | ||
|
||
/** | ||
* Returns true if the caller has access to the namespace or file, false otherwise. | ||
*/ | ||
function hasAccess(bytes16 namespace, bytes16 file, address caller) internal view returns (bool) { | ||
return | ||
ResourceAccess.get(ResourceSelector.from(namespace, 0), caller) || // First check access based on the namespace | ||
ResourceAccess.get(ResourceSelector.from(namespace, file), caller); // If caller has no namespace access, check access on the file | ||
} | ||
|
||
/** | ||
* Check for access at the given namespace or file. | ||
* Returns the resourceSelector if the caller has access. | ||
* Reverts with AccessDenied if the caller has no access. | ||
*/ | ||
function requireAccess( | ||
bytes16 namespace, | ||
bytes16 file, | ||
address caller | ||
) internal view returns (bytes32 resourceSelector) { | ||
resourceSelector = ResourceSelector.from(namespace, file); | ||
|
||
// Check if the given caller has access to the given namespace or file | ||
if (!hasAccess(namespace, file, msg.sender)) { | ||
revert IErrors.AccessDenied(resourceSelector.toString(), caller); | ||
} | ||
} | ||
|
||
function requireOwner( | ||
bytes16 namespace, | ||
bytes16 file, | ||
address caller | ||
) internal view returns (bytes32 resourceSelector) { | ||
resourceSelector = ResourceSelector.from(namespace, file); | ||
|
||
if (NamespaceOwner.get(namespace) != msg.sender) { | ||
revert IErrors.AccessDenied(resourceSelector.toString(), caller); | ||
} | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0; | ||
import { ROOT_NAMESPACE, ROOT_FILE } from "./constants.sol"; | ||
import { Bytes } from "@latticexyz/store/src/Bytes.sol"; | ||
|
||
bytes16 constant ROOT_NAMESPACE_STRING = bytes16("ROOT_NAMESPACE"); | ||
bytes16 constant ROOT_FILE_STRING = bytes16("ROOT_FILE"); | ||
|
||
library ResourceSelector { | ||
/** | ||
|
@@ -44,7 +49,26 @@ library ResourceSelector { | |
* Convert a selector to a string for more readable logs | ||
*/ | ||
function toString(bytes32 resourceSelector) internal pure returns (string memory) { | ||
return string(abi.encodePacked(getNamespace(resourceSelector), "/", getFile(resourceSelector))); | ||
bytes16 namespace = getNamespace(resourceSelector); | ||
bytes16 file = getFile(resourceSelector); | ||
return | ||
string( | ||
abi.encodePacked( | ||
namespace == ROOT_NAMESPACE ? ROOT_NAMESPACE_STRING : namespace, | ||
"/", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we have any guard rails to keep folks from using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. similar to the q above: we don't have checks that prevent folks from having |
||
file == ROOT_FILE ? ROOT_FILE_STRING : file | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Convert a selector to a trimmed string (no trailing `null` ASCII characters) | ||
*/ | ||
function toTrimmedString(bytes16 selector) internal pure returns (string memory) { | ||
uint256 length; | ||
for (; length < 16; length++) if (Bytes.slice1(selector, length) == 0) break; | ||
bytes memory packedSelector = abi.encodePacked(selector); | ||
return string(Bytes.setLength(packedSelector, length)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just thinking aloud: since we'll probably want to do more validating other than just null chars (e.g. slashes, underscores, non-alphanumeric, etc.), I wonder if its worth moving this logic to the client, and a Solidity function to just validate correctness? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the reason we need this function is to create a custom function selector ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In other words - we wouldn't need this if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a binary search for reference (would need to be modified for function findNull(uint256 _str) internal pure returns (uint256 result) {
result = 32;
// safe because result = 32, and max sum of all subtractions = 32
unchecked {
// binary search for the \0 terminator
if (_str & type(uint128).max == 0) {
result -= 16;
_str >>= 128;
}
if (_str & type(uint64).max == 0) {
result -= 8;
_str >>= 64;
}
if (_str & type(uint32).max == 0) {
result -= 4;
_str >>= 32;
}
if (_str & type(uint16).max == 0) {
result -= 2;
_str >>= 16;
}
if (_str & type(uint8).max == 0) {
result -= 1;
_str >>= 8;
}
// Another check is needed in case the string is not \0 terminated
if (_str & type(uint8).max == 0) {
result -= 1;
}
}
return result;
} |
||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any guard rails to keep folks from registering using these constants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, but also they're only used for logging, mostly in errors. Not worth the gas cost of adding checks to prevent them from being used as actual namespaces/files imo, wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it, I missed that this was only used in logging!