Skip to content

Commit

Permalink
add FunctionSignatures table, require function signature in registerR…
Browse files Browse the repository at this point in the history
…ootFunctionSelector
  • Loading branch information
alvrs committed Sep 22, 2023
1 parent 4aab3cf commit b75a0e5
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 56 deletions.
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
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 calldata 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 @@ -23,8 +23,6 @@ import { SystemRegistry } from "../tables/SystemRegistry.sol";
import { Systems } from "../tables/Systems.sol";
import { FunctionSelectors } from "../tables/FunctionSelectors.sol";

import { CORE_SYSTEM_ID } from "../constants.sol";

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

/**
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 calldata 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 signature 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
198 changes: 198 additions & 0 deletions packages/world/src/modules/core/tables/FunctionSignatures.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

/* Autogenerated file. Do not edit manually. */

// Import schema type
import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol";

// Import store internals
import { IStore } from "@latticexyz/store/src/IStore.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";
import { Bytes } from "@latticexyz/store/src/Bytes.sol";
import { Memory } from "@latticexyz/store/src/Memory.sol";
import { SliceLib } from "@latticexyz/store/src/Slice.sol";
import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol";
import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol";
import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol";
import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol";
import { ResourceId } from "@latticexyz/store/src/ResourceId.sol";
import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "@latticexyz/store/src/storeResourceTypes.sol";

ResourceId constant _tableId = ResourceId.wrap(
bytes32(abi.encodePacked(RESOURCE_OFFCHAIN_TABLE, bytes14(""), bytes16("FunctionSignatur")))
);
ResourceId constant FunctionSignaturesTableId = _tableId;

FieldLayout constant _fieldLayout = FieldLayout.wrap(
0x0000000100000000000000000000000000000000000000000000000000000000
);

library FunctionSignatures {
/** Get the table values' field layout */
function getFieldLayout() internal pure returns (FieldLayout) {
return _fieldLayout;
}

/** Get the table's key schema */
function getKeySchema() internal pure returns (Schema) {
SchemaType[] memory _keySchema = new SchemaType[](1);
_keySchema[0] = SchemaType.BYTES4;

return SchemaLib.encode(_keySchema);
}

/** Get the table's value schema */
function getValueSchema() internal pure returns (Schema) {
SchemaType[] memory _valueSchema = new SchemaType[](1);
_valueSchema[0] = SchemaType.STRING;

return SchemaLib.encode(_valueSchema);
}

/** Get the table's key names */
function getKeyNames() internal pure returns (string[] memory keyNames) {
keyNames = new string[](1);
keyNames[0] = "functionSelector";
}

/** Get the table's field names */
function getFieldNames() internal pure returns (string[] memory fieldNames) {
fieldNames = new string[](1);
fieldNames[0] = "functionSignature";
}

/** Register the table with its config */
function register() internal {
StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames());
}

/** Register the table with its config */
function _register() internal {
StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames());
}

/** Register the table with its config (using the specified store) */
function register(IStore _store) internal {
_store.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames());
}

/** Set the full data using individual values */
function set(bytes4 functionSelector, string memory functionSignature) internal {
bytes memory _staticData;
PackedCounter _encodedLengths = encodeLengths(functionSignature);
bytes memory _dynamicData = encodeDynamic(functionSignature);

bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout);
}

/** Set the full data using individual values */
function _set(bytes4 functionSelector, string memory functionSignature) internal {
bytes memory _staticData;
PackedCounter _encodedLengths = encodeLengths(functionSignature);
bytes memory _dynamicData = encodeDynamic(functionSignature);

bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout);
}

/** Set the full data using individual values (using the specified store) */
function set(IStore _store, bytes4 functionSelector, string memory functionSignature) internal {
bytes memory _staticData;
PackedCounter _encodedLengths = encodeLengths(functionSignature);
bytes memory _dynamicData = encodeDynamic(functionSignature);

bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

_store.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout);
}

/**
* Decode the tightly packed blob of static data using this table's field layout
* Undefined behaviour for invalid blobs
*/
function decodeDynamic(
PackedCounter _encodedLengths,
bytes memory _blob
) internal pure returns (string memory functionSignature) {
uint256 _start;
uint256 _end;
unchecked {
_end = _encodedLengths.atIndex(0);
}
functionSignature = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes()));
}

/**
* Decode the tightly packed blob using this table's field layout.
* Undefined behaviour for invalid blobs.
*/
function decode(
bytes memory,
PackedCounter _encodedLengths,
bytes memory _dynamicData
) internal pure returns (string memory functionSignature) {
(functionSignature) = decodeDynamic(_encodedLengths, _dynamicData);
}

/** Delete all data for given keys */
function deleteRecord(bytes4 functionSelector) internal {
bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

StoreSwitch.deleteRecord(_tableId, _keyTuple, _fieldLayout);
}

/** Delete all data for given keys */
function _deleteRecord(bytes4 functionSelector) internal {
bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout);
}

/** Delete all data for given keys (using the specified store) */
function deleteRecord(IStore _store, bytes4 functionSelector) internal {
bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

_store.deleteRecord(_tableId, _keyTuple, _fieldLayout);
}

/** Tightly pack dynamic data using this table's schema */
function encodeLengths(string memory functionSignature) internal pure returns (PackedCounter _encodedLengths) {
// Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits
unchecked {
_encodedLengths = PackedCounterLib.pack(bytes(functionSignature).length);
}
}

/** Tightly pack dynamic data using this table's schema */
function encodeDynamic(string memory functionSignature) internal pure returns (bytes memory) {
return abi.encodePacked(bytes((functionSignature)));
}

/** Tightly pack full data using this table's field layout */
function encode(string memory functionSignature) internal pure returns (bytes memory, PackedCounter, bytes memory) {
bytes memory _staticData;
PackedCounter _encodedLengths = encodeLengths(functionSignature);
bytes memory _dynamicData = encodeDynamic(functionSignature);

return (_staticData, _encodedLengths, _dynamicData);
}

/** Encode keys as a bytes32 array using this table's field layout */
function encodeKeyTuple(bytes4 functionSelector) internal pure returns (bytes32[] memory) {
bytes32[] memory _keyTuple = new bytes32[](1);
_keyTuple[0] = bytes32(functionSelector);

return _keyTuple;
}
}
Loading

0 comments on commit b75a0e5

Please sign in to comment.