-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(protocol): enhance ZKP handling & change proofs order (#288)
- Loading branch information
Showing
11 changed files
with
176 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ | |
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | ||
|
||
import "../common/ConfigManager.sol"; | ||
import "../common/EssentialContract.sol"; | ||
import "../common/IHeaderSync.sol"; | ||
|
@@ -17,7 +19,7 @@ import "./v1/V1Events.sol"; | |
import "./v1/V1Finalizing.sol"; | ||
import "./v1/V1Proposing.sol"; | ||
import "./v1/V1Proving.sol"; | ||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | ||
import "./v1/V1Utils.sol"; | ||
|
||
/** | ||
* @author dantaik <[email protected]> | ||
|
@@ -28,7 +30,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
using SafeCastUpgradeable for uint256; | ||
|
||
LibData.State public state; | ||
uint256[44] private __gap; | ||
uint256[43] private __gap; | ||
|
||
function init( | ||
address _addressManager, | ||
|
@@ -73,7 +75,8 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
V1Proposing.proposeBlock(state, inputs); | ||
V1Finalizing.verifyBlocks( | ||
state, | ||
LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX | ||
LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, | ||
false | ||
); | ||
} | ||
|
||
|
@@ -99,7 +102,8 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
V1Proving.proveBlock(state, AddressResolver(this), blockIndex, inputs); | ||
V1Finalizing.verifyBlocks( | ||
state, | ||
LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX | ||
LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, | ||
false | ||
); | ||
} | ||
|
||
|
@@ -117,7 +121,6 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
* on L2. Note that the `invalidBlock` transaction is supposed to | ||
* be the only transaction in the L2 block. | ||
*/ | ||
|
||
function proveBlockInvalid( | ||
uint256 blockIndex, | ||
bytes[] calldata inputs | ||
|
@@ -130,12 +133,21 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
); | ||
V1Finalizing.verifyBlocks( | ||
state, | ||
LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX | ||
LibConstants.TAIKO_MAX_VERIFICATIONS_PER_TX, | ||
false | ||
); | ||
} | ||
|
||
/** | ||
* Add or remove a prover from the whitelist. | ||
* Verify up to N blocks. | ||
* @param maxBlocks Max number of blocks to verify. | ||
*/ | ||
function verifyBlocks(uint256 maxBlocks) external nonReentrant { | ||
require(maxBlocks > 0, "L1:maxBlocks"); | ||
V1Finalizing.verifyBlocks(state, maxBlocks, true); | ||
} | ||
|
||
/* Add or remove a prover from the whitelist. | ||
* | ||
* @param prover The prover to be added or removed. | ||
* @param whitelisted True to add; remove otherwise. | ||
|
@@ -148,7 +160,15 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
} | ||
|
||
/** | ||
* Return whether a prover is whitelisted. | ||
* Halt or resume the chain. | ||
* @param toHalt True to halt, false to resume. | ||
*/ | ||
function halt(bool toHalt) public onlyOwner { | ||
V1Utils.halt(state, toHalt); | ||
} | ||
|
||
/** | ||
* Check whether a prover is whitelisted. | ||
* | ||
* @param prover The prover. | ||
* @return True if the prover is whitelisted, false otherwise. | ||
|
@@ -157,14 +177,12 @@ contract TaikoL1 is EssentialContract, IHeaderSync, V1Events { | |
return V1Proving.isProverWhitelisted(state, prover); | ||
} | ||
|
||
|
||
/** | ||
* Verify up to N blocks. | ||
* @param maxBlocks Max number of blocks to verify. | ||
/** | ||
* Check if the L1 is halted. | ||
* @return True if halted, false otherwise. | ||
*/ | ||
function verifyBlocks(uint256 maxBlocks) external nonReentrant { | ||
require(maxBlocks > 0, "L1:maxBlocks"); | ||
V1Finalizing.verifyBlocks(state, maxBlocks); | ||
function isHalted() public view returns (bool) { | ||
return V1Utils.isHalted(state); | ||
} | ||
|
||
function isCommitValid(bytes32 hash) public view returns (bool) { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,9 +8,7 @@ | |
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | ||
|
||
import "../LibData.sol"; | ||
import "./V1Utils.sol"; | ||
|
||
/// @author dantaik <[email protected]> | ||
library V1Finalizing { | ||
|
@@ -31,7 +29,19 @@ library V1Finalizing { | |
emit HeaderSynced(block.number, 0, _genesisBlockHash); | ||
} | ||
|
||
function verifyBlocks(LibData.State storage s, uint256 maxBlocks) public { | ||
function verifyBlocks( | ||
LibData.State storage s, | ||
uint256 maxBlocks, | ||
bool checkHalt | ||
) public { | ||
bool halted = V1Utils.isHalted(s); | ||
if (checkHalt) { | ||
require(!halted, "L1:halted"); | ||
} else if (halted) { | ||
// skip finalizing blocks | ||
return; | ||
} | ||
|
||
uint64 latestL2Height = s.latestVerifiedHeight; | ||
bytes32 latestL2Hash = s.l2Hashes[latestL2Height]; | ||
uint64 processed = 0; | ||
|
@@ -43,6 +53,15 @@ library V1Finalizing { | |
) { | ||
LibData.ForkChoice storage fc = s.forkChoices[i][latestL2Hash]; | ||
|
||
// TODO(daniel): use the average proof-time. | ||
if ( | ||
block.timestamp <= | ||
fc.provenAt + LibConstants.K_VERIFICATION_DELAY | ||
) { | ||
// This block is proven but still needs to wait for verificaiton. | ||
break; | ||
} | ||
|
||
if (fc.blockHash == LibConstants.TAIKO_BLOCK_DEADEND_HASH) { | ||
emit BlockVerified(i, 0); | ||
} else if (fc.blockHash != 0) { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,12 +8,10 @@ | |
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | ||
|
||
import "../../common/ConfigManager.sol"; | ||
import "../../libs/LibConstants.sol"; | ||
import "../../libs/LibTxDecoder.sol"; | ||
import "../LibData.sol"; | ||
import "./V1Utils.sol"; | ||
|
||
/// @author dantaik <[email protected]> | ||
library V1Proposing { | ||
|
@@ -25,6 +23,11 @@ library V1Proposing { | |
event BlockProposed(uint256 indexed id, LibData.BlockMetadata meta); | ||
|
||
function commitBlock(LibData.State storage s, bytes32 commitHash) public { | ||
// It's OK to allow committing block when the system is halt. | ||
// By not checking the halt status, this method will be cheaper. | ||
// | ||
// require(!V1Utils.isHalted(s), "L1:halt"); | ||
|
||
require(commitHash != 0, "L1:hash"); | ||
require(s.commits[commitHash] == 0, "L1:committed"); | ||
s.commits[commitHash] = block.number; | ||
|
@@ -39,6 +42,8 @@ library V1Proposing { | |
LibData.State storage s, | ||
bytes[] calldata inputs | ||
) public { | ||
require(!V1Utils.isHalted(s), "L1:halt"); | ||
|
||
require(inputs.length == 2, "L1:inputs:size"); | ||
LibData.BlockMetadata memory meta = abi.decode( | ||
inputs[0], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,6 @@ | |
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | ||
|
||
import "../../common/AddressResolver.sol"; | ||
import "../../common/ConfigManager.sol"; | ||
import "../../libs/LibAnchorSignature.sol"; | ||
|
@@ -22,7 +20,7 @@ import "../../libs/LibZKP.sol"; | |
import "../../thirdparty/LibBytesUtils.sol"; | ||
import "../../thirdparty/LibMerkleTrie.sol"; | ||
import "../../thirdparty/LibRLPWriter.sol"; | ||
import "../LibData.sol"; | ||
import "./V1Utils.sol"; | ||
|
||
/// @author dantaik <[email protected]> | ||
/// @author david <[email protected]> | ||
|
@@ -34,7 +32,7 @@ library V1Proving { | |
LibData.BlockMetadata meta; | ||
BlockHeader header; | ||
address prover; | ||
bytes[] proofs; | ||
bytes[] proofs; // The first K_ZKPROOFS_PER_BLOCK are ZKPs | ||
} | ||
|
||
event BlockProven( | ||
|
@@ -61,6 +59,8 @@ library V1Proving { | |
uint256 blockIndex, | ||
bytes[] calldata inputs | ||
) public onlyWhitelistedProver(s) { | ||
require(!V1Utils.isHalted(s), "L1:halt"); | ||
|
||
// Check and decode inputs | ||
require(inputs.length == 3, "L1:inputs:size"); | ||
Evidence memory evidence = abi.decode(inputs[0], (Evidence)); | ||
|
@@ -69,7 +69,10 @@ library V1Proving { | |
|
||
// Check evidence | ||
require(evidence.meta.id == blockIndex, "L1:id"); | ||
require(evidence.proofs.length == 3, "L1:proof:size"); | ||
require( | ||
evidence.proofs.length == 2 + LibConstants.K_ZKPROOFS_PER_BLOCK, | ||
"L1:proof:size" | ||
); | ||
|
||
// Check anchor tx is valid | ||
LibTxDecoder.Tx memory _tx = LibTxDecoder.decodeTx(anchorTx); | ||
|
@@ -105,7 +108,7 @@ library V1Proving { | |
LibMerkleTrie.verifyInclusionProof( | ||
LibRLPWriter.writeUint(0), | ||
anchorTx, | ||
evidence.proofs[1], | ||
evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], | ||
evidence.header.transactionsRoot | ||
), | ||
"L1:tx:proof" | ||
|
@@ -120,7 +123,7 @@ library V1Proving { | |
LibMerkleTrie.verifyInclusionProof( | ||
LibRLPWriter.writeUint(0), | ||
anchorReceipt, | ||
evidence.proofs[2], | ||
evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK + 1], | ||
evidence.header.receiptsRoot | ||
), | ||
"L1:receipt:proof" | ||
|
@@ -136,6 +139,8 @@ library V1Proving { | |
uint256 blockIndex, | ||
bytes[] calldata inputs | ||
) public onlyWhitelistedProver(s) { | ||
require(!V1Utils.isHalted(s), "L1:halt"); | ||
|
||
// Check and decode inputs | ||
require(inputs.length == 3, "L1:inputs:size"); | ||
Evidence memory evidence = abi.decode(inputs[0], (Evidence)); | ||
|
@@ -147,7 +152,10 @@ library V1Proving { | |
|
||
// Check evidence | ||
require(evidence.meta.id == blockIndex, "L1:id"); | ||
require(evidence.proofs.length == 2, "L1:proof:size"); | ||
require( | ||
evidence.proofs.length == 1 + LibConstants.K_ZKPROOFS_PER_BLOCK, | ||
"L1:proof:size" | ||
); | ||
|
||
// Check the 1st receipt is for an InvalidateBlock tx with | ||
// a BlockInvalidated event | ||
|
@@ -175,7 +183,7 @@ library V1Proving { | |
LibMerkleTrie.verifyInclusionProof( | ||
LibRLPWriter.writeUint(0), | ||
invalidateBlockReceipt, | ||
evidence.proofs[1], | ||
evidence.proofs[LibConstants.K_ZKPROOFS_PER_BLOCK], | ||
evidence.header.receiptsRoot | ||
), | ||
"L1:receipt:proof" | ||
|
@@ -229,15 +237,17 @@ library V1Proving { | |
|
||
bytes32 blockHash = evidence.header.hashBlockHeader(); | ||
|
||
LibZKP.verify( | ||
ConfigManager(resolver.resolve("config_manager")).getValue( | ||
"zk_vkey" | ||
), | ||
evidence.proofs[0], | ||
blockHash, | ||
evidence.prover, | ||
evidence.meta.txListHash | ||
); | ||
for (uint i = 0; i < LibConstants.K_ZKPROOFS_PER_BLOCK; i++) { | ||
LibZKP.verify( | ||
ConfigManager(resolver.resolve("config_manager")).getValue( | ||
string(abi.encodePacked("zk_vkey_", i)) | ||
), | ||
evidence.proofs[i], | ||
blockHash, | ||
evidence.prover, | ||
evidence.meta.txListHash | ||
); | ||
} | ||
|
||
_markBlockProven( | ||
s, | ||
|
@@ -263,9 +273,17 @@ library V1Proving { | |
fc.provenAt = uint64(block.timestamp); | ||
} else { | ||
require( | ||
fc.blockHash == blockHash && fc.proposedAt == target.timestamp, | ||
"L1:proof:conflict" | ||
fc.proposedAt == target.timestamp, | ||
"L1:proposedAt:conflict" | ||
); | ||
|
||
if (fc.blockHash != blockHash) { | ||
// We have a problem here: two proofs are both valid but claims | ||
// the new block has different hashes. | ||
V1Utils.halt(s, true); | ||
return; | ||
} | ||
|
||
require( | ||
fc.provers.length < | ||
LibConstants.TAIKO_MAX_PROOFS_PER_FORK_CHOICE, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: MIT | ||
// | ||
// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮ | ||
// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃ | ||
// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮ | ||
// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫ | ||
// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃ | ||
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯ | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; | ||
|
||
import "../../libs/LibMath.sol"; | ||
import "../LibData.sol"; | ||
|
||
/// @author dantaik <[email protected]> | ||
library V1Utils { | ||
uint64 public constant MASK_HALT = 1 << 0; | ||
|
||
event Halted(bool halted); | ||
|
||
function halt(LibData.State storage s, bool toHalt) public { | ||
require(isHalted(s) != toHalt, "L1:precondition"); | ||
setBit(s, MASK_HALT, toHalt); | ||
emit Halted(toHalt); | ||
} | ||
|
||
function isHalted(LibData.State storage s) public view returns (bool) { | ||
return isBitOne(s, MASK_HALT); | ||
} | ||
|
||
function setBit(LibData.State storage s, uint64 mask, bool one) private { | ||
s.statusBits = one ? s.statusBits | mask : s.statusBits & ~mask; | ||
} | ||
|
||
function isBitOne( | ||
LibData.State storage s, | ||
uint64 mask | ||
) private view returns (bool) { | ||
return s.statusBits & mask != 0; | ||
} | ||
} |
Oops, something went wrong.