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

Optimize EOA signature verification #2661

Merged
merged 16 commits into from
Aug 6, 2021
Merged

Conversation

k06a
Copy link
Contributor

@k06a k06a commented May 5, 2021

Optimize EOA signature verification. Since Berlin HF isContract() would cost 2000 gas.

Prev gas:


EOA: 2000 + 3500

SC: 2000



Now gas:


EOA: 3500

SC: (signature.length is 64 or 65 ? 3500 : 0) + 2000

k06a added 2 commits May 5, 2021 10:36
Prev gas:
EOA: 2000 + 3500
SC: 2000

Now gas:
EOA: 3500
SC: (signature.length is 64 or 65 ? 3500 : 0) + 2000
@frangio frangio requested a review from Amxx July 5, 2021 16:20
@Amxx
Copy link
Collaborator

Amxx commented Jul 12, 2021

Hello @k06a,

First of all, thank you for this PR, we noticed it a while back and failed to let you know. For that I am sorry.
The idea itself is sound, it would result in interesting saving in the EOA case, which is the one we expect to happen the most often. However this PR breaks one thing:

  • ERC1271 doesn't assume anything about the wallet's signature verification primitive, might be secp256k1 ecrecover like for EOAs, but it might be something different, particularly in the future.
  • Some wallets may used another crypto primitive that also uses 64 or 65 bytes long signatures.
  • If these signature return an invalid value for ecrecover, and we fallback to 1271, that is good, but if signature doesn't follow the right format, our ecrecover (which include some checks) could revert.

Unfortunatelly, we have no way to catch an internal library call :/

We will investigate other options (like having a non reverting ecrecover that returns an "error" enum + the recovered address).

What do you think ?

Copy link
Collaborator

@Amxx Amxx left a comment

Choose a reason for hiding this comment

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

[duplicate]

Copy link
Collaborator

@Amxx Amxx left a comment

Choose a reason for hiding this comment

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

We should ensure that, if signer is an ERC1271 implementing contract, the initial ecrecover doesn't revert and the contract call is performed.

@k06a
Copy link
Contributor Author

k06a commented Jul 12, 2021

@Amxx WDYT about ECDSA-tryRecover introduced in the latest commit?

Amxx added 3 commits July 13, 2021 00:20
Conflicts:
	contracts/utils/cryptography/ECDSA.sol
	contracts/utils/cryptography/SignatureChecker.sol
@Amxx Amxx self-requested a review July 12, 2021 22:28
Amxx
Amxx previously approved these changes Jul 12, 2021
@Amxx Amxx requested a review from frangio July 12, 2021 22:29
@k06a
Copy link
Contributor Author

k06a commented Jul 12, 2021

@Amxx nice changes! I believe this tryRecover will be very useful for developers.

@Amxx Amxx self-requested a review July 12, 2021 23:24
@frangio
Copy link
Contributor

frangio commented Jul 13, 2021

The problem I see with this try pattern is that I don't know how we'll migrate it from revert strings to custom errors, once we do that in a couple of months ideally and minimizing breaking changes.

As far as I know custom errors cannot be passed around as values. Perhaps this is something we should raise with the Solidity team.

sudiptab2100
sudiptab2100 previously approved these changes Jul 14, 2021
@k06a
Copy link
Contributor Author

k06a commented Jul 14, 2021

@frangio possibly we could return ABI-encoded version (bytes) of error after migration to custom errors.

@Amxx
Copy link
Collaborator

Amxx commented Jul 14, 2021

@frangio
Maybe we can

  • replace revert strings with an enum, making the return type of tryRecover address, uint8.
  • in the recover functions, replace the bytes(error).length > 0 with error != 0
  • have a error enum → string private function that we call
(address recovered, RecoveryError error) = tryRecover(hash, v, r, s)
if (error != RecoveryError.NoError) {
    revert(_recoveryErrorString(error));
}

In the futur we can remove the _recoveryErrorString private function in favor of custom errors.

Amxx
Amxx previously approved these changes Jul 14, 2021
Amxx
Amxx previously approved these changes Aug 5, 2021
@Amxx Amxx mentioned this pull request Aug 5, 2021
2 tasks
frangio
frangio previously approved these changes Aug 6, 2021
Copy link
Contributor

@frangio frangio left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks everyone.

@frangio frangio merged commit 541e821 into OpenZeppelin:master Aug 6, 2021
@matias904
Copy link

Good

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