Skip to content

Commit

Permalink
Add root namespace checks
Browse files Browse the repository at this point in the history
  • Loading branch information
vdrg committed Nov 22, 2024
1 parent e99c1ac commit e3deee7
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 29 deletions.
48 changes: 27 additions & 21 deletions packages/world/ts/node/render-solidity/renderSystemLibrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export function renderSystemLibrary(options: RenderSystemLibraryOptions) {
libraryName,
systemLabel,
resourceId,
functions,
namespace,
functions: systemFunctions,
errors: systemErrors,
worldImportPath,
storeImportPath,
Expand Down Expand Up @@ -49,11 +50,15 @@ export function renderSystemLibrary(options: RenderSystemLibraryOptions) {
},
];

const errors = [...systemErrors];
const callingFromRootSystemErrorName = `${libraryName}_CallingFromRootSystem`;
const errors = [{ name: callingFromRootSystemErrorName, parameters: [] }, ...systemErrors];

const camelCaseSystemLabel = systemLabel.charAt(0).toLowerCase() + systemLabel.slice(1);
const userTypeName = `${systemLabel}Type`;

// Remove view functions for root systems
const functions = systemFunctions.filter(({ stateMutability }) => namespace !== "" || stateMutability === "");

return `
${renderedSolidityHeader}
Expand Down Expand Up @@ -81,11 +86,11 @@ export function renderSystemLibrary(options: RenderSystemLibraryOptions) {
library ${libraryName} {
${renderErrors(errors)}
${renderUserTypeFunctions(functions, userTypeName)}
${renderList(functions, (contractFunction) => renderUserTypeFunction(contractFunction, userTypeName))}
${renderCallWrapperFunctions(functions, systemLabel)}
${renderList(functions, (contractFunction) => renderCallWrapperFunction(contractFunction, systemLabel, callingFromRootSystemErrorName))}
${renderRootCallWrapperFunctions(functions, systemLabel)}
${renderList(functions, (contractFunction) => renderRootCallWrapperFunction(contractFunction, systemLabel))}
function callFrom(${userTypeName} self, address from) internal pure returns (CallWrapper memory) {
return CallWrapper(self.toResourceId(), from);
Expand Down Expand Up @@ -130,10 +135,6 @@ function renderErrors(errors: ContractInterfaceError[]) {
return renderList(errors, ({ name, parameters }) => ` error ${name}(${renderArguments(parameters)});`);
}

function renderUserTypeFunctions(functions: ContractInterfaceFunction[], userTypeName: string) {
return renderList(functions, (contractFunction) => renderUserTypeFunction(contractFunction, userTypeName));
}

function renderUserTypeFunction(contractFunction: ContractInterfaceFunction, userTypeName: string) {
const { name, parameters, stateMutability, returnParameters } = contractFunction;

Expand All @@ -156,11 +157,11 @@ function renderUserTypeFunction(contractFunction: ContractInterfaceFunction, use
`;
}

function renderCallWrapperFunctions(functions: ContractInterfaceFunction[], systemLabel: string) {
return renderList(functions, (contractFunction) => renderCallWrapperFunction(contractFunction, systemLabel));
}

function renderCallWrapperFunction(contractFunction: ContractInterfaceFunction, systemLabel: string) {
function renderCallWrapperFunction(
contractFunction: ContractInterfaceFunction,
systemLabel: string,
callingFromRootSystemErrorName: string,
) {
const { name, parameters, stateMutability, returnParameters } = contractFunction;

const functionArguments = [`CallWrapper memory self`, ...parameters];
Expand All @@ -173,11 +174,17 @@ function renderCallWrapperFunction(contractFunction: ContractInterfaceFunction,
${renderReturnParameters(returnParameters)}
`;

const rootSystemCheck = `
// if the contract calling this function is a root system, it should use \`callAsRoot\`
if (address(_world()) == address(this)) revert ${callingFromRootSystemErrorName}();
`;

const encodedSystemCall = renderEncodeSystemCall(systemLabel, name, parameters);

if (stateMutability === "") {
return `
${functionSignature} {
${rootSystemCheck}
bytes memory systemCall = ${encodedSystemCall};
bytes memory result = self.from == address(0) ? _world().call(self.systemId, systemCall) : _world().callFrom(self.from, self.systemId, systemCall);
${renderAbiDecode(returnParameters)}
Expand All @@ -186,6 +193,7 @@ function renderCallWrapperFunction(contractFunction: ContractInterfaceFunction,
} else {
return `
${functionSignature} {
${rootSystemCheck}
bytes memory systemCall = ${encodedSystemCall};
bytes memory worldCall = self.from == address(0)
? abi.encodeCall(IWorldCall.call, (self.systemId, systemCall))
Expand All @@ -199,20 +207,16 @@ function renderCallWrapperFunction(contractFunction: ContractInterfaceFunction,
}
}

function renderRootCallWrapperFunctions(functions: ContractInterfaceFunction[], systemLabel: string) {
return renderList(functions, (contractFunction) => renderRootCallWrapperFunction(contractFunction, systemLabel));
}

function renderRootCallWrapperFunction(contractFunction: ContractInterfaceFunction, systemLabel: string) {
const { name, parameters, stateMutability, returnParameters } = contractFunction;

const functionArguments = [`RootCallWrapper memory ${stateMutability === "" ? "self" : ""}`, ...parameters];
const functionArguments = ["RootCallWrapper memory self", ...parameters];

const functionSignature = `
function ${name}(
${renderArguments(functionArguments)}
) internal
${stateMutability === "view" ? "pure" : stateMutability}
${stateMutability === "pure" ? "view" : stateMutability}
${renderReturnParameters(returnParameters)}
`;

Expand All @@ -229,7 +233,9 @@ function renderRootCallWrapperFunction(contractFunction: ContractInterfaceFuncti
} else {
return `
${functionSignature} {
revert("Static calls not implemented for root systems");
bytes memory systemCall = ${encodedSystemCall};
bytes memory result = SystemCall.staticcallOrRevert(self.from, self.systemId, systemCall);
${renderAbiDecode(returnParameters)}
}
`;
}
Expand Down
1 change: 1 addition & 0 deletions packages/world/ts/node/render-solidity/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface RenderSystemLibraryOptions {
interfaceName: string;
libraryName: string;
resourceId: string;
namespace: string;
functions: ContractInterfaceFunction[];
errors: ContractInterfaceError[];

Expand Down
1 change: 1 addition & 0 deletions packages/world/ts/node/render-solidity/worldgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export async function worldgen({
interfaceName: system.interfaceName,
systemLabel: system.label,
resourceId: resourceToHex({ type: "system", namespace: system.namespace, name: system.name }),
namespace: system.namespace,
functions,
errors,
imports: [systemImport, ...imports],
Expand Down
2 changes: 2 additions & 0 deletions test/system-libraries/src/codegen/world/IRootSystem.sol

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

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

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

5 changes: 5 additions & 0 deletions test/system-libraries/src/namespaces/root/RootSystem.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ contract RootSystem is System {
function setValueInA(uint256 value) external {
aSystem.callAsRoot().setValue(value);
}

// this function should not be present in the library (staticcalls disabled for root system)
function getValueFromA() external view returns (uint256) {
return aSystem.callAsRoot().getValue();
}
}

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

5 changes: 3 additions & 2 deletions test/system-libraries/test/Libraries.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ contract LibrariesTest is MudTest {
function testCanCallSystemFromOtherSystem() public {
uint256 value = 0xDEADBEEF;
bSystem.setValueInA(value);
assertEq(Value.get(), value, "Value.get");
assertEq(bSystem.getValueFromA(), value, "getValueFromA");
assertEq(Value.get(), value);
assertEq(bSystem.getValueFromA(), value);
}

function testCallFrom() public {
Expand All @@ -56,6 +56,7 @@ contract LibrariesTest is MudTest {

function testCanCallFromRootSystemWithLibrary() public {
uint256 value = 0xDEADBEEF;
// internally, rootSystem uses callAsRoot to call aSystem
rootSystem.setValueInA(value);
assertEq(Value.get(), value);
assertEq(aSystem.getValue(), value);
Expand Down

0 comments on commit e3deee7

Please sign in to comment.