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 <dan@taiko.xyz>
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
});
}
}
1 change: 0 additions & 1 deletion 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
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(
resolver.resolve("proof_verifier")
);

// 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(
resolver.resolve("proof_verifier")
);

// 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
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;
}
}
9 changes: 7 additions & 2 deletions packages/protocol/tasks/deploy_L1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ export async function deployContracts(hre: any) {
// AddressManager
const AddressManager = await utils.deployContract(hre, "AddressManager");
await utils.waitTx(hre, await AddressManager.init());

const ProofVerifier = await utils.deployContract(hre, "ProofVerifier");
await utils.waitTx(
hre,
await AddressManager.setAddress(`${chainId}.proof_verifier`, ProofVerifier.address)
);

await utils.waitTx(
hre,
await AddressManager.setAddress(`${chainId}.dao_vault`, daoVault)
Expand Down Expand Up @@ -195,7 +202,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 +212,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
9 changes: 2 additions & 7 deletions packages/protocol/test/L1/TaikoL1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ describe("TaikoL1", function () {
await ethers.getContractFactory("LibTxDecoder")
).deploy();

const libZKP = await (
await ethers.getContractFactory("LibZKP")
).deploy();

const libProposing = await (
await ethers.getContractFactory("LibProposing")
).deploy();
Expand All @@ -33,8 +29,7 @@ describe("TaikoL1", function () {
await ethers.getContractFactory("LibProving", {
libraries: {
LibReceiptDecoder: libReceiptDecoder.address,
LibTxDecoder: libTxDecoder.address,
LibZKP: libZKP.address,
LibTxDecoder: libTxDecoder.address
},
})
).deploy();
Expand All @@ -47,7 +42,7 @@ describe("TaikoL1", function () {
const feeBase = BigNumber.from(10).pow(18);
taikoL1 = await (
await ethers.getContractFactory(
"TestTaikoL1NoTokenomicsNoProofValidation",
"TestTaikoL1",
{
libraries: {
LibVerifying: libVerifying.address,
Expand Down