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): Enable protocol to handle multiple proof types #13745

Closed
wants to merge 19 commits into from

Conversation

adaki2004
Copy link
Contributor

@adaki2004 adaki2004 commented May 12, 2023

Related to: #13731

  • Code changes required to verify multiple proofs
  • Code changes required to overwrite unverified fork choice
  • Test changes of multiple proofs (ZKP & SGX) + overwrite unverified FK
  • Adjust other tests to changes

@adaki2004 adaki2004 changed the title Enable protocol to handle multiple proof types feat(protocol): Enable protocol to handle multiple proof types May 12, 2023
Copy link
Contributor

@Brechtpd Brechtpd left a comment

Choose a reason for hiding this comment

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

Looking good!

// RESERVED_Y_ONLY, // 0000 1000
// ZKP_AND_SGX, // 0000 0011
// X_ZKP_SGX, // 0000 0111
uint8 proofTypeEnabled;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice! Maybe a more clear name would be proofToggleMask or something.

)
// config.proofTypeEnabled can support 8 different proofs on 8 bits
for (uint8 index; index < 8; ) {
if (index == 0 && ((config.proofTypeEnabled >> index) & 1) == 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (index == 0 && ((config.proofTypeEnabled >> index) & 1) == 1) {
if (((config.proofTypeEnabled >> index) & 1) == 1) {
if (index == 0) {
// ...
} else if (index == 1) {
// ...
}

"signal_service",
false
)
// config.proofTypeEnabled can support 8 different proofs on 8 bits
Copy link
Contributor

Choose a reason for hiding this comment

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

The plan is still to abstract the actual implementations away in the verifier, or do you think a simple for loop with some ifs would also work? Seems okay to me like this, but I'm mostly concerned about the additional data needed for the different proof types, having a separate contract for each allows things to be very flexible with what a new proof type needs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, agree, thanks for the remark! Outsourced to new files now.

- delete systemProofCoolDown config param
- rename oracle test and add new test case checking the signature check logic
library LibVerifySGX {
error L1_INVALID_SGX_SIGNATURE();

function verifySgxProof(
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can also use this library for non-sgx signers, so I suggested to change the name to LibVerifyTrusted

library LibVerifySGX {
error L1_INVALID_SGX_SIGNATURE();

function verifySgxProof(
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 verifyProof?

library LibVerifyZKP {
error L1_INVALID_PROOF();

function verifyZkProof(
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 verifyProof?

@@ -74,6 +81,11 @@ library TaikoData {
TaikoData.EthDeposit[] depositsProcessed;
}

struct BlockProof {
Copy link
Contributor

Choose a reason for hiding this comment

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

How about we added a verifierType here?
uint16 proofType,

}

// config.proofToggleMask can support 8 different proof types on 8 bits
for (uint8 index; index < 8; ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we do something like this?

uint8 mask = config.proofToggleMask;

for (uint i = 0; i < blockProofs.length; i++) {
  TypedProof memory proof = blockProofs[i];
  uint8 bitMask = 1 << (proof.proofType - 1);
  require(mask & bitMask) != 0, "type_not_enabled_or_duplicated");
  _verifyTypedProof(proof);
  mask &= ~bitMask;
}
require(mask == 0, "not_all_requierd_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.

I also suggest that the cooldown period is a value returned from _verifyTypedProof:

uint8 mask = config.proofToggleMask;
uint64 maxCoolDownPeriod;
for (uint i = 0; i < blockProofs.length; i++) {
  TypedProof memory proof = blockProofs[i];
  uint8 bitMask = 1 << (proof.proofType - 1);
  require(mask & bitMask) != 0, "type_not_enabled_or_duplicated");
  uint64 coolDownPeriod = _verifyTypedProof(proof);
  maxCoolDownPeriod = maxCoolDownPeriod.max(coolDownPeriod);
  mask &= ~bitMask;
}
require(mask == 0, "not_all_requierd_proof_verified");
fc.provenAt = uint64(block.timestamp +maxCoolDownPeriod );

Copy link
Contributor

Choose a reason for hiding this comment

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

Inside, LibVerifyerSGX and LibVerifyingZKP, in the verify function, we also return a cooldown period. This allows for different cooldown period for different verifiers, then we just take the max.

Copy link
Contributor Author

@adaki2004 adaki2004 May 15, 2023

Choose a reason for hiding this comment

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

Question to this cooldown period: Since this affects proofTime = provenAt - proposedAt and cooldown only delays verification, why should not we let the the LibVerifying.sol do this job and revert ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Inside, LibVerifyerSGX and LibVerifyingZKP, in the verify function, we also return a cooldown period. This allows for different cooldown period for different verifiers, then we just take the max.

Maybe min makes more sense otherwise adding a proof type can increase the cooldown, although cooldown period length will always a bit tricky and handwavy and not sure if something that can be decided per proof type.

Question to this cooldown period: Since this affects proofTime = provenAt - proposedAt and cooldown only delays verification, why should not we let the the LibVerifying.sol do this job and revert ?

What do you mean with revert?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean with revert?

Simply break/revert, not allow to be verified.

@@ -74,6 +81,11 @@ library TaikoData {
TaikoData.EthDeposit[] depositsProcessed;
}

struct BlockProof {
Copy link
Contributor

Choose a reason for hiding this comment

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

BlockProof => TypedProof

// RESERVED_Y_ONLY, // 0000 1000
// ZKP_AND_SGX, // 0000 0011
// X_ZKP_SGX, // 0000 0111
uint8 proofToggleMask;
Copy link
Contributor

Choose a reason for hiding this comment

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

uint16?

@dantaik
Copy link
Contributor

dantaik commented May 15, 2023

I think this PR still has a high level design issue: the proofToggleMask can only specify an AND logics, but not something like: *if a valid SGX proof is given, then ZKP is not necessary; *.

@dantaik
Copy link
Contributor

dantaik commented May 15, 2023

Need to remvoe ""oracle_prover" and "system_prover" everywhere.

@adaki2004
Copy link
Contributor Author

I think this PR still has a high level design issue: the proofToggleMask can only specify an AND logics, but not something like: *if a valid SGX proof is given, then ZKP is not necessary; *.

I guess this is a 'special proving scenario' only available if we explicitly set so (in the config). Then
if (proofToggleMask == type(uint16).max) could be one.
Let me know if it works with you. I guess if this is the only special scenario we want to do support (like the system_proof previously) this could work well.

@Brechtpd
Copy link
Contributor

I think this PR still has a high level design issue: the proofToggleMask can only specify an AND logics, but not something like: *if a valid SGX proof is given, then ZKP is not necessary; *.

Could you give a scenario where we would use conditional logic like that? I saw this only really as an all proofs that are enabled are required thing.

I could see some benefit of the different proofs coming in at different times (like SGX proof first, which would set the cooldown period still pretty high, then a zkp proof that lowers it) but in practice I think probably doesn't really make a difference (we'll always want the zkp anyway, which is the slowest, and all of them being required all the time is best for security), and complicates things quite a bit like the fee payments.

We could still implement something like this in a verifier contract, the main protocol would would just require a single proof related to that verifier contract, but internally the verifier contract can do whatever it wants so could contain multiple sub-proofs with some special logic. This would actually be a pretty nice way to implement the testnet scenario where we want to skip some ZKP proofs, then there's no need to build this into the core protocol.

So maybe let's simplify the core protocol as much as possible and only let it support a single "proof" (as in some data and a verifier contract to call), then let the verifier contract handle the rest.

@adaki2004
Copy link
Contributor Author

@Brechtpd @dantaik
Thinking out loud, shouldn't we branch off from current main - call it alpha4 - and then every protocol change could go there from now on ?

@Brechtpd
Copy link
Contributor

Probably makes more sense to do the opposite which I think is what we already do, make a release for alpha-n, and keep working on main.

@adaki2004
Copy link
Contributor Author

Will incorporate #13777 into this PR as well!

Comment on lines 162 to 167
function setForkChoice(
TaikoData.State storage state,
TaikoData.Config memory config,
AddressResolver resolver,
uint256 blockId,
TaikoData.BlockEvidence memory evidence
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Put back this functionality into proveBlock()

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm why? The functionality of this is significantly different from proveBlock, which made proveBlock really complicated before.

// RESERVED_Y_ONLY, // 0000 1000
// ZKP_AND_SGX, // 0000 0011
// X_ZKP_SGX, // 0000 0111
uint16 proofToggleMask;
Copy link
Contributor Author

@adaki2004 adaki2004 May 23, 2023

Choose a reason for hiding this comment

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

Maybe have it static 2 proofs now - instead of a bitmap. Future updates will handle rest.

.resolve(LibUtils.getVerifierName(verifierId), false)
.staticcall(bytes.concat(inputHash, proof));

if (!verified || ret.length != 32 || bytes32(ret) != keccak256("taiko"))
Copy link
Member

Choose a reason for hiding this comment

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

Shall we define keccak256("taiko") as a library constant to save some gas?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, good one ! Thanks David.

adaki2004 and others added 2 commits May 23, 2023 14:56
- Get rid of toggle mask and restrict to ZKP + SGX only (in case latter is enabled)
- Removed setForkChoice() function and its logis is incorporated to proveBlock() hance function signature/interface is the same
- Added SGX enable flag
- Removed unnecessary vars (like proofType) because the static position within the array will determine if ZKP or SGX
- Removed unused errors
- Removed irrelevant tests
@adaki2004
Copy link
Contributor Author

@dantaik
Incorporated all the changes we discussed this morning.

(Latest commit changes):

  • Get rid of toggle mask and restrict to ZKP + SGX (only in case it is enabled)
  • Removed setForkChoice() function and incorporated the (Super Prover) into proveBlock() since sharing the same function signature/interface
  • Added SGX enable flag
  • Removed unnecessary vars (like proofType) because the static position in the array will determine if ZKP or SGX (we can save some gas here)
  • Removed unused errors
  • Removed irrelevant tests

Now we have 2 options to prove blocks.
A: ZKP + SGX (if enabled, or ZKP only in case we dont enable SGX) and those have to come together
B: There is a SuperProver which can write the proof

Gas comparison:
Old (but still with SGX): 36.805 gwei on average
kép

New: 34.143 gwei
kép

If I modify the code in a way that inline both the code of the LibVerifyZKP.verifyProof and LibVerifyTrusted.verifyProof we could save approx. 94gwei, but i thought does not worth the effort vs. maintenance (readability) later on.

@dantaik
Copy link
Contributor

dantaik commented Jul 1, 2023

@adaki2004 given that our core protocol and tokenomics have changed a lot, shall we close this PR then re-implement the idea later?

@adaki2004
Copy link
Contributor Author

Yes,im closing it (for now) without deletion.

@adaki2004 adaki2004 closed this Jul 1, 2023
@dantaik dantaik deleted the multi_prover_possibility branch September 25, 2023 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

feat(protocol): Even more basic multi-prover with PoA
4 participants