From 2bfee9217c0b08b2cca5b4a5aef6f4c2f0e7d2f2 Mon Sep 17 00:00:00 2001 From: yonada Date: Mon, 22 Jan 2024 19:20:32 +0000 Subject: [PATCH] fix(world): prevent initialising the world multiple times [L-05] (#2170) Co-authored-by: alvarius --- .changeset/neat-tools-check.md | 5 + packages/world-modules/gas-report.json | 62 ++--- packages/world/gas-report.json | 32 +-- packages/world/mud.config.ts | 4 + packages/world/src/World.sol | 5 +- packages/world/src/codegen/index.sol | 1 + .../src/codegen/tables/CoreModuleAddress.sol | 218 ++++++++++++++++++ .../world/src/modules/core/CoreModule.sol | 2 + 8 files changed, 281 insertions(+), 48 deletions(-) create mode 100644 .changeset/neat-tools-check.md create mode 100644 packages/world/src/codegen/tables/CoreModuleAddress.sol diff --git a/.changeset/neat-tools-check.md b/.changeset/neat-tools-check.md new file mode 100644 index 0000000000..f8d4fa1924 --- /dev/null +++ b/.changeset/neat-tools-check.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Added a table to track the `CoreModule` address the world was initialised with. diff --git a/packages/world-modules/gas-report.json b/packages/world-modules/gas-report.json index 33c0de667b..0752af8aee 100644 --- a/packages/world-modules/gas-report.json +++ b/packages/world-modules/gas-report.json @@ -3,85 +3,85 @@ "file": "test/ERC20.t.sol", "test": "testApprove", "name": "approve", - "gasUsed": 114348 + "gasUsed": 114354 }, { "file": "test/ERC20.t.sol", "test": "testBurn", "name": "burn", - "gasUsed": 75910 + "gasUsed": 75916 }, { "file": "test/ERC20.t.sol", "test": "testMint", "name": "mint", - "gasUsed": 161749 + "gasUsed": 161755 }, { "file": "test/ERC20.t.sol", "test": "testTransfer", "name": "transfer", - "gasUsed": 92995 + "gasUsed": 93001 }, { "file": "test/ERC20.t.sol", "test": "testTransferFrom", "name": "transferFrom", - "gasUsed": 130331 + "gasUsed": 130337 }, { "file": "test/ERC721.t.sol", "test": "testApproveAllGas", "name": "setApprovalForAll", - "gasUsed": 113972 + "gasUsed": 113978 }, { "file": "test/ERC721.t.sol", "test": "testApproveGas", "name": "approve", - "gasUsed": 87981 + "gasUsed": 87987 }, { "file": "test/ERC721.t.sol", "test": "testBurnGas", "name": "burn", - "gasUsed": 101910 + "gasUsed": 101916 }, { "file": "test/ERC721.t.sol", "test": "testMintGas", "name": "mint", - "gasUsed": 169480 + "gasUsed": 169486 }, { "file": "test/ERC721.t.sol", "test": "testSafeMintToEOAGas", "name": "safeMint", - "gasUsed": 169751 + "gasUsed": 169757 }, { "file": "test/ERC721.t.sol", "test": "testSafeTransferFromToEOAGas", "name": "safeTransferFrom", - "gasUsed": 143744 + "gasUsed": 143750 }, { "file": "test/ERC721.t.sol", "test": "testTransferFromGas", "name": "transferFrom", - "gasUsed": 136901 + "gasUsed": 136907 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1439079 + "gasUsed": 1439094 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1439079 + "gasUsed": 1439094 }, { "file": "test/KeysInTableModule.t.sol", @@ -93,13 +93,13 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1439079 + "gasUsed": 1439094 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1439079 + "gasUsed": 1439094 }, { "file": "test/KeysInTableModule.t.sol", @@ -117,7 +117,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1439079 + "gasUsed": 1439094 }, { "file": "test/KeysInTableModule.t.sol", @@ -135,7 +135,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 684459 + "gasUsed": 684471 }, { "file": "test/KeysWithValueModule.t.sol", @@ -153,7 +153,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 684459 + "gasUsed": 684471 }, { "file": "test/KeysWithValueModule.t.sol", @@ -165,7 +165,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 684459 + "gasUsed": 684471 }, { "file": "test/KeysWithValueModule.t.sol", @@ -183,7 +183,7 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 684459 + "gasUsed": 684471 }, { "file": "test/KeysWithValueModule.t.sol", @@ -267,60 +267,60 @@ "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 118198 + "gasUsed": 118201 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "call a system via a callbound delegation", - "gasUsed": 36679 + "gasUsed": 36685 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromSystemDelegation", "name": "register a systembound delegation", - "gasUsed": 115751 + "gasUsed": 115754 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromSystemDelegation", "name": "call a system via a systembound delegation", - "gasUsed": 33851 + "gasUsed": 33857 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 112674 + "gasUsed": 112677 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "call a system via a timebound delegation", - "gasUsed": 26797 + "gasUsed": 26803 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 704219 + "gasUsed": 704234 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 50365 + "gasUsed": 50368 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 673145 + "gasUsed": 673154 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 50368 + "gasUsed": 50371 } ] diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index 54da86f966..0696169fed 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -45,49 +45,49 @@ "file": "test/BatchCall.t.sol", "test": "testBatchCallFromReturnData", "name": "call systems with batchCallFrom", - "gasUsed": 52611 + "gasUsed": 52620 }, { "file": "test/BatchCall.t.sol", "test": "testBatchCallReturnData", "name": "call systems with batchCall", - "gasUsed": 51457 + "gasUsed": 51466 }, { "file": "test/Factories.t.sol", "test": "testCreate2Factory", "name": "deploy contract via Create2", - "gasUsed": 4659744 + "gasUsed": 4759984 }, { "file": "test/Factories.t.sol", "test": "testWorldFactory", "name": "deploy world via WorldFactory", - "gasUsed": 12642949 + "gasUsed": 13039359 }, { "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 12361 + "gasUsed": 12364 }, { "file": "test/World.t.sol", "test": "testCallFromNamespaceDelegation", "name": "call a system via a namespace fallback delegation", - "gasUsed": 26131 + "gasUsed": 26137 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 47607 + "gasUsed": 47610 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 12797 + "gasUsed": 12800 }, { "file": "test/World.t.sol", @@ -105,37 +105,37 @@ "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 84542 + "gasUsed": 84545 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 120969 + "gasUsed": 120972 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 80445 + "gasUsed": 80448 }, { "file": "test/World.t.sol", "test": "testRegisterSystem", "name": "register a system", - "gasUsed": 164363 + "gasUsed": 164366 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 536881 + "gasUsed": 536884 }, { "file": "test/World.t.sol", "test": "testRenounceNamespace", "name": "Renounce namespace ownership", - "gasUsed": 36773 + "gasUsed": 36776 }, { "file": "test/World.t.sol", @@ -153,13 +153,13 @@ "file": "test/World.t.sol", "test": "testUnregisterNamespaceDelegation", "name": "unregister a namespace delegation", - "gasUsed": 28360 + "gasUsed": 28363 }, { "file": "test/World.t.sol", "test": "testUnregisterUnlimitedDelegation", "name": "unregister an unlimited delegation", - "gasUsed": 26255 + "gasUsed": 26258 }, { "file": "test/WorldDynamicUpdate.t.sol", diff --git a/packages/world/mud.config.ts b/packages/world/mud.config.ts index 73a79a1393..ed898154b7 100644 --- a/packages/world/mud.config.ts +++ b/packages/world/mud.config.ts @@ -103,6 +103,10 @@ export default mudConfig({ }, offchainOnly: true, }, + CoreModuleAddress: { + keySchema: {}, + valueSchema: "address", + }, }, excludeSystems: [ // Worldgen currently does not support systems inheriting logic diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 6ddad55db0..920a9f9c28 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -19,6 +19,7 @@ import { requireInterface } from "./requireInterface.sol"; import { InstalledModules } from "./codegen/tables/InstalledModules.sol"; import { UserDelegationControl } from "./codegen/tables/UserDelegationControl.sol"; import { NamespaceDelegationControl } from "./codegen/tables/NamespaceDelegationControl.sol"; +import { CoreModuleAddress } from "./codegen/tables/CoreModuleAddress.sol"; import { IModule, IModule } from "./IModule.sol"; import { IWorldKernel } from "./IWorldKernel.sol"; @@ -71,10 +72,12 @@ contract World is StoreData, IWorldKernel { } // The World can only be initialized once - if (InstalledModules._get(CORE_MODULE_NAME, keccak256("")) != address(0)) { + if (CoreModuleAddress.get() != address(0)) { revert World_AlreadyInitialized(); } + CoreModuleAddress.set(address(coreModule)); + // Initialize the World by installing the core module _installRootModule(coreModule, new bytes(0)); } diff --git a/packages/world/src/codegen/index.sol b/packages/world/src/codegen/index.sol index 472f3049c3..65059df63f 100644 --- a/packages/world/src/codegen/index.sol +++ b/packages/world/src/codegen/index.sol @@ -14,3 +14,4 @@ import { SystemRegistry, SystemRegistryTableId } from "./tables/SystemRegistry.s import { SystemHooks, SystemHooksTableId } from "./tables/SystemHooks.sol"; import { FunctionSelectors, FunctionSelectorsTableId } from "./tables/FunctionSelectors.sol"; import { FunctionSignatures, FunctionSignaturesTableId } from "./tables/FunctionSignatures.sol"; +import { CoreModuleAddress, CoreModuleAddressTableId } from "./tables/CoreModuleAddress.sol"; diff --git a/packages/world/src/codegen/tables/CoreModuleAddress.sol b/packages/world/src/codegen/tables/CoreModuleAddress.sol new file mode 100644 index 0000000000..0933c2c60c --- /dev/null +++ b/packages/world/src/codegen/tables/CoreModuleAddress.sol @@ -0,0 +1,218 @@ +// 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_TABLE, bytes14("world"), bytes16("CoreModuleAddres"))) +); +ResourceId constant CoreModuleAddressTableId = _tableId; + +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0014010014000000000000000000000000000000000000000000000000000000 +); + +library CoreModuleAddress { + /** + * @notice Get the table values' field layout. + * @return _fieldLayout The field layout for the table. + */ + function getFieldLayout() internal pure returns (FieldLayout) { + return _fieldLayout; + } + + /** + * @notice Get the table's key schema. + * @return _keySchema The key schema for the table. + */ + function getKeySchema() internal pure returns (Schema) { + SchemaType[] memory _keySchema = new SchemaType[](0); + + return SchemaLib.encode(_keySchema); + } + + /** + * @notice Get the table's value schema. + * @return _valueSchema The value schema for the table. + */ + function getValueSchema() internal pure returns (Schema) { + SchemaType[] memory _valueSchema = new SchemaType[](1); + _valueSchema[0] = SchemaType.ADDRESS; + + return SchemaLib.encode(_valueSchema); + } + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](0); + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](1); + fieldNames[0] = "value"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** + * @notice Get value. + */ + function getValue() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get value. + */ + function _getValue() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get value. + */ + function get() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Get value. + */ + function _get() internal view returns (address value) { + bytes32[] memory _keyTuple = new bytes32[](0); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (address(bytes20(_blob))); + } + + /** + * @notice Set value. + */ + function setValue(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _setValue(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function set(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Set value. + */ + function _set(address value) internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((value)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord() internal { + bytes32[] memory _keyTuple = new bytes32[](0); + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(address value) internal pure returns (bytes memory) { + return abi.encodePacked(value); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode(address value) internal pure returns (bytes memory, PackedCounter, bytes memory) { + bytes memory _staticData = encodeStatic(value); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple() internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](0); + + return _keyTuple; + } +} diff --git a/packages/world/src/modules/core/CoreModule.sol b/packages/world/src/modules/core/CoreModule.sol index 0637cd92f2..3cef4271b7 100644 --- a/packages/world/src/modules/core/CoreModule.sol +++ b/packages/world/src/modules/core/CoreModule.sol @@ -28,6 +28,7 @@ import { FunctionSelectors } from "../../codegen/tables/FunctionSelectors.sol"; import { FunctionSignatures } from "../../codegen/tables/FunctionSignatures.sol"; import { SystemHooks } from "../../codegen/tables/SystemHooks.sol"; import { SystemRegistry } from "../../codegen/tables/SystemRegistry.sol"; +import { CoreModuleAddress } from "../../codegen/tables/CoreModuleAddress.sol"; import { Balances } from "../../codegen/tables/Balances.sol"; import { WorldRegistrationSystem } from "./implementations/WorldRegistrationSystem.sol"; @@ -99,6 +100,7 @@ contract CoreModule is Module { FunctionSignatures.register(); SystemHooks.register(); SystemRegistry.register(); + CoreModuleAddress.register(); ResourceIds._setExists(ROOT_NAMESPACE_ID, true); NamespaceOwner._set(ROOT_NAMESPACE_ID, _msgSender());