-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(world): require namespace to exist before registering systems/ta…
…bles in it [C-01] (#2007) Co-authored-by: Kevin Ingersoll <[email protected]>
- Loading branch information
Showing
26 changed files
with
337 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
--- | ||
"@latticexyz/cli": patch | ||
"@latticexyz/world-modules": patch | ||
"@latticexyz/world": major | ||
--- | ||
|
||
Previously `registerSystem` and `registerTable` had a side effect of registering namespaces if the system or table's namespace didn't exist yet. | ||
This caused a possible frontrunning issue, where an attacker could detect a `registerSystem`/`registerTable` transaction in the mempool, | ||
insert a `registerNamespace` transaction before it, grant themselves access to the namespace, transfer ownership of the namespace to the victim, | ||
so that the `registerSystem`/`registerTable` transactions still went through successfully. | ||
To mitigate this issue, the side effect of registering a namespace in `registerSystem` and `registerTable` has been removed. | ||
Calls to these functions now expect the respective namespace to exist and the caller to own the namespace, otherwise they revert. | ||
|
||
Changes in consuming projects are only necessary if tables or systems are registered manually. | ||
If only the MUD deployer is used to register tables and systems, no changes are necessary, as the MUD deployer has been updated accordingly. | ||
|
||
```diff | ||
+ world.registerNamespace(namespaceId); | ||
world.registerSystem(systemId, system, true); | ||
``` | ||
|
||
```diff | ||
+ world.registerNamespace(namespaceId); | ||
MyTable.register(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { Account, Chain, Client, Hex, Transport, getAddress } from "viem"; | ||
import { WorldDeploy, worldAbi, worldTables } from "./common"; | ||
import { hexToResource, resourceToHex, writeContract } from "@latticexyz/common"; | ||
import { getResourceIds } from "./getResourceIds"; | ||
import { getTableValue } from "./getTableValue"; | ||
import { debug } from "./debug"; | ||
|
||
export async function ensureNamespaceOwner({ | ||
client, | ||
worldDeploy, | ||
resourceIds, | ||
}: { | ||
readonly client: Client<Transport, Chain | undefined, Account>; | ||
readonly worldDeploy: WorldDeploy; | ||
readonly resourceIds: readonly Hex[]; | ||
}): Promise<readonly Hex[]> { | ||
const desiredNamespaces = Array.from(new Set(resourceIds.map((resourceId) => hexToResource(resourceId).namespace))); | ||
const existingResourceIds = await getResourceIds({ client, worldDeploy }); | ||
const existingNamespaces = new Set(existingResourceIds.map((resourceId) => hexToResource(resourceId).namespace)); | ||
if (existingNamespaces.size) { | ||
debug( | ||
"found", | ||
existingNamespaces.size, | ||
"existing namespaces:", | ||
Array.from(existingNamespaces) | ||
.map((namespace) => (namespace === "" ? "<root>" : namespace)) | ||
.join(", ") | ||
); | ||
} | ||
|
||
// Assert ownership of existing namespaces | ||
const existingDesiredNamespaces = desiredNamespaces.filter((namespace) => existingNamespaces.has(namespace)); | ||
const namespaceOwners = await Promise.all( | ||
existingDesiredNamespaces.map(async (namespace) => { | ||
const { owner } = await getTableValue({ | ||
client, | ||
worldDeploy, | ||
table: worldTables.world_NamespaceOwner, | ||
key: { namespaceId: resourceToHex({ type: "namespace", namespace, name: "" }) }, | ||
}); | ||
return [namespace, owner]; | ||
}) | ||
); | ||
|
||
const unauthorizedNamespaces = namespaceOwners | ||
.filter(([, owner]) => getAddress(owner) !== getAddress(client.account.address)) | ||
.map(([namespace]) => namespace); | ||
|
||
if (unauthorizedNamespaces.length) { | ||
throw new Error(`You are attempting to deploy to namespaces you do not own: ${unauthorizedNamespaces.join(", ")}`); | ||
} | ||
|
||
// Register missing namespaces | ||
const missingNamespaces = desiredNamespaces.filter((namespace) => !existingNamespaces.has(namespace)); | ||
if (missingNamespaces.length > 0) { | ||
debug("registering namespaces", Array.from(missingNamespaces).join(", ")); | ||
} | ||
const registrationTxs = Promise.all( | ||
missingNamespaces.map((namespace) => | ||
writeContract(client, { | ||
chain: client.chain ?? null, | ||
address: worldDeploy.address, | ||
abi: worldAbi, | ||
functionName: "registerNamespace", | ||
args: [resourceToHex({ namespace, type: "namespace", name: "" })], | ||
}) | ||
) | ||
); | ||
|
||
return registrationTxs; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.