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

Strings: add toUint, toInt and hexToUint #5166

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Aug 28, 2024

As discussed. Usefull for parsing addresses from CAIP10 identifier (strings)

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

Copy link

changeset-bot bot commented Aug 28, 2024

🦋 Changeset detected

Latest commit: 26cec97

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

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Minor

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

contracts/utils/Strings.sol Outdated Show resolved Hide resolved
.changeset/eighty-hounds-promise.md Outdated Show resolved Hide resolved
Comment on lines 143 to 147
* @dev Parse an decimal string and returns the value as a `int256`.
*
* This function will revert if:
* - the string contains any character (outside the prefix) that is not in [0-9].
* - the result does not fit in a int256.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @dev Parse an decimal string and returns the value as a `int256`.
*
* This function will revert if:
* - the string contains any character (outside the prefix) that is not in [0-9].
* - the result does not fit in a int256.
* @dev Parse a string in decimal (i.e. base 10) and returns the value as a `int256`.
*
* This function will revert if:
* - the string contains any character (outside the prefix) that is not in [0-9].
* - the result does not fit in a `int256`.

Comment on lines 125 to 129
* @dev Parse an decimal string and returns the value as a `uint256`.
*
* This function will revert if:
* - the string contains any character that is not in [0-9].
* - the result does not fit in a uint256.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @dev Parse an decimal string and returns the value as a `uint256`.
*
* This function will revert if:
* - the string contains any character that is not in [0-9].
* - the result does not fit in a uint256.
* @dev Parse a string in decimal (i.e. base 10) and returns the value as a `uint256`.
*
* This function will revert if:
* - the string contains any character that is not in [0-9].
* - the result does not fit in a `uint256`.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

When looking online, It looks like "decimal string" is the correct way. Maybe "string in decimal format" would be ok, but "string in decimal" doesn't look commonly used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I'd be fine with decimal string too. I see that's the current state so it's fine imo

contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
@ernestognw ernestognw added this to the 5.2 milestone Sep 3, 2024
cairoeth
cairoeth previously approved these changes Sep 5, 2024
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
function hexToUint(string memory input) internal pure returns (uint256) {
bytes memory buffer = bytes(input);

// skip 0x prefix if present. Length check doesn't appear to be critical
Copy link
Contributor

Choose a reason for hiding this comment

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

second sentence feels out of place

Suggested change
// skip 0x prefix if present. Length check doesn't appear to be critical
// skip 0x prefix if present

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just for the record, this is what the comment was about:

The string length may be less than 2. String could be empty, of just "1". In some cases, doing a input[1] or even a input[0] would revert (out of bound acces). Doing bytes2(input) does not revert if the string is to short.

So we check the length to verify that it ok to read the prefix ? It turn out no.

  • If the string is empty, then regardless of the result of that lookup (that could read dirty bytes), the loop will not run (because length == 0), and the result will be 0 -> that is ok
  • If the string has length 1 then we have two options
    • the check identifies the prefix
      • that means the string is "0", and there is a dirty x after.
      • In that case we have an offset of 2, and the length is 1, so the for loop does not run and the function returns 0 -> that is ok
    • the check does not find the prefix
      • the only "digit" is read by the loop, and the result should be just fine
  • If the string has length >= 2, then the prefix lookup is in the bounds

That is the long explanation (I'm happy its visible in the PR 😃) to something that is not really trivial, and can be missed, but missing it is not a risk.

We may get questions about it though ...

cairoeth
cairoeth previously approved these changes Sep 9, 2024
@cairoeth cairoeth dismissed their stale review September 9, 2024 15:46

TODO: add functions to specify start and end of string to parse

}

// TODO: documentation.
function unsafeReadBytesOffset(bytes memory buffer, uint256 offset) internal pure returns (bytes32 value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a bytes operation so it shouldn't be in the Strings library.

Copy link
Collaborator Author

@Amxx Amxx Sep 17, 2024

Choose a reason for hiding this comment

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

Where should that go ? A Bytes library ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note that the same function exists in RSA.sol

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this function was present in other places but always private.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. private, and marked as memory safe, because the private calls are all enforcing the safety.

But we are starting to use it in many places ... so having 3 private implementation of the same functions (with different names?) doesn't feel right.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. private, and marked as memory safe, because the private calls are all enforcing the safety.

Right... That was the issue. That using memory-unsafe functions disables some optimizations globally, so they were important to avoid.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So do we want to have:

  • multiple private (and memory safe) implementations
  • one internal, none memory safe implementation
  • one internal, memory safe (checks memory bound) implementation
    ?

contracts/utils/Strings.sol Outdated Show resolved Hide resolved
Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

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

I see we added more variations of the base functions and understand that we might want to reduce it. However, I like that the interface is pretty versatile (especially by providing pointers).

I'm not too worried about the length of this particular library since it's not procedurally generated

contracts/utils/Strings.sol Outdated Show resolved Hide resolved
Comment on lines 125 to 129
* @dev Parse an decimal string and returns the value as a `uint256`.
*
* This function will revert if:
* - the string contains any character that is not in [0-9].
* - the result does not fit in a uint256.
Copy link
Member

Choose a reason for hiding this comment

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

I'd be fine with decimal string too. I see that's the current state so it's fine imo

contracts/utils/Strings.sol Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
contracts/utils/Strings.sol Outdated Show resolved Hide resolved
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.

5 participants