Lowercase authorized users before sorting in convert_to_multi_sig_user - #316
Open
BhariGowda wants to merge 1 commit into
Open
Lowercase authorized users before sorting in convert_to_multi_sig_user#316BhariGowda wants to merge 1 commit into
BhariGowda wants to merge 1 commit into
Conversation
convert_to_multi_sig_user sorts authorized_users to give the signers list
a canonical order, then serialises it into the signed action. The sort
runs on the strings as given, so with checksummed addresses it orders on
ASCII, where every uppercase letter sorts before every lowercase one.
The result is that the same set of signers produces a different signers
string depending only on how the caller happened to case its input:
users = ["0xB1C4b0f9E7fD0B9d2F0c0F6C0B0D0E0F0A0b0C0D",
"0xa2c4B0F9e7Fd0b9D2f0C0f6c0b0d0e0f0A0B0c0D"]
sorted(users) -> ["0xB1C4...", "0xa2c4..."]
sorted(u.lower() for u in users) -> ["0xa2c4...", "0xb1c4..."]
so the canonical ordering is not canonical, and the addresses go out
checksummed rather than lowercased.
Every other address the SDK puts into an action is lowercased first
(multi_sig_user, the builder address, the userGenesis and freezeUser
users, oracleUpdater, and the multi-sig payload fields). Lowercasing
before the sort makes this consistent with those and makes the ordering
depend only on the set of signers.
Added a test asserting the emitted signers list is the same for
checksummed input, lowercased input, and reversed input.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
convert_to_multi_sig_usersortsauthorized_users, serialises the result to JSON, and puts that string in the action.signersis an EIP-712string, so those exact bytes are what gets signed and what the server parses.The sort runs on the strings as the caller passed them. Two things follow from that, one unconditional and one narrower.
The unconditional one is the casing. If the caller passes checksummed addresses, which is what
eth_accountgives you fromLocalAccount.address, the addresses go into the signed string checksummed. A caller that lowercases first produces a differentsignersstring for the same set of signers. That happens on every call, not on some of them.The narrower one is the ordering, and it is the sharper consequence. ASCII sorts every uppercase letter before every lowercase one, and EIP-55 casing is effectively random per character, so sorting the strings as given does not order the addresses. Measured over 40,000 randomly generated address sets per row, the order produced today differs from the order of the same addresses normalised:
A randomly generated pair that shows it:
Here the comparison is decided at index 2, on
'C'against'c', which should have been a tie and left the decision to index 3, where'F'and'4'compare as'f'against'4'. The other way it happens is at the first genuinely differing position, for example'B'against'a', where the ASCII order is the reverse of the real one. Both mechanisms occur, so this is not a single narrow case.Every other address the SDK puts into an action is lowercased first:
multi_sig_user, the builder address inbulk_orders, theuserGenesisandfreezeUserusers in the spot deploy actions,oracleUpdaterinperp_deploy_register_asset, and both multi-sig payload fields. Lowercasing before the sort makes this consistent with those, and makes both the casing and the ordering depend only on the set of signers.I have not tested what the server does with either form, so this is about the SDK producing one representation for a given set of signers rather than a claim that the current output is rejected.
The test asserts the emitted signers list is identical for checksummed input, lowercased input and reversed input. It fails on master on the first of those.
Ran
pytest tests/signing_test.py(14 passed) plus black, isort and flake8 with the repo's configured settings.