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

refactor(protocol): extract IProofVerifier interface #6800

Merged
merged 15 commits into from
Jan 3, 2023
64 changes: 64 additions & 0 deletions packages/protocol/contracts/L1/ProofVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
//
// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮
// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃
// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮
// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫
// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

import "../thirdparty/LibMerkleTrie.sol";
import "../libs/LibZKP.sol";

/// @author dantaik <[email protected]>
interface IProofVerifier {
function verifyZKP(
bytes memory verificationKey,
bytes calldata zkproof,
bytes32 blockHash,
address prover,
bytes32 txListHash
) external pure returns (bool verified);

function verifyMKP(
bytes memory key,
bytes memory value,
bytes memory proof,
bytes32 root
) external pure returns (bool verified);
}

contract ProofVerifier is IProofVerifier {
function verifyZKP(
bytes memory verificationKey,
bytes calldata zkproof,
bytes32 blockHash,
address prover,
bytes32 txListHash
) external pure returns (bool) {
return
LibZKP.verify({
verificationKey: verificationKey,
zkproof: zkproof,
blockHash: blockHash,
prover: prover,
txListHash: txListHash
});
}

function verifyMKP(
bytes memory key,
bytes memory value,
bytes memory proof,
bytes32 root
) external pure returns (bool) {
return
LibMerkleTrie.verifyInclusionProof({
_key: key,
_value: value,
_proof: proof,
_root: root
});
}
}
4 changes: 2 additions & 2 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ library TaikoData {
uint64 boostrapDiscountHalvingPeriod;
uint64 initialUncleDelay;
bool enableTokenomics;
bool skipProofValidation;
}

struct BlockMetadata {
Expand Down Expand Up @@ -83,6 +82,7 @@ library TaikoData {
mapping(uint256 => mapping(bytes32 => ForkChoice)) forkChoices;
// proposer => commitSlot => hash(commitHash, commitHeight)
mapping(address => mapping(uint256 => bytes32)) commits;
mapping(string => bytes32) lookups;
// Never or rarely changed
uint64 genesisHeight;
uint64 genesisTimestamp;
Expand All @@ -103,6 +103,6 @@ library TaikoData {
uint64 avgProofTime;
uint64 __reservedC1;
// Reserved
uint256[42] __gap;
uint256[41] __gap;
}
}
2 changes: 2 additions & 0 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents {

function init(
address _addressManager,
address _proofVerifier,
bytes32 _genesisBlockHash,
uint256 _feeBase
) external initializer {
EssentialContract._init(_addressManager);
LibVerifying.init({
state: state,
proofVerifier: _proofVerifier,
genesisBlockHash: _genesisBlockHash,
feeBase: _feeBase
});
Expand Down
107 changes: 58 additions & 49 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

import {IProofVerifier} from "../ProofVerifier.sol";
import "../../common/AddressResolver.sol";
import "../../common/ConfigManager.sol";
import "../../libs/LibAnchorSignature.sol";
import "../../libs/LibBlockHeader.sol";
import "../../libs/LibReceiptDecoder.sol";
import "../../libs/LibTxDecoder.sol";
import "../../libs/LibTxUtils.sol";
import "../../libs/LibZKP.sol";
import "../../thirdparty/LibBytesUtils.sol";
import "../../thirdparty/LibMerkleTrie.sol";
import "../../thirdparty/LibRLPWriter.sol";
import "./LibUtils.sol";

Expand Down Expand Up @@ -107,40 +106,43 @@ library LibProving {
);
}

if (!config.skipProofValidation) {
// Check anchor tx is the 1st tx in the block
require(
LibMerkleTrie.verifyInclusionProof({
_key: LibRLPWriter.writeUint(0),
_value: anchorTx,
_proof: evidence.proofs[zkProofsPerBlock],
_root: evidence.header.transactionsRoot
}),
"L1:tx:proof"
);
IProofVerifier proofVerifier = IProofVerifier(
address(uint160(uint256(state.lookups["proofVerifier"])))
);

// Check anchor tx does not throw
// Check anchor tx is the 1st tx in the block
require(
proofVerifier.verifyMKP({
key: LibRLPWriter.writeUint(0),
value: anchorTx,
proof: evidence.proofs[zkProofsPerBlock],
root: evidence.header.transactionsRoot
}),
"L1:tx:proof"
);

LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder
.decodeReceipt(anchorReceipt);
// Check anchor tx does not throw

require(receipt.status == 1, "L1:receipt:status");
require(
LibMerkleTrie.verifyInclusionProof({
_key: LibRLPWriter.writeUint(0),
_value: anchorReceipt,
_proof: evidence.proofs[zkProofsPerBlock + 1],
_root: evidence.header.receiptsRoot
}),
"L1:receipt:proof"
);
}
LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder
.decodeReceipt(anchorReceipt);

require(receipt.status == 1, "L1:receipt:status");
require(
proofVerifier.verifyMKP({
key: LibRLPWriter.writeUint(0),
value: anchorReceipt,
proof: evidence.proofs[zkProofsPerBlock + 1],
root: evidence.header.receiptsRoot
}),
"L1:receipt:proof"
);

// ZK-prove block and mark block proven to be valid.
_proveBlock({
state: state,
config: config,
resolver: resolver,
proofVerifier: proofVerifier,
evidence: evidence,
target: evidence.meta,
blockHashOverride: 0
Expand Down Expand Up @@ -172,14 +174,29 @@ library LibProving {
"L1:proof:size"
);

if (!config.skipProofValidation) {
// Check the 1st receipt is for an InvalidateBlock tx with
// a BlockInvalidated event
LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder
.decodeReceipt(invalidateBlockReceipt);
require(receipt.status == 1, "L1:receipt:status");
require(receipt.logs.length == 1, "L1:receipt:logsize");
IProofVerifier proofVerifier = IProofVerifier(
address(uint160(uint256(state.lookups["proofVerifier"])))
);

// Check the event is the first one in the throw-away block
require(
proofVerifier.verifyMKP({
key: LibRLPWriter.writeUint(0),
value: invalidateBlockReceipt,
proof: evidence.proofs[config.zkProofsPerBlock],
root: evidence.header.receiptsRoot
}),
"L1:receipt:proof"
);

// Check the 1st receipt is for an InvalidateBlock tx with
// a BlockInvalidated event
LibReceiptDecoder.Receipt memory receipt = LibReceiptDecoder
.decodeReceipt(invalidateBlockReceipt);
require(receipt.status == 1, "L1:receipt:status");
require(receipt.logs.length == 1, "L1:receipt:logsize");

{
LibReceiptDecoder.Log memory log = receipt.logs[0];
require(
log.contractAddress ==
Expand All @@ -193,24 +210,14 @@ library LibProving {
log.topics[1] == target.txListHash,
"L1:receipt:topics"
);

// Check the event is the first one in the throw-away block
require(
LibMerkleTrie.verifyInclusionProof({
_key: LibRLPWriter.writeUint(0),
_value: invalidateBlockReceipt,
_proof: evidence.proofs[config.zkProofsPerBlock],
_root: evidence.header.receiptsRoot
}),
"L1:receipt:proof"
);
}

// ZK-prove block and mark block proven as invalid.
_proveBlock({
state: state,
config: config,
resolver: resolver,
proofVerifier: proofVerifier,
evidence: evidence,
target: target,
blockHashOverride: LibUtils.BLOCK_DEADEND_HASH
Expand All @@ -221,6 +228,7 @@ library LibProving {
TaikoData.State storage state,
TaikoData.Config memory config,
AddressResolver resolver,
IProofVerifier proofVerifier,
Evidence memory evidence,
TaikoData.BlockMetadata memory target,
bytes32 blockHashOverride
Expand All @@ -238,17 +246,18 @@ library LibProving {
bytes32 blockHash = evidence.header.hashBlockHeader();

for (uint256 i = 0; i < config.zkProofsPerBlock; i++) {
if (!config.skipProofValidation) {
LibZKP.verify({
require(
proofVerifier.verifyZKP({
verificationKey: ConfigManager(
resolver.resolve("config_manager")
).getValue(string(abi.encodePacked("zk_vkey_", i))),
zkproof: evidence.proofs[i],
blockHash: blockHash,
prover: evidence.prover,
txListHash: evidence.meta.txListHash
});
}
}),
"L1:zkp"
);
}

_markBlockProven({
Expand Down
6 changes: 6 additions & 0 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ library LibVerifying {

function init(
TaikoData.State storage state,
address proofVerifier,
bytes32 genesisBlockHash,
uint256 feeBase
) public {
Expand All @@ -39,6 +40,11 @@ library LibVerifying {
state.lastProposedAt = uint64(block.timestamp);
state.l2Hashes[0] = genesisBlockHash;

require(proofVerifier != address(0), "L1:proofVerifier");
state.lookups["proofVerifier"] = bytes32(
uint256(uint160(proofVerifier))
);

emit BlockVerified(0, genesisBlockHash);
emit HeaderSynced(block.number, 0, genesisBlockHash);
}
Expand Down
3 changes: 1 addition & 2 deletions packages/protocol/contracts/libs/LibSharedConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ library LibSharedConfig {
proofTimeCap: 60 minutes,
boostrapDiscountHalvingPeriod: 180 days,
initialUncleDelay: 60 minutes,
enableTokenomics: false,
skipProofValidation: false
enableTokenomics: false
});
}
}
2 changes: 1 addition & 1 deletion packages/protocol/contracts/libs/LibZKP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ library LibZKP {
bytes32 blockHash,
address prover,
bytes32 txListHash
) public pure {
) internal pure returns (bool verified) {
// TODO
}
}
23 changes: 21 additions & 2 deletions packages/protocol/contracts/test/L1/TestTaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

import {IProofVerifier} from "../../L1/ProofVerifier.sol";
import "../../L1/TaikoL1.sol";

contract TestTaikoL1NoTokenomicsNoProofValidation is TaikoL1 {
contract TestTaikoL1 is TaikoL1, IProofVerifier {
function getConfig()
public
pure
Expand Down Expand Up @@ -49,6 +50,24 @@ contract TestTaikoL1NoTokenomicsNoProofValidation is TaikoL1 {
config.boostrapDiscountHalvingPeriod = 180 days;
config.initialUncleDelay = 1 minutes;
config.enableTokenomics = false;
config.skipProofValidation = true;
}

function verifyZKP(
bytes memory /*verificationKey*/,
bytes calldata /*zkproof*/,
bytes32 /*blockHash*/,
address /*prover*/,
bytes32 /*txListHash*/
) public pure override returns (bool) {
return true;
}

function verifyMKP(
bytes memory /*key*/,
bytes memory /*value*/,
bytes memory /*proof*/,
bytes32 /*root*/
) public pure override returns (bool) {
return true;
}
}
5 changes: 2 additions & 3 deletions packages/protocol/tasks/deploy_L1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export async function deployContracts(hre: any) {
log.debug();

// AddressManager
const ProofVerifier = await utils.deployContract(hre, "ProofVerifier");
const AddressManager = await utils.deployContract(hre, "AddressManager");
await utils.waitTx(hre, await AddressManager.init());
await utils.waitTx(
Expand Down Expand Up @@ -128,7 +129,7 @@ export async function deployContracts(hre: any) {

await utils.waitTx(
hre,
await TaikoL1.init(AddressManager.address, l2GenesisBlockHash, feeBase)
await TaikoL1.init(ProofVerifier.address, AddressManager.address, l2GenesisBlockHash, feeBase)
);

// Used by LibBridgeRead
Expand Down Expand Up @@ -195,7 +196,6 @@ export async function deployContracts(hre: any) {
}

async function deployBaseLibs(hre: any) {
const libZKP = await utils.deployContract(hre, "LibZKP");
const libReceiptDecoder = await utils.deployContract(
hre,
"LibReceiptDecoder"
Expand All @@ -206,7 +206,6 @@ async function deployBaseLibs(hre: any) {
const libProposing = await utils.deployContract(hre, "LibProposing", {});

const libProving = await utils.deployContract(hre, "LibProving", {
LibZKP: libZKP.address,
LibReceiptDecoder: libReceiptDecoder.address,
LibTxDecoder: libTxDecoder.address,
});
Expand Down
Loading