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

feat(AccountAPI): sign raw hash32 #1966

Merged
merged 4 commits into from
Mar 21, 2024

Conversation

fubuloubu
Copy link
Member

@fubuloubu fubuloubu commented Mar 14, 2024

What I did

Add a new path to AccountAPI called .sign_raw_msghash that allows signing a raw 32 byte hash without treating it as an EIP191 SignableMessage type

fixes: #1962

How I did it

I needed this for work I was doing on ape safe, but it is a really dangerous thing to allow, so I added a warning message and additionally disallowed autosigning for it. For AccountAPI.recover_message I added an additional kwarg recover_using_eip191=True that can be set false if you want to opt-in for that behavior for verifying a signature.

How to verify it

Try the experience locally, I have added tests for it. Ultimately, when installled alongside the delegate feature for ape safe (ApeWorX/ape-safe#41) you should now be able to use it to directly sign something with the API

Checklist

  • All changes are completed
  • New test cases have been added
  • Documentation has been updated

@fubuloubu fubuloubu changed the title Feat/account api/sign raw hash32 feat(AccountAPI): sign raw hash32 Mar 14, 2024
Copy link
Member

@antazoey antazoey left a comment

Choose a reason for hiding this comment

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

The UX is better with this being in a separate method rather than relying on size-checking and prompts.

display_msg = f"Signing raw bytes: '{msg.hex()}'"

if sign_raw_hash := (
len(msg) == 32 and not click.confirm("Sign using EIP-191? (Select 'y' if unsure)")
Copy link
Member

Choose a reason for hiding this comment

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

Using click-isms outside of a CLI is a bit strange, I know we are already doing it elsewhere in this calss, but it'd be nice to keep prompting in the CLI components.

Copy link
Member Author

Choose a reason for hiding this comment

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

This class needs to prompt the user directly to unlock or approve actions, so seems okay to me

src/ape_accounts/accounts.py Outdated Show resolved Hide resolved
src/ape_accounts/accounts.py Outdated Show resolved Hide resolved
@antazoey
Copy link
Member

Another reason a separate method might be better: Accounts that don't support this will be more obvious. Otherwise by the same rationale, we'd have to warn or raise exceptions in accounts trying to sign a bytes value of a certain length, which assumes stuff about the signer's intent.

By having separate methods, the intent is clear, and when the account does not support such thing, it can raise a NotImplementedError which is more intuitive than it silently working or warning "hey in case you are trying to size a hash directly, i can't do that, but if not, carry on). It'd be different if signing bytes always worked this way, but still not as good as separate methods imo.

Just something I was thinking about ...

@fubuloubu
Copy link
Member Author

Another reason a separate method might be better: Accounts that don't support this will be more obvious. Otherwise by the same rationale, we'd have to warn or raise exceptions in accounts trying to sign a bytes value of a certain length, which assumes stuff about the signer's intent.

By having separate methods, the intent is clear, and when the account does not support such thing, it can raise a NotImplementedError which is more intuitive than it silently working or warning "hey in case you are trying to size a hash directly, i can't do that, but if not, carry on). It'd be different if signing bytes always worked this way, but still not as good as separate methods imo.

Just something I was thinking about ...

I'm on board with this change, also makes it easier to roll out

@fubuloubu fubuloubu force-pushed the feat/AccountAPI/sign-raw-hash32 branch from df3c28c to 1045ec5 Compare March 21, 2024 18:25
@fubuloubu fubuloubu marked this pull request as ready for review March 21, 2024 18:26
@fubuloubu
Copy link
Member Author

Refactored in fc4b250 as a separate method with NotImplementedError raised by default

@fubuloubu fubuloubu enabled auto-merge (squash) March 21, 2024 18:51
@fubuloubu fubuloubu requested a review from antazoey March 21, 2024 18:52
Copy link
Member

@antazoey antazoey left a comment

Choose a reason for hiding this comment

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

Looks good!
Some small feedback if you'll indulge, but LGTM either way

:class:`~ape.types.signatures.MessageSignature` (optional):
The signature corresponding to the message.
"""
raise NotImplementedError(
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: The @raises_not_implemented decorator does exactly this! same messaging.

@@ -307,6 +325,10 @@ def check_signature(
signature (Optional[:class:`~ape.types.signatures.MessageSignature`]):
The signature to check. Defaults to ``None`` and is not needed when the first
argument is a transaction class.
recover_using_eip191 (bool):
Perform recovery using EIP-191 signed message check. If set False, then will attempt
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Perform recovery using EIP-191 signed message check. If set False, then will attempt
Perform recovery using EIP-191 signed message check. If False, then will attempt

@@ -315,6 +337,8 @@ def check_signature(
data = encode_defunct(text=data)
elif isinstance(data, int):
data = encode_defunct(hexstr=HexBytes(data).hex())
elif isinstance(data, bytes) and (len(data) != 32 or recover_using_eip191):
Copy link
Member

Choose a reason for hiding this comment

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

small perf, maybe, for short-circuit eval on the or stmt

Suggested change
elif isinstance(data, bytes) and (len(data) != 32 or recover_using_eip191):
elif isinstance(data, bytes) and (recover_using_eip191 or len(data) != 32):

as checking a bool is prolly faster than a length check

@fubuloubu fubuloubu merged commit af5689c into ApeWorX:main Mar 21, 2024
16 checks passed
@fubuloubu fubuloubu deleted the feat/AccountAPI/sign-raw-hash32 branch March 21, 2024 21:09
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.

Allow signing raw 32 byte message hash directly
2 participants