Skip to content

Commit

Permalink
feat(world): add FunctionSignatures offchain table (#1575)
Browse files Browse the repository at this point in the history
  • Loading branch information
alvrs authored Sep 22, 2023
1 parent 31ffc9d commit e5d208e
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 64 deletions.
18 changes: 18 additions & 0 deletions .changeset/honest-months-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@latticexyz/cli": major
"@latticexyz/world": major
---

The `registerRootFunctionSelector` function's signature was changed to accept a `string functionSignature` parameter instead of a `bytes4 functionSelector` parameter.
This change enables the `World` to store the function signatures of all registered functions in a `FunctionSignatures` offchain table, which will allow for the automatic generation of interfaces for a given `World` address in the future.

```diff
IBaseWorld {
function registerRootFunctionSelector(
ResourceId systemId,
- bytes4 worldFunctionSelector,
+ string memory worldFunctionSignature,
bytes4 systemFunctionSelector
) external returns (bytes4 worldFunctionSelector);
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function getRegisterFunctionSelectorCallData(input: {
const functionSelector = toFunctionSelector(systemFunctionSignature);
return {
func: "registerRootFunctionSelector",
args: [resourceIdToHex({ type: "system", namespace, name }), functionSelector, functionSelector],
args: [resourceIdToHex({ type: "system", namespace, name }), systemFunctionSignature, functionSelector],
};
} else {
return {
Expand Down
20 changes: 10 additions & 10 deletions packages/world/gas-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testGetKeysWithValueGas",
"name": "install keys with value module",
"gasUsed": 654836
"gasUsed": 654858
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -117,7 +117,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testInstall",
"name": "install keys with value module",
"gasUsed": 654836
"gasUsed": 654858
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -129,7 +129,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetAndDeleteRecordHook",
"name": "install keys with value module",
"gasUsed": 654836
"gasUsed": 654858
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -147,7 +147,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetField",
"name": "install keys with value module",
"gasUsed": 654836
"gasUsed": 654858
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand Down Expand Up @@ -255,7 +255,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstall",
"name": "install unique entity module",
"gasUsed": 676985
"gasUsed": 682963
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand All @@ -267,7 +267,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstallRoot",
"name": "installRoot unique entity module",
"gasUsed": 644325
"gasUsed": 650259
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand Down Expand Up @@ -309,19 +309,19 @@
"file": "test/World.t.sol",
"test": "testRegisterFunctionSelector",
"name": "Register a function selector",
"gasUsed": 77897
"gasUsed": 83885
},
{
"file": "test/World.t.sol",
"test": "testRegisterNamespace",
"name": "Register a new namespace",
"gasUsed": 123141
"gasUsed": 123163
},
{
"file": "test/World.t.sol",
"test": "testRegisterRootFunctionSelector",
"name": "Register a root function selector",
"gasUsed": 74651
"gasUsed": 81147
},
{
"file": "test/World.t.sol",
Expand All @@ -333,7 +333,7 @@
"file": "test/World.t.sol",
"test": "testRegisterTable",
"name": "Register a new table in the namespace",
"gasUsed": 640825
"gasUsed": 640821
},
{
"file": "test/World.t.sol",
Expand Down
10 changes: 10 additions & 0 deletions packages/world/mud.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ export default mudConfig({
},
dataStruct: false,
},
FunctionSignatures: {
directory: "modules/core/tables",
keySchema: {
functionSelector: "bytes4",
},
valueSchema: {
functionSignature: "string",
},
offchainOnly: true,
},
KeysWithValue: {
directory: "modules/keyswithvalue/tables",
keySchema: {
Expand Down
1 change: 1 addition & 0 deletions packages/world/src/index.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Systems, SystemsTableId } from "./modules/core/tables/Systems.sol";
import { SystemRegistry, SystemRegistryTableId } from "./modules/core/tables/SystemRegistry.sol";
import { SystemHooks, SystemHooksTableId } from "./modules/core/tables/SystemHooks.sol";
import { FunctionSelectors, FunctionSelectorsTableId } from "./modules/core/tables/FunctionSelectors.sol";
import { FunctionSignatures, FunctionSignaturesTableId } from "./modules/core/tables/FunctionSignatures.sol";
import { KeysWithValue } from "./modules/keyswithvalue/tables/KeysWithValue.sol";
import { KeysInTable, KeysInTableData, KeysInTableTableId } from "./modules/keysintable/tables/KeysInTable.sol";
import { UsedKeysIndex, UsedKeysIndexTableId } from "./modules/keysintable/tables/UsedKeysIndex.sol";
Expand Down
4 changes: 2 additions & 2 deletions packages/world/src/interfaces/IWorldRegistrationSystem.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ interface IWorldRegistrationSystem {

function registerRootFunctionSelector(
ResourceId systemId,
bytes4 worldFunctionSelector,
string memory worldFunctionSignature,
bytes4 systemFunctionSelector
) external returns (bytes4);
) external returns (bytes4 worldFunctionSelector);

function registerDelegation(address delegatee, ResourceId delegationControlId, bytes memory initCallData) external;
}
40 changes: 20 additions & 20 deletions packages/world/src/modules/core/CoreModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,33 +96,33 @@ contract CoreModule is Module {
* Register function selectors for all CoreSystem functions in the World
*/
function _registerFunctionSelectors() internal {
bytes4[17] memory functionSelectors = [
string[17] memory functionSignatures = [
// --- AccessManagementSystem ---
AccessManagementSystem.grantAccess.selector,
AccessManagementSystem.revokeAccess.selector,
AccessManagementSystem.transferOwnership.selector,
"grantAccess(bytes32,address)",
"revokeAccess(bytes32,address)",
"transferOwnership(bytes32,address)",
// --- BalanceTransferSystem ---
BalanceTransferSystem.transferBalanceToNamespace.selector,
BalanceTransferSystem.transferBalanceToAddress.selector,
"transferBalanceToNamespace(bytes32,bytes32,uint256)",
"transferBalanceToAddress(bytes32,address,uint256)",
// --- CallBatchSystem ---
CallBatchSystem.callBatch.selector,
"callBatch((bytes32,bytes)[])",
// --- ModuleInstallationSystem ---
ModuleInstallationSystem.installModule.selector,
"installModule(address,bytes)",
// --- StoreRegistrationSystem ---
StoreRegistrationSystem.registerTable.selector,
StoreRegistrationSystem.registerStoreHook.selector,
StoreRegistrationSystem.unregisterStoreHook.selector,
"registerTable(bytes32,bytes32,bytes32,bytes32,string[],string[])",
"registerStoreHook(bytes32,address,uint8)",
"unregisterStoreHook(bytes32,address)",
// --- WorldRegistrationSystem ---
WorldRegistrationSystem.registerNamespace.selector,
WorldRegistrationSystem.registerSystemHook.selector,
WorldRegistrationSystem.unregisterSystemHook.selector,
WorldRegistrationSystem.registerSystem.selector,
WorldRegistrationSystem.registerFunctionSelector.selector,
WorldRegistrationSystem.registerRootFunctionSelector.selector,
WorldRegistrationSystem.registerDelegation.selector
"registerNamespace(bytes32)",
"registerSystemHook(bytes32,address,uint8)",
"unregisterSystemHook(bytes32,address)",
"registerSystem(bytes32,address,bool)",
"registerFunctionSelector(bytes32,string)",
"registerRootFunctionSelector(bytes32,string,bytes4)",
"registerDelegation(address,bytes32,bytes)"
];

for (uint256 i = 0; i < functionSelectors.length; i++) {
for (uint256 i = 0; i < functionSignatures.length; i++) {
// Use the CoreSystem's `registerRootFunctionSelector` to register the
// root function selectors in the World.
WorldContextProvider.delegatecallWithContextOrRevert({
Expand All @@ -131,7 +131,7 @@ contract CoreModule is Module {
target: coreSystem,
callData: abi.encodeCall(
WorldRegistrationSystem.registerRootFunctionSelector,
(CORE_SYSTEM_ID, functionSelectors[i], functionSelectors[i])
(CORE_SYSTEM_ID, functionSignatures[i], bytes4(keccak256(bytes(functionSignatures[i]))))
)
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { SystemHooks, SystemHooksTableId } from "../tables/SystemHooks.sol";
import { SystemRegistry } from "../tables/SystemRegistry.sol";
import { Systems } from "../tables/Systems.sol";
import { FunctionSelectors } from "../tables/FunctionSelectors.sol";
import { FunctionSignatures } from "../tables/FunctionSignatures.sol";

/**
* Functions related to registering resources other than tables in the World.
Expand Down Expand Up @@ -159,9 +160,14 @@ contract WorldRegistrationSystem is System, IWorldErrors {
// Compute global function selector
string memory namespaceString = WorldResourceIdLib.toTrimmedString(systemId.getNamespace());
string memory nameString = WorldResourceIdLib.toTrimmedString(systemId.getName());
worldFunctionSelector = bytes4(
keccak256(abi.encodePacked(namespaceString, "_", nameString, "_", systemFunctionSignature))
bytes memory worldFunctionSignature = abi.encodePacked(
namespaceString,
"_",
nameString,
"_",
systemFunctionSignature
);
worldFunctionSelector = bytes4(keccak256(worldFunctionSignature));

// Require the function selector to be globally unique
bytes32 existingSystemId = FunctionSelectors._getSystemId(worldFunctionSelector);
Expand All @@ -171,6 +177,9 @@ contract WorldRegistrationSystem is System, IWorldErrors {
// Register the function selector
bytes4 systemFunctionSelector = bytes4(keccak256(bytes(systemFunctionSignature)));
FunctionSelectors._set(worldFunctionSelector, ResourceId.unwrap(systemId), systemFunctionSelector);

// Register the function signature for offchain use
FunctionSignatures._set(worldFunctionSelector, string(worldFunctionSignature));
}

/**
Expand All @@ -179,12 +188,15 @@ contract WorldRegistrationSystem is System, IWorldErrors {
*/
function registerRootFunctionSelector(
ResourceId systemId,
bytes4 worldFunctionSelector,
string memory worldFunctionSignature,
bytes4 systemFunctionSelector
) public returns (bytes4) {
) public returns (bytes4 worldFunctionSelector) {
// Require the caller to own the root namespace
AccessControl.requireOwner(ROOT_NAMESPACE_ID, _msgSender());

// Compute the function selector from the provided signature
worldFunctionSelector = bytes4(keccak256(bytes(worldFunctionSignature)));

// Require the function selector to be globally unique
bytes32 existingSystemId = FunctionSelectors._getSystemId(worldFunctionSelector);

Expand All @@ -193,7 +205,8 @@ contract WorldRegistrationSystem is System, IWorldErrors {
// Register the function selector
FunctionSelectors._set(worldFunctionSelector, ResourceId.unwrap(systemId), systemFunctionSelector);

return worldFunctionSelector;
// Register the function signature for offchain use
FunctionSignatures._set(worldFunctionSelector, worldFunctionSignature);
}

/**
Expand Down
Loading

0 comments on commit e5d208e

Please sign in to comment.