EIP-7212: Precompiled for secp256r1 Curve Support

As an RIP, the proposal has not been planned for the Ethereum mainnet yet. It can be considered in the future.

Integration Update

With the Napoli Hard Fork in the Polygon PoS Chain, RIP-7212 is active on the mainnet. Congrats to the team for being the first to implement an RIP on the mainnet.

Announcement Tweet

2 Likes

Do you guys have a code snippet on how to implement it onchain in Solidity? Thank you

Fastest implementation is here

Actually i’m still working on it and will push a version around 160K in April which will go to an audit.

You can easily adapt this script to bench extra libraries ONCHAIN (this one demonstrate benching of daimo and FCL). (it is easy to forgot to configure toml effectively, or cancel stack optimization).

Thanks for sharing additional resources @rdubois-crypto!

Also, sharing basic wrapper contract to call the precompile contract.

Function here
    /**
     * @notice Calls the verifier function with given params
     * @param verifier address     - Address of the verifier contract
     * @param hash bytes32         - Signed data hash
     * @param rs bytes32[2]        - Signature array for the r and s values
     * @param pubKey bytes32[2]    - Public key coordinates array for the x and y values
     * @return - bool - Return the success of the verification
     */
    function callVerifier(
        address verifier,
        bytes32 hash,
        bytes32[2] memory rs,
        bytes32[2] memory pubKey
    ) internal view returns (bool) {
        /**
         * Prepare the input format
         * input[  0: 32] = signed data hash
         * input[ 32: 64] = signature r
         * input[ 64: 96] = signature s
         * input[ 96:128] = public key x
         * input[128:160] = public key y
         */
        bytes memory input = abi.encodePacked(hash, rs[0], rs[1], pubKey[0], pubKey[1]);

        // Make a call to verify the signature
        (bool success, bytes memory data) = verifier.staticcall(input);

        uint256 returnValue;
        // Return true if the call was successful and the return value is 1
        if (success && data.length > 0) {
            assembly {
                returnValue := mload(add(data, 0x20))
            }
            return returnValue == 1;
        }

        // Otherwise return false for the unsucessful calls and invalid signatures
        return false;
    }
1 Like

Check out the interesting conversation with @ulerdogan on the present Signature scheme, special Elliptic Curves & a deep dive inside EOA while providing the overview of RIP7212 on PEEPanEIP

1 Like

Hi ulerdogan
I’m a developer from BSC.
BSC is part of the EVM ecosystem, and the client is based on a fork of go-ethereum, and will continue to provide feedback on any bugs found.
I hope when setting these EIPs, we can consider BSC: the BSC precompile addresses 0x100~0x105 are already in use,
Can the RIP-related precompile addresses use a different address range? For example, starting from 0x1000.

1 Like

The RIP is already deployed on several L2s.

See Polygon: PIP-27: Precompiled for secp256r1 Curve Support - #7 by ulerdogan - Canonical PIPs - Polygon Community Forum
See Arbitrum: AIP: Support RIP-7212 for Account Abstraction Wallets - #13 by cliffton.eth - Proposals - Arbitrum
See zkSync: zkSync Protocol Upgrade v24: New precompiles, more blobs, Validiums, and more. · zkSync-Community-Hub/zksync-developers · Discussion #519 · GitHub

I don’t think it’s reasonable to change it now and disrupt all the code that was deployed to take advantage of this new EIP and prove its utility for next Ethereum upgrade.

Furthermore, that reserved RIP range is to enshrine a range for experimentation for L2s so it’s fine for EVM forks to also use this knowing Ethereum L1s changes won’t reuse that range. See also: RollCall Breakout #3: Precompiles and the RIP Process

3 Likes

@mratsim thanks for the explanations!

Hi @NathanBSC, isn’t 0x1000 range also blocked by these? Also, I couldn’t find what blocks the 0x100 range. In case of misunderstanding: 0x1000x0000000000000000000000000000000000000100

A couple of questions regarding the P256 Verifier precompile.

  • There was some discussion about adding a progressive precompile from Daimo where their precompile address was 0xc2b78104907F722DABAc4C69f826a522B2754De4 but the deployment of precompile was at address 0x100. And similarly the FreshCryptoLib’s implementation address was 0xE9399D1183a5cf9E14B120875A616b6E2bcB840a. So, If I am building something using Daimo or FreshCryptoLib implementation, I will to create a wrapper that fallbacks to 0x100 address, right?

  • @rdubois-crypto does the current state of FreshCryptoLib implementation use the 0x100 address? Also, when are you expecting a complete audited implementation?

  • If I want to directly call the precompile from solidity, I will have to do address(100).staticcall({params}) or is there any wrapper like ecrecover?

Answer to first question is “yes”. (Solving third ?).

Considering the second question :

FCL is not gonna be audited, but has been scrutinized by Base cryptographer’s team. We believe that 7212 is amazing at the current moment, but we also believe that it is too limited to introduce only one curve, while a generic precompile can be introduced for (quasi) no extra cost, leading to several use cases:

Using RIP7696 you can emulate P256, Ed25519, starkcurve, babyjujub, palla, vesta, …

We are now pushing 7696, which can emulate 7212 as a specific case.

  • As a progressive compile in solidity for a lower cost (160K) than FCL(200K) or Daimo (330K).
    You will find an example of all wychproof vectors assessment here:
    crypto-lib/test/libSCL_ecdsa.t.sol at audit-cryptoexperts · get-smooth/crypto-lib · GitHub

  • As a circuit/node implementation: Optimizations are independant of the language (just pure algorithmic), so it is transposable to any circuit/node implementation (of course it requires some work). We are working with a L2 on a tradeoff rn.

RIP7696 solidity implementation is going through 2 independant audits. The first is running right now, ending end of June, before a second one starting 1st July.

1 Like

I think that RIP-7212 has a unfortunate flaw–as specified after this update from two weeks ago and deployed on Polygon PoS: it now returns empty data for an invalid signature.

Why is this an issue?

It means there’s no clean way to implement a fallback

Contracts cannot distinguish between (invalid signature) and (7212 not deployed). On a chain without the precompile, calling 0x100 with any calldata will return (success=true, return value=0x).

On a chain with the precompile, an invalid signature will return exactly the same.

You can still implement a fallback, but the fallback contract will run on invalid signatures even on chains that have the precompile. In practice, this means that invalid signatures will often fail with an out-of-gas instead of a descriptive revert message.

This was changed after Final

Both EIP-7212 and RIP-7212 originally specified that the return value should always be 32 bytes.

Here’s my reconstruction of what happened:

I’m not sure if this is worth fixing, given that it’s now live on Polygon, but it warrants a retrospective on how we can avoid this kind of outcome in the future.

I propose a rule that no new precompile, going forward, should return empty data, since this makes a clean fallback implementation impossible.

Finally, I’d challenge the community to come up with a good process to verify new RIP implementations before they ship. I don’t think this story would’ve happened with an EIP: L1 client implementations have a lot of careful eyes on them. RIPs, by contrast, will land in production on the currently-fastest-shipping L2. This can happen in just a few weeks, as it did here. Once in production, if there are any discrepancies, it’s more feasible to update the spec than the implementation, locking them in forever.

3 Likes

@mratsim thanks for the explanations

Luckily, it looks like 0x100 is not already in use in BSC. However, the address with decimal value 100 (0x64) is in use.

1 Like

Considering some comments received after the Final status, I completely agree with the feedback that the RIP process should proceed in a more actively participated way by ecosystem members. Otherwise, I think that the correct state of the RIP, updated with the last PR (to just fix misunderstandings), is correct in terms of the rollup implementations and discussions in the Rollcalls. Additionally, it was mentioned that a registry could be developed as a solution to the problem you mentioned. So, I think the proposal has not been changed retroactively to adapt to Polygon PoS Chain implementation (also ready with the same specs in other rollups).

1 Like

my mistake
no conflicts with bsc, for bsc addresses 100~105 has been taked, not 0x100~0x105

https://github.com/bnb-chain/bsc/blob/master/core/vm/contracts.go#L242
1 Like

yes, you are right, thx.
we will advance this EIP more quickly.

1 Like

In addition to the data result, a call to the precompile returns a boolean, which is always true in the go implementation. Should it be mentioned in the RIP? Alternatively it might return false if length of the input was not 160 or the components of the input were out of range.

Hey! The given reference implementation in Go and the Final RIP spec are compatible. Unfortunately, as a Final proposal that has already been implemented by some rollups, it’s not open to change.

Given spec:

# Output data:
- If the signature verification process succeeds, it returns 1 in 32 bytes format.
- If the signature verification process fails, it does not return any output data.