-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Conversation
There was a problem hiding this 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; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
library LibVerifySGX { | ||
error L1_INVALID_SGX_SIGNATURE(); | ||
|
||
function verifySgxProof( |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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; ) { |
There was a problem hiding this comment.
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");
There was a problem hiding this comment.
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 );
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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 theLibVerifying.sol
do this job and revert ?
What do you mean with revert?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uint16?
I think this PR still has a high level design issue: the |
Need to remvoe ""oracle_prover" and "system_prover" everywhere. |
I guess this is a 'special proving scenario' only available if we explicitly set so (in the config). Then |
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. |
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. |
Will incorporate #13777 into this PR as well! |
function setForkChoice( | ||
TaikoData.State storage state, | ||
TaikoData.Config memory config, | ||
AddressResolver resolver, | ||
uint256 blockId, | ||
TaikoData.BlockEvidence memory evidence |
There was a problem hiding this comment.
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()
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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")) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
- 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
@dantaik (Latest commit changes):
Now we have 2 options to prove blocks. Gas comparison: If I modify the code in a way that inline both the code of the |
@adaki2004 given that our core protocol and tokenomics have changed a lot, shall we close this PR then re-implement the idea later? |
Yes,im closing it (for now) without deletion. |
Related to: #13731