From f93cf2de1c2dbbc002a8513be2f0484ea4c2fbbf Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 11 Sep 2023 13:49:41 +0100 Subject: [PATCH 1/6] don't index singleton tables --- .../src/modules/keysintable/KeysInTableModule.sol | 7 +++++++ .../modules/keyswithvalue/KeysWithValueModule.sol | 13 +++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/world/src/modules/keysintable/KeysInTableModule.sol b/packages/world/src/modules/keysintable/KeysInTableModule.sol index 253d9d0f9f..0fe0d978d5 100644 --- a/packages/world/src/modules/keysintable/KeysInTableModule.sol +++ b/packages/world/src/modules/keysintable/KeysInTableModule.sol @@ -29,6 +29,8 @@ import { UsedKeysIndex, UsedKeysIndexTableId } from "./tables/UsedKeysIndex.sol" contract KeysInTableModule is IModule, WorldContextConsumer { using ResourceSelector for bytes32; + error KeysInTableModule_EmptyKeySchema(); + // The KeysInTableHook is deployed once and infers the target table id // from the source table id (passed as argument to the hook methods) KeysInTableHook immutable hook = new KeysInTableHook(); @@ -43,6 +45,11 @@ contract KeysInTableModule is IModule, WorldContextConsumer { IBaseWorld world = IBaseWorld(_world()); + // It doesn't make sense to index keys of a singleton table (only a single value) + if (world.getKeySchema(sourceTableId).isEmpty()) { + revert KeysInTableModule_EmptyKeySchema(); + } + if (ResourceType.get(KeysInTableTableId) == Resource.NONE) { // Register the tables KeysInTable.register(world); diff --git a/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol b/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol index 1cd17800d2..aca6528ddb 100644 --- a/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol +++ b/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol @@ -29,6 +29,8 @@ import { getTargetTableSelector } from "../utils/getTargetTableSelector.sol"; contract KeysWithValueModule is IModule, WorldContextConsumer { using ResourceSelector for bytes32; + error KeysWithValueModule_EmptyKeySchema(); + // The KeysWithValueHook is deployed once and infers the target table id // from the source table id (passed as argument to the hook methods) KeysWithValueHook immutable hook = new KeysWithValueHook(); @@ -42,11 +44,18 @@ contract KeysWithValueModule is IModule, WorldContextConsumer { bytes32 sourceTableId = abi.decode(args, (bytes32)); bytes32 targetTableSelector = getTargetTableSelector(MODULE_NAMESPACE, sourceTableId); + IBaseWorld world = IBaseWorld(_world()); + + // It doesn't make sense to index keys of a singleton table (only a single value) + if (world.getKeySchema(sourceTableId).isEmpty()) { + revert KeysWithValueModule_EmptyKeySchema(); + } + // Register the target table - KeysWithValue.register(IBaseWorld(_world()), targetTableSelector); + KeysWithValue.register(world, targetTableSelector); // Grant the hook access to the target table - IBaseWorld(_world()).grantAccess(targetTableSelector, address(hook)); + world.grantAccess(targetTableSelector, address(hook)); // Register a hook that is called when a value is set in the source table StoreSwitch.registerStoreHook( From 78ba95cb3c26ecd2da94c2533d26d6fbcb41c304 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 11 Sep 2023 14:35:53 +0100 Subject: [PATCH 2/6] update tests --- packages/store/src/IStore.sol | 2 + packages/store/src/StoreRead.sol | 4 + packages/store/test/SchemaEncodeHelper.sol | 5 ++ .../modules/keysintable/KeysInTableModule.sol | 8 +- .../keyswithvalue/KeysWithValueModule.sol | 6 +- packages/world/test/KeysInTableModule.t.sol | 82 ++++++++----------- packages/world/test/KeysWithValueModule.t.sol | 70 +++++++++------- 7 files changed, 95 insertions(+), 82 deletions(-) diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index 988e9c5b25..8a1699ac1f 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -5,6 +5,8 @@ import { IStoreErrors } from "./IStoreErrors.sol"; import { Schema } from "./Schema.sol"; interface IStoreRead { + function hasTable(bytes32 table) external view returns (bool); + function getValueSchema(bytes32 table) external view returns (Schema schema); function getKeySchema(bytes32 table) external view returns (Schema schema); diff --git a/packages/store/src/StoreRead.sol b/packages/store/src/StoreRead.sol index 7c6ffe0a30..4d28cd1a07 100644 --- a/packages/store/src/StoreRead.sol +++ b/packages/store/src/StoreRead.sol @@ -10,6 +10,10 @@ contract StoreRead is IStoreRead { StoreCore.initialize(); } + function hasTable(bytes32 table) public view virtual returns (bool) { + return StoreCore.hasTable(table); + } + function getValueSchema(bytes32 table) public view virtual returns (Schema schema) { schema = StoreCore.getValueSchema(table); } diff --git a/packages/store/test/SchemaEncodeHelper.sol b/packages/store/test/SchemaEncodeHelper.sol index 2fc21b70fa..343a3d94ef 100644 --- a/packages/store/test/SchemaEncodeHelper.sol +++ b/packages/store/test/SchemaEncodeHelper.sol @@ -8,6 +8,11 @@ import { Schema, SchemaLib } from "../src/Schema.sol"; * Overrides for encode function to simplify tests */ library SchemaEncodeHelper { + function encode() internal pure returns (Schema) { + SchemaType[] memory schema = new SchemaType[](0); + return SchemaLib.encode(schema); + } + function encode(SchemaType a) internal pure returns (Schema) { SchemaType[] memory schema = new SchemaType[](1); schema[0] = a; diff --git a/packages/world/src/modules/keysintable/KeysInTableModule.sol b/packages/world/src/modules/keysintable/KeysInTableModule.sol index 0fe0d978d5..9ab6988e3c 100644 --- a/packages/world/src/modules/keysintable/KeysInTableModule.sol +++ b/packages/world/src/modules/keysintable/KeysInTableModule.sol @@ -29,11 +29,12 @@ import { UsedKeysIndex, UsedKeysIndexTableId } from "./tables/UsedKeysIndex.sol" contract KeysInTableModule is IModule, WorldContextConsumer { using ResourceSelector for bytes32; + error KeysInTableModule_TableNotFound(); error KeysInTableModule_EmptyKeySchema(); // The KeysInTableHook is deployed once and infers the target table id // from the source table id (passed as argument to the hook methods) - KeysInTableHook immutable hook = new KeysInTableHook(); + KeysInTableHook public immutable hook = new KeysInTableHook(); function getName() public pure returns (bytes16) { return bytes16("keysInTable"); @@ -45,8 +46,11 @@ contract KeysInTableModule is IModule, WorldContextConsumer { IBaseWorld world = IBaseWorld(_world()); + if (!world.hasTable(sourceTableId)) { + revert KeysInTableModule_TableNotFound(); + } // It doesn't make sense to index keys of a singleton table (only a single value) - if (world.getKeySchema(sourceTableId).isEmpty()) { + if (world.hasTable(sourceTableId) && world.getKeySchema(sourceTableId).isEmpty()) { revert KeysInTableModule_EmptyKeySchema(); } diff --git a/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol b/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol index aca6528ddb..e642e88feb 100644 --- a/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol +++ b/packages/world/src/modules/keyswithvalue/KeysWithValueModule.sol @@ -29,11 +29,12 @@ import { getTargetTableSelector } from "../utils/getTargetTableSelector.sol"; contract KeysWithValueModule is IModule, WorldContextConsumer { using ResourceSelector for bytes32; + error KeysWithValueModule_TableNotFound(); error KeysWithValueModule_EmptyKeySchema(); // The KeysWithValueHook is deployed once and infers the target table id // from the source table id (passed as argument to the hook methods) - KeysWithValueHook immutable hook = new KeysWithValueHook(); + KeysWithValueHook public immutable hook = new KeysWithValueHook(); function getName() public pure returns (bytes16) { return bytes16("index"); @@ -46,6 +47,9 @@ contract KeysWithValueModule is IModule, WorldContextConsumer { IBaseWorld world = IBaseWorld(_world()); + if (!world.hasTable(sourceTableId)) { + revert KeysWithValueModule_TableNotFound(); + } // It doesn't make sense to index keys of a singleton table (only a single value) if (world.getKeySchema(sourceTableId).isEmpty()) { revert KeysWithValueModule_EmptyKeySchema(); diff --git a/packages/world/test/KeysInTableModule.t.sol b/packages/world/test/KeysInTableModule.t.sol index 1968d14a00..a81e9a1ee6 100644 --- a/packages/world/test/KeysInTableModule.t.sol +++ b/packages/world/test/KeysInTableModule.t.sol @@ -20,41 +20,41 @@ import { hasKey } from "../src/modules/keysintable/hasKey.sol"; contract KeysInTableModuleTest is Test, GasReporter { using ResourceSelector for bytes32; - IBaseWorld world; - KeysInTableModule keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times - - bytes16 namespace = ROOT_NAMESPACE; - bytes16 name = bytes16("source"); - bytes16 singletonName = bytes16("singleton"); - bytes16 compositeName = bytes16("composite"); - bytes32 key1 = keccak256("test"); - bytes32[] keyTuple1; - bytes32 key2 = keccak256("test2"); - bytes32[] keyTuple2; - bytes32 key3 = keccak256("test3"); - bytes32[] keyTuple3; - - Schema tableValueSchema; - Schema tableKeySchema; - Schema singletonKeySchema; - Schema compositeKeySchema; - bytes32 tableId = ResourceSelector.from(namespace, name); - bytes32 singletonTableId = ResourceSelector.from(namespace, singletonName); - bytes32 compositeTableId = ResourceSelector.from(namespace, compositeName); - - uint256 val1 = 123; - uint256 val2 = 42; + IBaseWorld public world; + KeysInTableModule public keysInTableModule = new KeysInTableModule(); // Modules can be deployed once and installed multiple times + + bytes16 public namespace = ROOT_NAMESPACE; + bytes16 public name = bytes16("source"); + bytes16 public singletonName = bytes16("singleton"); + bytes16 public compositeName = bytes16("composite"); + bytes32 public key1 = keccak256("test"); + bytes32[] public keyTuple1; + bytes32 public key2 = keccak256("test2"); + bytes32[] public keyTuple2; + bytes32 public key3 = keccak256("test3"); + bytes32[] public keyTuple3; + + bytes32 public tableId = ResourceSelector.from(namespace, name); + bytes32 public singletonTableId = ResourceSelector.from(namespace, singletonName); + bytes32 public compositeTableId = ResourceSelector.from(namespace, compositeName); + + Schema public tableValueSchema = SchemaEncodeHelper.encode(SchemaType.UINT256); + Schema public tableKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); + Schema public compositeKeySchema = + SchemaEncodeHelper.encode(SchemaType.BYTES32, SchemaType.BYTES32, SchemaType.BYTES32); + Schema public singletonKeySchema = SchemaEncodeHelper.encode(); + + uint256 public val1 = 123; + uint256 public val2 = 42; function setUp() public { - tableValueSchema = SchemaEncodeHelper.encode(SchemaType.UINT256); - tableKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); - compositeKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32, SchemaType.BYTES32, SchemaType.BYTES32); - - SchemaType[] memory _schema = new SchemaType[](0); - singletonKeySchema = SchemaLib.encode(_schema); - world = IBaseWorld(address(new World())); world.installRootModule(new CoreModule(), new bytes(0)); + + world.registerTable(tableId, tableKeySchema, tableValueSchema, new string[](1), new string[](1)); + world.registerTable(singletonTableId, singletonKeySchema, tableValueSchema, new string[](0), new string[](1)); + world.registerTable(compositeTableId, compositeKeySchema, tableValueSchema, new string[](3), new string[](1)); + keyTuple1 = new bytes32[](1); keyTuple1[0] = key1; keyTuple2 = new bytes32[](1); @@ -64,35 +64,19 @@ contract KeysInTableModuleTest is Test, GasReporter { } function _installKeysInTableModule() internal { - // Register source table - world.registerTable(tableId, tableKeySchema, tableValueSchema, new string[](1), new string[](1)); - world.registerTable(singletonTableId, singletonKeySchema, tableValueSchema, new string[](0), new string[](1)); - world.registerTable(compositeTableId, compositeKeySchema, tableValueSchema, new string[](3), new string[](1)); - // Install the index module // TODO: add support for installing this via installModule // -> requires `callFrom` for the module to be able to register a hook in the name of the original caller startGasReport("install keys in table module"); world.installRootModule(keysInTableModule, abi.encode(tableId)); endGasReport(); - world.installRootModule(keysInTableModule, abi.encode(singletonTableId)); world.installRootModule(keysInTableModule, abi.encode(compositeTableId)); } // This test is expected to fail because `getKeySchema()` on StoreCore reverts on singleton tables - // TODO: we need to be able to determine whether a table is singleton vs. nonexistent function testInstallSingleton() public { - _installKeysInTableModule(); - - bytes32[] memory keyTuple = new bytes32[](0); - - world.setRecord(singletonTableId, keyTuple, abi.encodePacked(val1), tableValueSchema); - - // Get the list of keys in this target table - bytes32[][] memory keysInTable = getKeysInTable(world, singletonTableId); - - // Assert that the list is correct - assertEq(keysInTable.length, 0); + vm.expectRevert(); + world.installRootModule(keysInTableModule, abi.encode(singletonTableId)); } function testInstallComposite() public { diff --git a/packages/world/test/KeysWithValueModule.t.sol b/packages/world/test/KeysWithValueModule.t.sol index 4e27ae8cc7..82747d7e4d 100644 --- a/packages/world/test/KeysWithValueModule.t.sol +++ b/packages/world/test/KeysWithValueModule.t.sol @@ -22,38 +22,43 @@ import { getTargetTableSelector } from "../src/modules/utils/getTargetTableSelec contract KeysWithValueModuleTest is Test, GasReporter { using ResourceSelector for bytes32; - IBaseWorld world; - KeysWithValueModule keysWithValueModule = new KeysWithValueModule(); // Modules can be deployed once and installed multiple times - - bytes16 namespace = ROOT_NAMESPACE; - bytes16 sourceName = bytes16("source"); - bytes32 key1 = keccak256("test"); - bytes32[] keyTuple1; - bytes32 key2 = keccak256("test2"); - bytes32[] keyTuple2; - - Schema sourceTableSchema; - Schema sourceTableKeySchema; - bytes32 sourceTableId; - bytes32 targetTableId; + IBaseWorld public world; + KeysWithValueModule public keysWithValueModule = new KeysWithValueModule(); // Modules can be deployed once and installed multiple times + + bytes16 public namespace = ROOT_NAMESPACE; + bytes16 public sourceName = bytes16("source"); + bytes16 public singletonName = bytes16("singleton"); + bytes32 public key1 = keccak256("test"); + bytes32[] public keyTuple1; + bytes32 public key2 = keccak256("test2"); + bytes32[] public keyTuple2; + + Schema public sourceTableValueSchema = SchemaEncodeHelper.encode(SchemaType.UINT256); + Schema public sourceTableKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); + bytes32 public sourceTableId = ResourceSelector.from(namespace, sourceName); + bytes32 public singletonTableId = ResourceSelector.from(namespace, singletonName); + bytes32 public targetTableId = getTargetTableSelector(MODULE_NAMESPACE, sourceTableId); function setUp() public { - sourceTableSchema = SchemaEncodeHelper.encode(SchemaType.UINT256); - sourceTableKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); world = IBaseWorld(address(new World())); world.installRootModule(new CoreModule(), new bytes(0)); + + world.registerTable(sourceTableId, sourceTableKeySchema, sourceTableValueSchema, new string[](1), new string[](1)); + world.registerTable( + singletonTableId, + SchemaEncodeHelper.encode(), + SchemaEncodeHelper.encode(SchemaType.BOOL), + new string[](0), + new string[](1) + ); + keyTuple1 = new bytes32[](1); keyTuple1[0] = key1; keyTuple2 = new bytes32[](1); keyTuple2[0] = key2; - sourceTableId = ResourceSelector.from(namespace, sourceName); - targetTableId = getTargetTableSelector(MODULE_NAMESPACE, sourceTableId); } function _installKeysWithValueModule() internal { - // Register source table - world.registerTable(sourceTableId, sourceTableSchema, sourceTableKeySchema, new string[](1), new string[](1)); - // Install the index module // TODO: add support for installing this via installModule // -> requires `callFrom` for the module to be able to register a hook in the name of the original caller @@ -68,7 +73,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { uint256 value = 1; startGasReport("set a record on a table with KeysWithValueModule installed"); - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value), sourceTableSchema); + world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value), sourceTableValueSchema); endGasReport(); // Get the list of entities with this value from the target table @@ -85,7 +90,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { // Set a value in the source table uint256 value1 = 1; - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value1), sourceTableSchema); + world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value1), sourceTableValueSchema); // Get the list of entities with value1 from the target table bytes32[] memory keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); @@ -95,7 +100,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { assertEq(keysWithValue[0], key1, "2"); // Set a another key with the same value - world.setRecord(sourceTableId, keyTuple2, abi.encodePacked(value1), sourceTableSchema); + world.setRecord(sourceTableId, keyTuple2, abi.encodePacked(value1), sourceTableValueSchema); // Get the list of entities with value2 from the target table keysWithValue = KeysWithValue.get(world, targetTableId, keccak256(abi.encode(value1))); @@ -109,7 +114,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { uint256 value2 = 2; startGasReport("change a record on a table with KeysWithValueModule installed"); - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value2), sourceTableSchema); + world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value2), sourceTableValueSchema); endGasReport(); // Get the list of entities with value1 from the target table @@ -128,7 +133,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { // Delete the first key startGasReport("delete a record on a table with KeysWithValueModule installed"); - world.deleteRecord(sourceTableId, keyTuple1, sourceTableSchema); + world.deleteRecord(sourceTableId, keyTuple1, sourceTableValueSchema); endGasReport(); // Get the list of entities with value2 from the target table @@ -145,7 +150,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { uint256 value1 = 1; startGasReport("set a field on a table with KeysWithValueModule installed"); - world.setField(sourceTableId, keyTuple1, 0, abi.encodePacked(value1), sourceTableSchema); + world.setField(sourceTableId, keyTuple1, 0, abi.encodePacked(value1), sourceTableValueSchema); endGasReport(); // Get the list of entities with value1 from the target table @@ -159,7 +164,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { // Change the value using setField startGasReport("change a field on a table with KeysWithValueModule installed"); - world.setField(sourceTableId, keyTuple1, 0, abi.encodePacked(value2), sourceTableSchema); + world.setField(sourceTableId, keyTuple1, 0, abi.encodePacked(value2), sourceTableValueSchema); endGasReport(); // Get the list of entities with value1 from the target table @@ -200,7 +205,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { _installKeysWithValueModule(); // Set a value in the source table - world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value), sourceTableSchema); + world.setRecord(sourceTableId, keyTuple1, abi.encodePacked(value), sourceTableValueSchema); startGasReport("Get list of keys with a given value"); bytes32[] memory keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value)); @@ -211,7 +216,7 @@ contract KeysWithValueModuleTest is Test, GasReporter { assertEq(keysWithValue[0], key1); // Set a another key with the same value - world.setRecord(sourceTableId, keyTuple2, abi.encodePacked(value), sourceTableSchema); + world.setRecord(sourceTableId, keyTuple2, abi.encodePacked(value), sourceTableValueSchema); // Get the list of keys with value from the target table keysWithValue = getKeysWithValue(world, sourceTableId, abi.encode(value)); @@ -221,4 +226,9 @@ contract KeysWithValueModuleTest is Test, GasReporter { assertEq(keysWithValue[0], key1); assertEq(keysWithValue[1], key2); } + + function testInstallSingleton() public { + vm.expectRevert(); + world.installRootModule(keysWithValueModule, abi.encode(singletonTableId)); + } } From 4af68509441e797c1dd6bb93034d9c9669efc3c4 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 11 Sep 2023 14:38:09 +0100 Subject: [PATCH 3/6] add note --- packages/world/src/modules/keysintable/KeysInTableModule.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/world/src/modules/keysintable/KeysInTableModule.sol b/packages/world/src/modules/keysintable/KeysInTableModule.sol index 9ab6988e3c..9ea237c4d3 100644 --- a/packages/world/src/modules/keysintable/KeysInTableModule.sol +++ b/packages/world/src/modules/keysintable/KeysInTableModule.sol @@ -54,6 +54,7 @@ contract KeysInTableModule is IModule, WorldContextConsumer { revert KeysInTableModule_EmptyKeySchema(); } + // TODO: should this use `world.hasTable`? if (ResourceType.get(KeysInTableTableId) == Resource.NONE) { // Register the tables KeysInTable.register(world); From 6234cf9d7e6130b32460b06840f3d6dd8d505a62 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 11 Sep 2023 14:45:31 +0100 Subject: [PATCH 4/6] improve test --- packages/world/test/KeysInTableModule.t.sol | 2 +- packages/world/test/KeysWithValueModule.t.sol | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/world/test/KeysInTableModule.t.sol b/packages/world/test/KeysInTableModule.t.sol index a81e9a1ee6..9b4f88b059 100644 --- a/packages/world/test/KeysInTableModule.t.sol +++ b/packages/world/test/KeysInTableModule.t.sol @@ -75,7 +75,7 @@ contract KeysInTableModuleTest is Test, GasReporter { // This test is expected to fail because `getKeySchema()` on StoreCore reverts on singleton tables function testInstallSingleton() public { - vm.expectRevert(); + vm.expectRevert(KeysInTableModule.KeysInTableModule_EmptyKeySchema.selector); world.installRootModule(keysInTableModule, abi.encode(singletonTableId)); } diff --git a/packages/world/test/KeysWithValueModule.t.sol b/packages/world/test/KeysWithValueModule.t.sol index 82747d7e4d..53a26f2293 100644 --- a/packages/world/test/KeysWithValueModule.t.sol +++ b/packages/world/test/KeysWithValueModule.t.sol @@ -84,6 +84,11 @@ contract KeysWithValueModuleTest is Test, GasReporter { assertEq(keysWithValue[0], key1); } + function testInstallSingleton() public { + vm.expectRevert(KeysWithValueModule.KeysWithValueModule_EmptyKeySchema.selector); + world.installRootModule(keysWithValueModule, abi.encode(singletonTableId)); + } + function testSetAndDeleteRecordHook() public { _installKeysWithValueModule(); @@ -226,9 +231,4 @@ contract KeysWithValueModuleTest is Test, GasReporter { assertEq(keysWithValue[0], key1); assertEq(keysWithValue[1], key2); } - - function testInstallSingleton() public { - vm.expectRevert(); - world.installRootModule(keysWithValueModule, abi.encode(singletonTableId)); - } } From 21441b08cfaf2546e336def5b66a0e05281c3fce Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Mon, 11 Sep 2023 14:46:20 +0100 Subject: [PATCH 5/6] update gas report --- packages/store/gas-report.json | 12 ++--- packages/world/gas-report.json | 92 ++++++++++++++++------------------ 2 files changed, 49 insertions(+), 55 deletions(-) diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index dcfe0a654a..25713f6d1e 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -585,19 +585,19 @@ "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set record on table with subscriber", - "gasUsed": 70983 + "gasUsed": 71006 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set static field on table with subscriber", - "gasUsed": 24290 + "gasUsed": 24333 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "delete record on table with subscriber", - "gasUsed": 19379 + "gasUsed": 19357 }, { "file": "test/StoreCoreGas.t.sol", @@ -609,19 +609,19 @@ "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) record on table with subscriber", - "gasUsed": 163851 + "gasUsed": 163874 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) field on table with subscriber", - "gasUsed": 26226 + "gasUsed": 26269 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "delete (dynamic) record on table with subscriber", - "gasUsed": 20852 + "gasUsed": 20830 }, { "file": "test/StoreCoreGas.t.sol", diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index ec99ed9234..1dede4e06a 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -39,73 +39,67 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1412624 + "gasUsed": 1467388 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1412624 + "gasUsed": 1467388 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 182589 - }, - { - "file": "test/KeysInTableModule.t.sol", - "test": "testInstallSingleton", - "name": "install keys in table module", - "gasUsed": 1412624 + "gasUsed": 184523 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1412624 + "gasUsed": 1467388 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 26174 + "gasUsed": 26152 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 251091 + "gasUsed": 251116 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1412624 + "gasUsed": 1467388 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 24894 + "gasUsed": 24872 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 129380 + "gasUsed": 129493 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 651087 + "gasUsed": 697319 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "Get list of keys with a given value", - "gasUsed": 6909 + "gasUsed": 6908 }, { "file": "test/KeysWithValueModule.t.sol", @@ -117,79 +111,79 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 651087 + "gasUsed": 697319 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 151906 + "gasUsed": 153929 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 651087 + "gasUsed": 697319 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 118369 + "gasUsed": 118391 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 43982 + "gasUsed": 44095 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 651087 + "gasUsed": 697319 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 158593 + "gasUsed": 160683 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 120851 + "gasUsed": 120940 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "gasUsed": 165765 + "gasUsed": 165435 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "gasUsed": 76018 + "gasUsed": 75908 }, { "file": "test/query.t.sol", "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "gasUsed": 229965 + "gasUsed": 229393 }, { "file": "test/query.t.sol", "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "gasUsed": 151676 + "gasUsed": 151302 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 143536 + "gasUsed": 143206 }, { "file": "test/query.t.sol", @@ -201,19 +195,19 @@ "file": "test/query.t.sol", "test": "testHasQuery", "name": "HasQuery", - "gasUsed": 34905 + "gasUsed": 34817 }, { "file": "test/query.t.sol", "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "gasUsed": 9272667 + "gasUsed": 9250623 }, { "file": "test/query.t.sol", "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "gasUsed": 861400 + "gasUsed": 859156 }, { "file": "test/query.t.sol", @@ -225,43 +219,43 @@ "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 69612 + "gasUsed": 69502 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 122373 + "gasUsed": 122395 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "call a system via a callbound delegation", - "gasUsed": 44114 + "gasUsed": 44180 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 116622 + "gasUsed": 116644 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "call a system via a timebound delegation", - "gasUsed": 34819 + "gasUsed": 34885 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 726567 + "gasUsed": 726589 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 65194 + "gasUsed": 65261 }, { "file": "test/UniqueEntityModule.t.sol", @@ -273,31 +267,31 @@ "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 65194 + "gasUsed": 65261 }, { "file": "test/World.t.sol", "test": "testCall", "name": "call a system via the World", - "gasUsed": 17519 + "gasUsed": 17497 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 55485 + "gasUsed": 55507 }, { "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "call a system via an unlimited delegation", - "gasUsed": 17893 + "gasUsed": 17959 }, { "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 12229 + "gasUsed": 12296 }, { "file": "test/World.t.sol", @@ -345,25 +339,25 @@ "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 40722 + "gasUsed": 40789 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 39585 + "gasUsed": 39652 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (cold)", - "gasUsed": 31097 + "gasUsed": 31075 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromField", "name": "pop 1 address (warm)", - "gasUsed": 17887 + "gasUsed": 17865 }, { "file": "test/WorldDynamicUpdate.t.sol", From e3a08b573037081a6f4338c451c1326e8e841034 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Tue, 12 Sep 2023 09:33:06 +0100 Subject: [PATCH 6/6] update abis --- packages/store/abi/IStore.sol/IStore.abi.json | 19 +++++++++++++++++++ .../store/abi/IStore.sol/IStore.abi.json.d.ts | 19 +++++++++++++++++++ .../store/abi/IStore.sol/IStoreData.abi.json | 19 +++++++++++++++++++ .../abi/IStore.sol/IStoreData.abi.json.d.ts | 19 +++++++++++++++++++ .../store/abi/IStore.sol/IStoreRead.abi.json | 19 +++++++++++++++++++ .../abi/IStore.sol/IStoreRead.abi.json.d.ts | 19 +++++++++++++++++++ .../abi/StoreMock.sol/StoreMock.abi.json | 19 +++++++++++++++++++ .../abi/StoreMock.sol/StoreMock.abi.json.d.ts | 19 +++++++++++++++++++ .../abi/StoreRead.sol/StoreRead.abi.json | 19 +++++++++++++++++++ .../abi/StoreRead.sol/StoreRead.abi.json.d.ts | 19 +++++++++++++++++++ .../StoreReadWithStubs.abi.json | 19 +++++++++++++++++++ .../StoreReadWithStubs.abi.json.d.ts | 19 +++++++++++++++++++ 12 files changed, 228 insertions(+) diff --git a/packages/store/abi/IStore.sol/IStore.abi.json b/packages/store/abi/IStore.sol/IStore.abi.json index 48e69022f3..803466c39b 100644 --- a/packages/store/abi/IStore.sol/IStore.abi.json +++ b/packages/store/abi/IStore.sol/IStore.abi.json @@ -435,6 +435,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "table", + "type": "bytes32" + } + ], + "name": "hasTable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/packages/store/abi/IStore.sol/IStore.abi.json.d.ts b/packages/store/abi/IStore.sol/IStore.abi.json.d.ts index 9838b2e184..f7ed4fbb16 100644 --- a/packages/store/abi/IStore.sol/IStore.abi.json.d.ts +++ b/packages/store/abi/IStore.sol/IStore.abi.json.d.ts @@ -435,6 +435,25 @@ declare const abi: [ stateMutability: "view"; type: "function"; }, + { + inputs: [ + { + internalType: "bytes32"; + name: "table"; + type: "bytes32"; + } + ]; + name: "hasTable"; + outputs: [ + { + internalType: "bool"; + name: ""; + type: "bool"; + } + ]; + stateMutability: "view"; + type: "function"; + }, { inputs: [ { diff --git a/packages/store/abi/IStore.sol/IStoreData.abi.json b/packages/store/abi/IStore.sol/IStoreData.abi.json index f02203d5e9..4431398745 100644 --- a/packages/store/abi/IStore.sol/IStoreData.abi.json +++ b/packages/store/abi/IStore.sol/IStoreData.abi.json @@ -276,6 +276,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "table", + "type": "bytes32" + } + ], + "name": "hasTable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/packages/store/abi/IStore.sol/IStoreData.abi.json.d.ts b/packages/store/abi/IStore.sol/IStoreData.abi.json.d.ts index 069dc301f8..dc99eeefa4 100644 --- a/packages/store/abi/IStore.sol/IStoreData.abi.json.d.ts +++ b/packages/store/abi/IStore.sol/IStoreData.abi.json.d.ts @@ -276,6 +276,25 @@ declare const abi: [ stateMutability: "view"; type: "function"; }, + { + inputs: [ + { + internalType: "bytes32"; + name: "table"; + type: "bytes32"; + } + ]; + name: "hasTable"; + outputs: [ + { + internalType: "bool"; + name: ""; + type: "bool"; + } + ]; + stateMutability: "view"; + type: "function"; + }, { inputs: [ { diff --git a/packages/store/abi/IStore.sol/IStoreRead.abi.json b/packages/store/abi/IStore.sol/IStoreRead.abi.json index 0f47144f3d..a3ccf9836a 100644 --- a/packages/store/abi/IStore.sol/IStoreRead.abi.json +++ b/packages/store/abi/IStore.sol/IStoreRead.abi.json @@ -177,5 +177,24 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "table", + "type": "bytes32" + } + ], + "name": "hasTable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" } ] \ No newline at end of file diff --git a/packages/store/abi/IStore.sol/IStoreRead.abi.json.d.ts b/packages/store/abi/IStore.sol/IStoreRead.abi.json.d.ts index 1fcf24a77e..73819af396 100644 --- a/packages/store/abi/IStore.sol/IStoreRead.abi.json.d.ts +++ b/packages/store/abi/IStore.sol/IStoreRead.abi.json.d.ts @@ -177,6 +177,25 @@ declare const abi: [ ]; stateMutability: "view"; type: "function"; + }, + { + inputs: [ + { + internalType: "bytes32"; + name: "table"; + type: "bytes32"; + } + ]; + name: "hasTable"; + outputs: [ + { + internalType: "bool"; + name: ""; + type: "bool"; + } + ]; + stateMutability: "view"; + type: "function"; } ]; export default abi; diff --git a/packages/store/abi/StoreMock.sol/StoreMock.abi.json b/packages/store/abi/StoreMock.sol/StoreMock.abi.json index 276498c2ca..ab251b699e 100644 --- a/packages/store/abi/StoreMock.sol/StoreMock.abi.json +++ b/packages/store/abi/StoreMock.sol/StoreMock.abi.json @@ -483,6 +483,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "table", + "type": "bytes32" + } + ], + "name": "hasTable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts b/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts index c04f073704..7545d50970 100644 --- a/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts +++ b/packages/store/abi/StoreMock.sol/StoreMock.abi.json.d.ts @@ -483,6 +483,25 @@ declare const abi: [ stateMutability: "view"; type: "function"; }, + { + inputs: [ + { + internalType: "bytes32"; + name: "table"; + type: "bytes32"; + } + ]; + name: "hasTable"; + outputs: [ + { + internalType: "bool"; + name: ""; + type: "bool"; + } + ]; + stateMutability: "view"; + type: "function"; + }, { inputs: [ { diff --git a/packages/store/abi/StoreRead.sol/StoreRead.abi.json b/packages/store/abi/StoreRead.sol/StoreRead.abi.json index 93a0e17cf5..1fc7fe4515 100644 --- a/packages/store/abi/StoreRead.sol/StoreRead.abi.json +++ b/packages/store/abi/StoreRead.sol/StoreRead.abi.json @@ -304,5 +304,24 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "table", + "type": "bytes32" + } + ], + "name": "hasTable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" } ] \ No newline at end of file diff --git a/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts b/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts index 13d190bb30..35817d11e9 100644 --- a/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts +++ b/packages/store/abi/StoreRead.sol/StoreRead.abi.json.d.ts @@ -304,6 +304,25 @@ declare const abi: [ ]; stateMutability: "view"; type: "function"; + }, + { + inputs: [ + { + internalType: "bytes32"; + name: "table"; + type: "bytes32"; + } + ]; + name: "hasTable"; + outputs: [ + { + internalType: "bool"; + name: ""; + type: "bool"; + } + ]; + stateMutability: "view"; + type: "function"; } ]; export default abi; diff --git a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json b/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json index 58e66783e2..dd50e7dbad 100644 --- a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json +++ b/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json @@ -477,6 +477,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "table", + "type": "bytes32" + } + ], + "name": "hasTable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts b/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts index 53d8067ddb..f9d4261b2b 100644 --- a/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts +++ b/packages/store/abi/StoreReadWithStubs.sol/StoreReadWithStubs.abi.json.d.ts @@ -477,6 +477,25 @@ declare const abi: [ stateMutability: "view"; type: "function"; }, + { + inputs: [ + { + internalType: "bytes32"; + name: "table"; + type: "bytes32"; + } + ]; + name: "hasTable"; + outputs: [ + { + internalType: "bool"; + name: ""; + type: "bool"; + } + ]; + stateMutability: "view"; + type: "function"; + }, { inputs: [ {