-
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
Changes from 16 commits
60ef24b
9fa1641
920677b
feeca2b
3ac6a30
3a1ad2c
31d5539
067b806
d2e135a
ffe917d
290f1b7
7857ca8
f3f5398
083f995
a807dee
6f6c848
72e5901
4f4316f
9fc7f27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,8 @@ import {AddressResolver} from "../../common/AddressResolver.sol"; | |
import {LibMath} from "../../libs/LibMath.sol"; | ||
import {LibUtils} from "./LibUtils.sol"; | ||
import {TaikoData} from "../../L1/TaikoData.sol"; | ||
import {LibVerifyTrusted} from "./proofTypes/LibVerifyTrusted.sol"; | ||
import {LibVerifyZKP} from "./proofTypes/LibVerifyZKP.sol"; | ||
|
||
library LibProving { | ||
using LibMath for uint256; | ||
|
@@ -29,13 +31,11 @@ library LibProving { | |
error L1_EVIDENCE_MISMATCH(bytes32 expected, bytes32 actual); | ||
error L1_FORK_CHOICE_NOT_FOUND(); | ||
error L1_INVALID_EVIDENCE(); | ||
error L1_INVALID_PROOF(); | ||
error L1_INVALID_PROOF_OVERWRITE(); | ||
error L1_NOT_SPECIAL_PROVER(); | ||
error L1_ORACLE_PROVER_DISABLED(); | ||
error L1_SAME_PROOF(); | ||
error L1_SYSTEM_PROVER_DISABLED(); | ||
error L1_SYSTEM_PROVER_PROHIBITED(); | ||
error L1_INVALID_PROOFTYPE(); | ||
error L1_NO_AUTH_TO_OVERWRITE_FK(); | ||
error L1_NOT_ALL_REQ_PROOF_VERIFIED(); | ||
error L1_NOT_ENABLED_PROOFTYPE(); | ||
error L1_NOTHING_TO_OVERWRITE(); | ||
|
||
function proveBlock( | ||
TaikoData.State storage state, | ||
|
@@ -62,48 +62,6 @@ library LibProving { | |
revert L1_EVIDENCE_MISMATCH(blk.metaHash, evidence.metaHash); | ||
} | ||
|
||
// Separate between oracle proof (which needs to be overwritten) | ||
// and non-oracle but system proofs | ||
address specialProver; | ||
if (evidence.prover == address(0)) { | ||
specialProver = resolver.resolve("oracle_prover", true); | ||
if (specialProver == address(0)) { | ||
revert L1_ORACLE_PROVER_DISABLED(); | ||
} | ||
} else if (evidence.prover == address(1)) { | ||
specialProver = resolver.resolve("system_prover", true); | ||
if (specialProver == address(0)) { | ||
revert L1_SYSTEM_PROVER_DISABLED(); | ||
} | ||
|
||
if (config.realProofSkipSize <= 1 || blockId % config.realProofSkipSize == 0) { | ||
revert L1_SYSTEM_PROVER_PROHIBITED(); | ||
} | ||
} | ||
|
||
if (specialProver != address(0) && msg.sender != specialProver) { | ||
if (evidence.proof.length != 64) { | ||
revert L1_NOT_SPECIAL_PROVER(); | ||
} else { | ||
uint8 v = uint8(evidence.verifierId); | ||
bytes32 r; | ||
bytes32 s; | ||
bytes memory data = evidence.proof; | ||
assembly { | ||
r := mload(add(data, 32)) | ||
s := mload(add(data, 64)) | ||
} | ||
|
||
// clear the proof before hashing evidence | ||
evidence.verifierId = 0; | ||
evidence.proof = new bytes(0); | ||
|
||
if (specialProver != ecrecover(keccak256(abi.encode(evidence)), v, r, s)) { | ||
revert L1_NOT_SPECIAL_PROVER(); | ||
} | ||
} | ||
} | ||
|
||
TaikoData.ForkChoice storage fc; | ||
|
||
uint256 fcId = | ||
|
@@ -124,80 +82,66 @@ library LibProving { | |
} else { | ||
state.forkChoiceIds[blk.blockId][evidence.parentHash][evidence.parentGasUsed] = fcId; | ||
} | ||
} else if (evidence.prover == address(0)) { | ||
// This is the branch the oracle prover is trying to overwrite | ||
fc = blk.forkChoices[fcId]; | ||
if ( | ||
fc.blockHash == evidence.blockHash && fc.signalRoot == evidence.signalRoot | ||
&& fc.gasUsed == evidence.gasUsed | ||
) revert L1_SAME_PROOF(); | ||
} else { | ||
// This is the branch provers trying to overwrite | ||
fc = blk.forkChoices[fcId]; | ||
if (fc.prover != address(0) && fc.prover != address(1)) { | ||
revert L1_ALREADY_PROVEN(); | ||
} | ||
|
||
if ( | ||
fc.blockHash != evidence.blockHash || fc.signalRoot != evidence.signalRoot | ||
|| fc.gasUsed != evidence.gasUsed | ||
) revert L1_INVALID_PROOF_OVERWRITE(); | ||
revert L1_ALREADY_PROVEN(); | ||
} | ||
|
||
fc.blockHash = evidence.blockHash; | ||
fc.signalRoot = evidence.signalRoot; | ||
fc.gasUsed = evidence.gasUsed; | ||
fc.prover = evidence.prover; | ||
|
||
if (evidence.prover == address(1)) { | ||
fc.provenAt = uint64(block.timestamp.max(blk.proposedAt + state.proofTimeTarget)); | ||
} else { | ||
fc.provenAt = uint64(block.timestamp); | ||
fc.provenAt = uint64(block.timestamp); | ||
|
||
// Put together the input for proof and signature verification | ||
uint256[10] memory inputs; | ||
|
||
inputs[0] = uint256(uint160(address(resolver.resolve("signal_service", false)))); | ||
inputs[1] = | ||
uint256(uint160(address(resolver.resolve(config.chainId, "signal_service", false)))); | ||
inputs[2] = uint256(uint160(address(resolver.resolve(config.chainId, "taiko", false)))); | ||
|
||
inputs[3] = uint256(evidence.metaHash); | ||
inputs[4] = uint256(evidence.parentHash); | ||
inputs[5] = uint256(evidence.blockHash); | ||
inputs[6] = uint256(evidence.signalRoot); | ||
inputs[7] = uint256(evidence.graffiti); | ||
inputs[8] = (uint256(uint160(evidence.prover)) << 96) | ||
| (uint256(evidence.parentGasUsed) << 64) | (uint256(evidence.gasUsed) << 32); | ||
|
||
// Also hash configs that will be used by circuits | ||
inputs[9] = uint256(config.blockMaxGasLimit) << 192 | ||
| uint256(config.maxTransactionsPerBlock) << 128 | ||
| uint256(config.maxBytesPerTxList) << 64; | ||
|
||
bytes32 instance; | ||
assembly { | ||
instance := keccak256(inputs, mul(32, 10)) | ||
} | ||
|
||
if (evidence.prover != address(0) && evidence.prover != address(1)) { | ||
uint256[10] memory inputs; | ||
|
||
inputs[0] = uint256(uint160(address(resolver.resolve("signal_service", false)))); | ||
inputs[1] = | ||
uint256(uint160(address(resolver.resolve(config.chainId, "signal_service", false)))); | ||
inputs[2] = uint256(uint160(address(resolver.resolve(config.chainId, "taiko", false)))); | ||
|
||
inputs[3] = uint256(evidence.metaHash); | ||
inputs[4] = uint256(evidence.parentHash); | ||
inputs[5] = uint256(evidence.blockHash); | ||
inputs[6] = uint256(evidence.signalRoot); | ||
inputs[7] = uint256(evidence.graffiti); | ||
inputs[8] = (uint256(uint160(evidence.prover)) << 96) | ||
| (uint256(evidence.parentGasUsed) << 64) | (uint256(evidence.gasUsed) << 32); | ||
|
||
// Also hash configs that will be used by circuits | ||
inputs[9] = uint256(config.blockMaxGasLimit) << 192 | ||
| uint256(config.maxTransactionsPerBlock) << 128 | ||
| uint256(config.maxBytesPerTxList) << 64; | ||
|
||
bytes32 instance; | ||
assembly { | ||
instance := keccak256(inputs, mul(32, 10)) | ||
uint16 mask = config.proofToggleMask; | ||
for (uint16 i; i < evidence.blockProofs.length; ) { | ||
TaikoData.TypedProof memory proof = evidence.blockProofs[i]; | ||
if (proof.proofType == 0) { | ||
revert L1_INVALID_PROOFTYPE(); | ||
} | ||
|
||
(bool verified, bytes memory ret) = resolver.resolve( | ||
LibUtils.getVerifierName(evidence.verifierId), false | ||
).staticcall( | ||
bytes.concat( | ||
bytes16(0), | ||
bytes16(instance), // left 16 bytes of the given instance | ||
bytes16(0), | ||
bytes16(uint128(uint256(instance))), // right 16 bytes of the given instance | ||
evidence.proof | ||
) | ||
); | ||
uint16 bitMask = uint16(1 << (proof.proofType - 1)); | ||
if ((mask & bitMask) == 0) { | ||
revert L1_NOT_ENABLED_PROOFTYPE(); | ||
} | ||
|
||
verifyTypedProof(proof, instance, resolver); | ||
mask &= ~bitMask; | ||
|
||
if (!verified || ret.length != 32 || bytes32(ret) != keccak256("taiko")) { | ||
revert L1_INVALID_PROOF(); | ||
unchecked { | ||
++i; | ||
} | ||
} | ||
|
||
if(mask != 0) { | ||
revert L1_NOT_ALL_REQ_PROOF_VERIFIED(); | ||
} | ||
|
||
emit BlockProven({ | ||
id: blk.blockId, | ||
parentHash: evidence.parentHash, | ||
|
@@ -208,6 +152,41 @@ library LibProving { | |
}); | ||
} | ||
|
||
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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm why? The functionality of this is significantly different from |
||
) internal { | ||
if (msg.sender != resolver.resolve("forkchoice_failsafe", false)) { | ||
revert L1_NO_AUTH_TO_OVERWRITE_FK(); | ||
} | ||
|
||
if (blockId <= state.lastVerifiedBlockId || blockId >= state.numBlocks) { | ||
adaki2004 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
revert L1_BLOCK_ID(); | ||
} | ||
|
||
TaikoData.Block storage blk = state.blocks[blockId % config.ringBufferSize]; | ||
|
||
// We make it so this will always be the first fork choice | ||
TaikoData.ForkChoice storage fc = blk.forkChoices[1]; | ||
|
||
// In case it was 0 (unproven) - it's fine we prove it here otherwise | ||
// it does not matter if 2 or 3, only fk idx 1 is valid. | ||
unchecked { | ||
++blk.nextForkChoiceId; | ||
} | ||
|
||
fc.key = LibUtils.keyForForkChoice(evidence.parentHash, evidence.parentGasUsed); | ||
|
||
fc.blockHash = evidence.blockHash; | ||
fc.signalRoot = evidence.signalRoot; | ||
fc.gasUsed = evidence.gasUsed; | ||
fc.prover = msg.sender; // "special" prover, the failsafe one | ||
fc.provenAt = blk.proposedAt + state.proofTimeTarget; | ||
} | ||
|
||
function getForkChoice( | ||
TaikoData.State storage state, | ||
TaikoData.Config memory config, | ||
|
@@ -222,4 +201,30 @@ library LibProving { | |
if (fcId == 0) revert L1_FORK_CHOICE_NOT_FOUND(); | ||
fc = blk.forkChoices[fcId]; | ||
} | ||
|
||
function verifyTypedProof( | ||
TaikoData.TypedProof memory proof, | ||
bytes32 instance, | ||
AddressResolver resolver | ||
) internal view { | ||
if (proof.proofType == 1) { | ||
// This is the regular ZK proof and required based on the flag | ||
// in config.proofToggleMask | ||
LibVerifyZKP.verifyProof( | ||
resolver, | ||
proof.proof, | ||
instance, | ||
proof.verifierId | ||
); | ||
} else if (proof.proofType == 2) { | ||
// This is the SGX signature proof and required based on the flag | ||
// in config.proofToggleMask | ||
LibVerifyTrusted.verifyProof( | ||
resolver, | ||
proof.proof, | ||
instance, | ||
proof.verifierId | ||
); | ||
} | ||
} | ||
} |
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.