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(protocol): Separate proof verification to a standalone, deployed contract #13794

Closed
wants to merge 4 commits into from

Conversation

adaki2004
Copy link
Contributor

Base branch of this introduced multi-prover possibilities.
This one outsourced proof verification into a separate, deployed contract.

Gas comparison:

Base: (42.074 gwei)
kép

This one (47.418 gwei)
kép

IMPORTANT NOTE

Code to calculate the public instance below stayed in LibProving.

// Put together the input for proof and signature verification
        uint256[9] memory inputs;
...

        bytes32 instance;
        assembly {
            instance := keccak256(inputs, mul(32, 9))
        }

I decided to leave it there, because:

  1. tested a version where i pass the Evidence towards the verifier contract and calculate it there, but it adds additional data (overhead) which costs roughly +1K gas.
  2. In case there is an additional protocol upgrade which changes the Evidence we need to upgrade this new Verifier anyways as well..

Only issue I see with this is: there will be a completely new proof method which will require only some portion of the Evidence so would be useful having that extra data at the verifier contract.

With current plans (ZK, SGX and Failsafe signature overwrite) i see it a no issue, but who knows, maybe worth to pass the whole Evidence data ?
Please let me know what you think @dantaik @Brechtpd

@adaki2004 adaki2004 requested review from Brechtpd and dantaik May 22, 2023 11:44
@adaki2004 adaki2004 changed the title feat(protocol): Separate proof verification to a standalon deployed contract feat(protocol): Separate proof verification to a standalone, deployed contract May 22, 2023
@adaki2004 adaki2004 self-assigned this May 22, 2023
function verifyProof(
bytes32 instance,
TaikoData.TypedProof[] calldata blockProofs,
AddressResolver resolver
Copy link
Contributor

Choose a reason for hiding this comment

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

We should not pass in the resolver as this contract shall be a resolver itself.

Copy link
Contributor Author

@adaki2004 adaki2004 May 22, 2023

Choose a reason for hiding this comment

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

Well, yes, so I did it on purpose (until there is no setter for AddressManager and wanted to use the 'common' one). Will change tomorrow when addressing the PR findings.

*/
function verifyProof(
bytes32 instance,
TaikoData.TypedProof[] calldata blockProofs,
Copy link
Contributor

Choose a reason for hiding this comment

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

What if you change calldata to memory? Will the cost be lower?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, calldata is much cheaper.

function verifyProof(
bytes32 instance,
TaikoData.TypedProof[] calldata blockProofs,
AddressResolver resolver
Copy link
Contributor

Choose a reason for hiding this comment

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

Should remove resolver as it is implementation specific.

Copy link
Contributor Author

@adaki2004 adaki2004 May 22, 2023

Choose a reason for hiding this comment

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

Referred to this in the prev. point.

import {TaikoData} from "./TaikoData.sol";
import {AddressResolver} from "../common/AddressResolver.sol";

interface IProofVerifier {
Copy link
Contributor

Choose a reason for hiding this comment

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

rename the file to IProofVerifier.sol

}

/// @custom:security-contact [email protected]
contract TaikoProofVerifier is TaikoErrors {
Copy link
Contributor

Choose a reason for hiding this comment

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

Rename to ProofVerifier? (and the filename)

@@ -13,6 +13,7 @@ import {TaikoToken} from "../contracts/L1/TaikoToken.sol";
import {SignalService} from "../contracts/signal/SignalService.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {TaikoL1TestBase} from "./TaikoL1TestBase.t.sol";
import {TaikoProofToggleMask, TaikoProofVerifier} from "../contracts/L1/TaikoProofVerifier.sol";
Copy link
Contributor

Choose a reason for hiding this comment

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

TaikoProofToggleMask not used at all.

Copy link
Contributor Author

@adaki2004 adaki2004 May 22, 2023

Choose a reason for hiding this comment

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

Indeed, here not, but we need to have it for toggle mask setter during upgrades. If you referred to this line exaclty then sure, i'll delete it, thanks!

import {LibVerifyTrusted} from "./libs/proofTypes/LibVerifyTrusted.sol";
import {LibVerifyZKP} from "./libs/proofTypes/LibVerifyZKP.sol";

library TaikoProofToggleMask {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need the code in a library?

Copy link
Contributor Author

@adaki2004 adaki2004 May 22, 2023

Choose a reason for hiding this comment

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

Not necessary, just given the fact it's 3 lines and highly related to the ProofVerifier code (so during uprages, this is how we can manage setting the mask) i thought fine setting here. Let me know and i can put it to a different file.

* @param blockProofs Proof array
* @param resolver Current (up-to-date) address resolver
*/
function verifyProof(
Copy link
Contributor

Choose a reason for hiding this comment

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

rename to verifyProofs?

}

if(mask != 0) {
revert L1_NOT_ALL_REQ_PROOF_VERIFIED();
Copy link
Contributor

Choose a reason for hiding this comment

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

@adaki2004 Lets talk about it tomorrow. But i feel like this current code can only express AND logic, not the OR logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is true however:

    1. This scenario is covered by an option to (over)write the correct fork choice so a 'SuperProver' is still available.
    1. Is somewhat Brecht's answer here answering your question ?
    1. If you mean 'in general' I'd say - the current implementation of this is indeed does not implement an OR relationship, but this is the beauty of this separation. We do not need to handle things just now, it lives as a standalone deployment and in case we need to OR / AND, we can very easily adjust it.

Comment on lines +20 to +24
function verifyProofs(
bytes32 instance,
TaikoData.TypedProof[] calldata blockProofs,
AddressResolver resolver
) external;
Copy link
Contributor

@Brechtpd Brechtpd May 22, 2023

Choose a reason for hiding this comment

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

Suggested change
function verifyProofs(
bytes32 instance,
TaikoData.TypedProof[] calldata blockProofs,
AddressResolver resolver
) external;
function verifyProof(bytes32 instance, bytes calldata proof) external;

What do you think of this API? This way we don't have any dependencies and the proof can really be whatever, and proofs don't have to confirm to TypedProof which may not always fit.

EDIT: Updated to still have the instance separate. I would say not to pass in the AddressResolver, I think addresses accessed by the verifier should not be set on the protocol AddressResolver, they should store their own data.

external
view
{
uint16 mask = getToggleMask();
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should simply remove the mask? A verifier just always hard-codes the logic that needs to be followed. If this logic changes, a different verifier contract should be deployed.

Well, it is maybe useful for testing still, so this could be the testnet verifier contract or something like that. But on mainnet, probably just want to keep it as simple as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, makes things more difficult with testing (multi prover vs. exisitng ZK only proofs).
So, i'd just leave it for now - we shall keep it in mind - but since harmless even on mainnet i'd just handle it with low prio - and change it close to mainnet. (?)

@dantaik
Copy link
Contributor

dantaik commented May 23, 2023

@adaki2004 , as mentioned #13791 (comment), I now perfer not splitting verification logics into their own contracts.

@adaki2004
Copy link
Contributor Author

@adaki2004 , as mentioned #13791 (comment), I now perfer not splitting verification logics into their own contracts.

Yes, i got it.

@adaki2004
Copy link
Contributor Author

Closing for now!

@adaki2004 adaki2004 closed this May 23, 2023
@dantaik dantaik deleted the standalone_proof_verifier branch September 25, 2023 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants