Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(world): prevent misconfigured delegations, allow unregistering [L-04] #2096

Merged
merged 11 commits into from
Jan 17, 2024
5 changes: 5 additions & 0 deletions .changeset/smart-games-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/world": patch
---

Prevented invalid delegations by performing full validation regardless of whether `initCallData` is empty. Added an `unregisterDelegation` function which allows explicit unregistration, as opposed of passing in zero bytes into `registerDelegation`.
18 changes: 9 additions & 9 deletions packages/world-modules/gas-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testGetKeysWithValueGas",
"name": "install keys with value module",
"gasUsed": 674298
"gasUsed": 674321
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -153,7 +153,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testInstall",
"name": "install keys with value module",
"gasUsed": 674298
"gasUsed": 674321
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -165,7 +165,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetAndDeleteRecordHook",
"name": "install keys with value module",
"gasUsed": 674298
"gasUsed": 674321
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand All @@ -183,7 +183,7 @@
"file": "test/KeysWithValueModule.t.sol",
"test": "testSetField",
"name": "install keys with value module",
"gasUsed": 674298
"gasUsed": 674321
},
{
"file": "test/KeysWithValueModule.t.sol",
Expand Down Expand Up @@ -267,7 +267,7 @@
"file": "test/StandardDelegationsModule.t.sol",
"test": "testCallFromCallboundDelegation",
"name": "register a callbound delegation",
"gasUsed": 118172
"gasUsed": 118138
},
{
"file": "test/StandardDelegationsModule.t.sol",
Expand All @@ -279,7 +279,7 @@
"file": "test/StandardDelegationsModule.t.sol",
"test": "testCallFromSystemDelegation",
"name": "register a systembound delegation",
"gasUsed": 115725
"gasUsed": 115691
},
{
"file": "test/StandardDelegationsModule.t.sol",
Expand All @@ -291,7 +291,7 @@
"file": "test/StandardDelegationsModule.t.sol",
"test": "testCallFromTimeboundDelegation",
"name": "register a timebound delegation",
"gasUsed": 112648
"gasUsed": 112614
},
{
"file": "test/StandardDelegationsModule.t.sol",
Expand All @@ -303,7 +303,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstall",
"name": "install unique entity module",
"gasUsed": 694710
"gasUsed": 694733
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand All @@ -315,7 +315,7 @@
"file": "test/UniqueEntityModule.t.sol",
"test": "testInstallRoot",
"name": "installRoot unique entity module",
"gasUsed": 663729
"gasUsed": 663752
},
{
"file": "test/UniqueEntityModule.t.sol",
Expand Down
12 changes: 9 additions & 3 deletions packages/world/gas-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"file": "test/Factories.t.sol",
"test": "testWorldFactory",
"name": "deploy world via WorldFactory",
"gasUsed": 12321908
"gasUsed": 12386832
},
{
"file": "test/World.t.sol",
Expand All @@ -81,7 +81,7 @@
"file": "test/World.t.sol",
"test": "testCallFromUnlimitedDelegation",
"name": "register an unlimited delegation",
"gasUsed": 47552
"gasUsed": 47532
},
{
"file": "test/World.t.sol",
Expand Down Expand Up @@ -111,7 +111,7 @@
"file": "test/World.t.sol",
"test": "testRegisterNamespace",
"name": "Register a new namespace",
"gasUsed": 120895
"gasUsed": 120918
},
{
"file": "test/World.t.sol",
Expand Down Expand Up @@ -143,6 +143,12 @@
"name": "Write data to the table",
"gasUsed": 39056
},
{
"file": "test/World.t.sol",
"test": "testUnregisterUnlimitedDelegation",
"name": "unregister an unlimited delegation",
"gasUsed": 26191
},
{
"file": "test/WorldDynamicUpdate.t.sol",
"test": "testPopFromDynamicField",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/world/src/modules/core/CoreModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ contract CoreModule is Module {
_registerRootFunctionSelector(BATCH_CALL_SYSTEM_ID, functionSignaturesBatchCall[i]);
}

string[12] memory functionSignaturesCoreRegistration = [
string[13] memory functionSignaturesCoreRegistration = [
// --- ModuleInstallationSystem ---
"installModule(address,bytes)",
// --- StoreRegistrationSystem ---
Expand All @@ -183,6 +183,7 @@ contract CoreModule is Module {
"registerFunctionSelector(bytes32,string)",
"registerRootFunctionSelector(bytes32,string,bytes4)",
"registerDelegation(address,bytes32,bytes)",
"unregisterDelegation(address)",
"registerNamespaceDelegation(bytes32,bytes32,bytes)"
];
for (uint256 i = 0; i < functionSignaturesCoreRegistration.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ contract WorldRegistrationSystem is System, IWorldErrors {
});

// If the delegation is limited...
if (Delegation.isLimited(delegationControlId) && initCallData.length > 0) {
if (Delegation.isLimited(delegationControlId)) {
// Require the delegationControl contract to implement the IDelegationControl interface
address delegationControl = Systems._getSystem(delegationControlId);
requireInterface(delegationControl, DELEGATION_CONTROL_INTERFACE_ID);
Expand All @@ -260,6 +260,11 @@ contract WorldRegistrationSystem is System, IWorldErrors {
}
}

function unregisterDelegation(address delegatee) public {
holic marked this conversation as resolved.
Show resolved Hide resolved
// Delete the delegation control contract address
UserDelegationControl.deleteRecord({ delegator: _msgSender(), delegatee: delegatee });
}

/**
* @notice Registers a delegation for a namespace
* @dev Sets up a new delegation control for a specific namespace
Expand Down
43 changes: 43 additions & 0 deletions packages/world/test/World.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,49 @@ contract WorldTest is Test, GasReporter {
);
}

function testUnregisterUnlimitedDelegation() public {
// Register a new system
WorldTestSystem system = new WorldTestSystem();
ResourceId systemId = WorldResourceIdLib.encode({
typeId: RESOURCE_SYSTEM,
namespace: "namespace",
name: "testSystem"
});
world.registerNamespace(systemId.getNamespaceId());
world.registerSystem(systemId, system, true);

// Register an unlimited delegation
address delegator = address(1);
address delegatee = address(2);
vm.prank(delegator);
world.registerDelegation(delegatee, UNLIMITED_DELEGATION, new bytes(0));

// Call a system from the delegatee on behalf of the delegator
vm.prank(delegatee);
bytes memory returnData = world.callFrom(delegator, systemId, abi.encodeCall(WorldTestSystem.msgSender, ()));
address returnedAddress = abi.decode(returnData, (address));

// Expect the system to have received the delegator's address
assertEq(returnedAddress, delegator);

// Unregister the delegation
vm.prank(delegator);
startGasReport("unregister an unlimited delegation");
world.unregisterDelegation(delegatee);
endGasReport();

// Expect a revert when attempting to perform a call on behalf of an address that doesn't have a delegation
vm.expectRevert(
abi.encodeWithSelector(
IWorldErrors.World_DelegationNotFound.selector,
delegator,
delegatee // Invalid delegatee
)
);
vm.prank(delegatee);
world.callFrom(delegator, systemId, abi.encodeCall(WorldTestSystem.msgSender, ()));
}

function testRegisterStoreHook() public {
FieldLayout fieldLayout = Bool.getFieldLayout();
Schema valueSchema = Bool.getValueSchema();
Expand Down
Loading