Skip to content

Commit

Permalink
test(store): add tests and gas reports for using offchain tables (#2117)
Browse files Browse the repository at this point in the history
Co-authored-by: Kevin Ingersoll <[email protected]>
  • Loading branch information
yonadaa and holic authored Jan 12, 2024
1 parent aee8020 commit ca01b29
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 8 deletions.
24 changes: 18 additions & 6 deletions packages/store/gas-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,12 @@
"name": "delete record (complex data, 3 slots)",
"gasUsed": 8015
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testDeleteDataOffchainTable",
"name": "StoreCore: delete record in offchain table",
"gasUsed": 4687
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testGetStaticDataLocation",
Expand Down Expand Up @@ -753,19 +759,19 @@
"file": "test/StoreCoreGas.t.sol",
"test": "testHooks",
"name": "set record on table with subscriber",
"gasUsed": 72296
"gasUsed": 72361
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testHooks",
"name": "set static field on table with subscriber",
"gasUsed": 19791
"gasUsed": 19813
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testHooks",
"name": "delete record on table with subscriber",
"gasUsed": 18617
"gasUsed": 18595
},
{
"file": "test/StoreCoreGas.t.sol",
Expand All @@ -777,19 +783,19 @@
"file": "test/StoreCoreGas.t.sol",
"test": "testHooksDynamicData",
"name": "set (dynamic) record on table with subscriber",
"gasUsed": 165451
"gasUsed": 165516
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testHooksDynamicData",
"name": "set (dynamic) field on table with subscriber",
"gasUsed": 24469
"gasUsed": 24424
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testHooksDynamicData",
"name": "delete (dynamic) record on table with subscriber",
"gasUsed": 20283
"gasUsed": 20261
},
{
"file": "test/StoreCoreGas.t.sol",
Expand Down Expand Up @@ -941,6 +947,12 @@
"name": "get static record (2 slots)",
"gasUsed": 1740
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testSetDataOffchainTable",
"name": "StoreCore: set record in offchain table",
"gasUsed": 8082
},
{
"file": "test/StoreCoreGas.t.sol",
"test": "testUpdateInDynamicField",
Expand Down
87 changes: 86 additions & 1 deletion packages/store/test/StoreCore.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { StoreSwitch } from "../src/StoreSwitch.sol";
import { IStoreHook } from "../src/IStoreHook.sol";
import { Tables, ResourceIds, TablesTableId } from "../src/codegen/index.sol";
import { ResourceId, ResourceIdLib, ResourceIdInstance } from "../src/ResourceId.sol";
import { RESOURCE_TABLE } from "../src/storeResourceTypes.sol";
import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "../src/storeResourceTypes.sol";
import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol";
import { BEFORE_SET_RECORD, AFTER_SET_RECORD, BEFORE_SPLICE_STATIC_DATA, AFTER_SPLICE_STATIC_DATA, BEFORE_SPLICE_DYNAMIC_DATA, AFTER_SPLICE_DYNAMIC_DATA, BEFORE_DELETE_RECORD, AFTER_DELETE_RECORD, ALL, BEFORE_ALL, AFTER_ALL } from "../src/storeHookTypes.sol";
import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol";
Expand All @@ -44,6 +44,7 @@ contract StoreCoreTest is Test, StoreMock {
string[] defaultKeyNames = new string[](1);
ResourceId _tableId = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some table" });
ResourceId _tableId2 = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some other table" });
ResourceId _tableId3 = ResourceIdLib.encode({ typeId: RESOURCE_OFFCHAIN_TABLE, name: "some offchain table" });

function testGetStaticDataLocation() public {
ResourceId tableId = _tableId;
Expand Down Expand Up @@ -1318,4 +1319,88 @@ contract StoreCoreTest is Test, StoreMock {
assertEq(loadedData.encodedLengths.unwrap(), bytes32(0));
assertEq(loadedData.dynamicData, "");
}

function testSetDataOffchainTable() public {
ResourceId tableId = _tableId3;

// Register offchain table
FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(1, 2, 1, 2, 0);
Schema valueSchema = SchemaEncodeHelper.encode(
SchemaType.UINT8,
SchemaType.UINT16,
SchemaType.UINT8,
SchemaType.UINT16
);

IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4));

// Set data
bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506));

bytes32[] memory keyTuple = new bytes32[](1);
keyTuple[0] = "some key";

// Expect a Store_SetRecord event to be emitted
vm.expectEmit(true, true, true, true);
emit Store_SetRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0));

IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0));
}

function testDeleteDataOffchainTable() public {
ResourceId tableId = _tableId3;

// Register table
FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 2);
{
Schema valueSchema = SchemaEncodeHelper.encode(
SchemaType.UINT128,
SchemaType.UINT32_ARRAY,
SchemaType.UINT32_ARRAY
);
IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3));
}

bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10);

bytes memory secondDataBytes;
{
uint32[] memory secondData = new uint32[](2);
secondData[0] = 0x11121314;
secondData[1] = 0x15161718;
secondDataBytes = EncodeArray.encode(secondData);
}

bytes memory thirdDataBytes;
{
uint32[] memory thirdData = new uint32[](3);
thirdData[0] = 0x191a1b1c;
thirdData[1] = 0x1d1e1f20;
thirdData[2] = 0x21222324;
thirdDataBytes = EncodeArray.encode(thirdData);
}

PackedCounter encodedDynamicLength;
{
encodedDynamicLength = PackedCounterLib.pack(uint40(secondDataBytes.length), uint40(thirdDataBytes.length));
}

// Concat data
bytes memory staticData = abi.encodePacked(firstDataBytes);
bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes);

// Create keyTuple
bytes32[] memory keyTuple = new bytes32[](1);
keyTuple[0] = bytes32("some key");

// Set data
IStore(this).setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData);

// Expect a Store_DeleteRecord event to be emitted
vm.expectEmit(true, true, true, true);
emit Store_DeleteRecord(tableId, keyTuple);

// Delete data
IStore(this).deleteRecord(tableId, keyTuple);
}
}
91 changes: 90 additions & 1 deletion packages/store/test/StoreCoreGas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { IStoreErrors } from "../src/IStoreErrors.sol";
import { IStore } from "../src/IStore.sol";
import { ResourceId, ResourceIdLib } from "../src/ResourceId.sol";
import { ResourceIds } from "../src/codegen/tables/ResourceIds.sol";
import { RESOURCE_TABLE } from "../src/storeResourceTypes.sol";
import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "../src/storeResourceTypes.sol";
import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol";
import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol";
import { StoreMock } from "./StoreMock.sol";
Expand All @@ -37,6 +37,7 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock {
Schema defaultKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32);
ResourceId _tableId = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some table" });
ResourceId _tableId2 = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some other table" });
ResourceId _tableId3 = ResourceIdLib.encode({ typeId: RESOURCE_OFFCHAIN_TABLE, name: "some offchain table" });

function testGetStaticDataLocation() public {
ResourceId tableId = _tableId;
Expand Down Expand Up @@ -724,4 +725,92 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock {
StoreCore.deleteRecord(tableId, keyTuple);
endGasReport();
}

function testSetDataOffchainTable() public {
ResourceId tableId = _tableId3;

// Register offchain table
FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(1, 2, 1, 2, 0);
Schema valueSchema = SchemaEncodeHelper.encode(
SchemaType.UINT8,
SchemaType.UINT16,
SchemaType.UINT8,
SchemaType.UINT16
);

IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4));

// Set data
bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506));

bytes32[] memory keyTuple = new bytes32[](1);
keyTuple[0] = "some key";

// Expect a Store_SetRecord event to be emitted
vm.expectEmit(true, true, true, true);
emit Store_SetRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0));

startGasReport("StoreCore: set record in offchain table");
IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0));
endGasReport();
}

function testDeleteDataOffchainTable() public {
ResourceId tableId = _tableId3;

// Register table
FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 2);
{
Schema valueSchema = SchemaEncodeHelper.encode(
SchemaType.UINT128,
SchemaType.UINT32_ARRAY,
SchemaType.UINT32_ARRAY
);
IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3));
}

bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10);

bytes memory secondDataBytes;
{
uint32[] memory secondData = new uint32[](2);
secondData[0] = 0x11121314;
secondData[1] = 0x15161718;
secondDataBytes = EncodeArray.encode(secondData);
}

bytes memory thirdDataBytes;
{
uint32[] memory thirdData = new uint32[](3);
thirdData[0] = 0x191a1b1c;
thirdData[1] = 0x1d1e1f20;
thirdData[2] = 0x21222324;
thirdDataBytes = EncodeArray.encode(thirdData);
}

PackedCounter encodedDynamicLength;
{
encodedDynamicLength = PackedCounterLib.pack(uint40(secondDataBytes.length), uint40(thirdDataBytes.length));
}

// Concat data
bytes memory staticData = abi.encodePacked(firstDataBytes);
bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes);

// Create keyTuple
bytes32[] memory keyTuple = new bytes32[](1);
keyTuple[0] = bytes32("some key");

// Set data
IStore(this).setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData);

// Expect a Store_DeleteRecord event to be emitted
vm.expectEmit(true, true, true, true);
emit Store_DeleteRecord(tableId, keyTuple);

// Delete data
startGasReport("StoreCore: delete record in offchain table");
IStore(this).deleteRecord(tableId, keyTuple);
endGasReport();
}
}

0 comments on commit ca01b29

Please sign in to comment.