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): inline debug constants [L-11] #1976

Merged
merged 9 commits into from
Jan 12, 2024

Conversation

ClaudeZsb
Copy link
Contributor

Description

The original type of ROOT_NAMESPACE_STRING defined in WorldResourceId.sol is

bytes16 constant ROOT_NAMESPACE_STRING = bytes16("ROOT_NAMESPACE");

Obviously its length is supposed to be 14. And then problem is coming when we use it in a conditional operator ? : and put it in the true condition. See code here:

bytes14 resourceNamespace = getNamespace(resourceId);
...
return string(
  abi.encodePacked(
    ...,
    resourceNamespace == ROOT_NAMESPACE ? ROOT_NAMESPACE_STRING : resourceNamespace,
    ...
  )
)

Although resourceNamespace is defined as bytes14, the type of the value packed will be converted to bytes16 just because the type of value at the true condition is bytes16. As a result, ResourceId.toString() will return a string of length 36=2+1+16+1+16 instead of 34=2+1+14+1+16.

Reproduction

Try the following test code

console.logBytes(abi.encodePacked(bytes16("true")));
console.logBytes(abi.encodePacked(bytes14("false")));
console.logBytes(abi.encodePacked(1 == 2 ? bytes16("true") : bytes14("false")));

And you'll get

Logs:
  0x74727565000000000000000000000000
  0x66616c7365000000000000000000
  0x66616c73650000000000000000000000

where bytes14("false") is packed as a bytes16.

Copy link

changeset-bot bot commented Nov 30, 2023

🦋 Changeset detected

Latest commit: ca84b88

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
@latticexyz/world Patch
@latticexyz/cli Patch
@latticexyz/dev-tools Patch
@latticexyz/store-sync Patch
@latticexyz/world-modules Patch
@latticexyz/store-indexer Patch
@latticexyz/abi-ts Patch
@latticexyz/block-logs-stream Patch
@latticexyz/common Patch
@latticexyz/config Patch
create-mud Patch
@latticexyz/ecs-browser Patch
@latticexyz/faucet Patch
@latticexyz/gas-report Patch
@latticexyz/network Patch
@latticexyz/noise Patch
@latticexyz/phaserx Patch
@latticexyz/protocol-parser Patch
@latticexyz/react Patch
@latticexyz/recs Patch
@latticexyz/schema-type Patch
@latticexyz/services Patch
@latticexyz/solecs Patch
solhint-config-mud Patch
solhint-plugin-mud Patch
@latticexyz/std-client Patch
@latticexyz/std-contracts Patch
@latticexyz/store-cache Patch
@latticexyz/store Patch
@latticexyz/utils Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@@ -10,7 +10,7 @@ import { RESOURCE_NAMESPACE, MASK_RESOURCE_NAMESPACE } from "./worldResourceType
uint256 constant NAMESPACE_BITS = 14 * 8;
uint256 constant NAME_BITS = 16 * 8;

bytes16 constant ROOT_NAMESPACE_STRING = bytes16("ROOT_NAMESPACE");
bytes14 constant ROOT_NAMESPACE_STRING = bytes14("ROOT_NAMESPACE");
bytes16 constant ROOT_NAME_STRING = bytes16("ROOT_NAME");
Copy link
Member

@holic holic Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alvrs A few notes/questions for us:

  • Since these strings are just for debugging, they don't need to be type-aligned with the actual namespace/name types, right? They could technically be any length?
  • I have been using an empty string for root namespace/name in ~all of the client code, and wonder if we should align on that here. Alternatively, I wonder if we should use a less likely string like <root> or something.
  • Should we inline this into toString (since that's the only place it's used) instead of defining up here, so we don't need to specify the type?
  • Should we use toTrimmedString for namespace and name inside toString?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these strings are just for debugging, they don't need to be type-aligned with the actual namespace/name types, right? They could technically be any length?

Correct (but kinda agree using something else than bytes14 can lead to confusion)

I have been using an empty string for root namespace/name in ~all of the client code, and wonder if we should align on that here. Alternatively, I wonder if we should use a less likely string like or something.

If we do that we don't even need the ternary operator here anymore since the actual namespace/name is already empty. The original reason why we replaced the empty string here with a more explicit one was that ns:: might be less easy to understand than ns:ROOT_NAMESPACE:ROOT_NAME. (No strong opinion on which string we use, also happy to make it ns:<root>:<root>)

Should we inline this into toString (since that's the only place it's used) instead of defining up here, so we don't need to specify the type?

Yeah that makes sense! (still need to specify the type tho because the ternary operator is unhappy if the result is string | bytes14)

Should we use toTrimmedString for namespace and name inside toString?

We could, but if it's logged as string the 0's are already not displayed, and if it's logged as raw bytes it might be a tiny little bit easier to figure out what the log means if it always has the same length (34 bytes). On the other hand advantage of using toTrimmedString would mean it's possible to compare the result with an inline string, which also might be useful.

Copy link
Contributor Author

@ClaudeZsb ClaudeZsb Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the new changes? Use the inline and shorter string <root> for root namespace and name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it!

Given that this method is meant to be for debug/human-readability purposes and comparison/parsing should be done against the resource ID itself, I am keen for this to be more string like and use toTrimmedString around the whole thing. But would be curious to see gas differences before making the final call.

Copy link
Contributor Author

@ClaudeZsb ClaudeZsb Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With test code:

  function testToString() public {
    ResourceId resourceId = WorldResourceIdLib.encode({
      typeId: RESOURCE_SYSTEM,
      namespace: "namespace",
      name: "name"
    });

    startGasReport("convert resource ID to string");
    string memory resourceIdString = resourceId.toString();
    endGasReport();

    // assertEq(resourceIdString, "sy:namespace:name");
  }

It turns out that:

GAS REPORT: 460 convert resource ID to string

After using toTrimmedString inside of toString in the following form:

string(
  abi.encodePacked(
    resourceType,
    ":",
    resourceNamespace == bytes14("") ? "<root>" : WorldResourceIdLib.toTrimmedString(resourceNamespace),
    ":",
    resourceName == bytes16("") ? "<root>" : WorldResourceIdLib.toTrimmedString(resourceName)
  )
);

We got:

GAS REPORT: 3287 convert resource ID to string

toString is actually used in error message, so we might not care much about its gas consumption?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ClaudeZsb ClaudeZsb force-pushed the fix-resourceId-string branch from fbc0ac1 to 9b4f570 Compare December 11, 2023 02:16
Comment on lines 102 to 104
resourceNamespace == bytes14("") ? "<root>" : WorldResourceIdLib.toTrimmedString(resourceNamespace),
":",
resourceName == ROOT_NAME ? ROOT_NAME_STRING : resourceName
resourceName == bytes16("") ? "<root>" : WorldResourceIdLib.toTrimmedString(resourceName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small nit: let's use the ROOT_NAMESPACE and ROOT_NAME constants (imported from constants.sol) here instead of the inline bytes14

Copy link
Member

@alvrs alvrs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to be merged and replace #2030. @yonadaaa can you take this over and finish it up (using the ROOT_NAMESPACE and ROOT_NAME constants and adding a short changeset)

@holic holic changed the title fix(world): fix the length of ROOT_NAMESPACE_STRING fix(world): fix the length of ROOT_NAMESPACE_STRING [L-11] Jan 2, 2024
@yonadaa yonadaa requested a review from alvrs January 7, 2024 23:48
@holic holic changed the title fix(world): fix the length of ROOT_NAMESPACE_STRING [L-11] fix(world): inline debug constants [L-11] Jan 12, 2024
@holic holic merged commit d00c4a9 into latticexyz:main Jan 12, 2024
11 of 12 checks passed
@holic
Copy link
Member

holic commented Jan 12, 2024

Thanks @ClaudeZsb! Sorry for the delay in getting back to this: we all took a long winter break :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants