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

feat(gwyneth): add first simple tests for proposer/prover #17

Merged
merged 8 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions packages/protocol/contracts/L1/BasedOperator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

/// @dev Struct representing transition to be proven.
struct ProofBatch {
TaikoData.BlockMetadata _block;
TaikoData.BlockMetadata blockMetadata;
TaikoData.Transition transition;
ProofData[] proofs;
address prover;
Expand All @@ -51,12 +51,17 @@
uint256 public constant MAX_BLOCKS_TO_VERIFY = 5;
uint256 public constant PROVING_WINDOW = 1 hours;

TaikoL1 public taiko;
VerifierRegistry public verifierRegistry;
address public treasury;
address public treasury; // (?)

mapping(uint256 => Block) public blocks;

function init(address _addressManager) external initializer {
if (_addressManager == address(0)) {
revert L1_INVALID_ADDRESS();
}
__Essential_init(_addressManager);
}

/// @dev Proposes a Taiko L2 block.
function proposeBlock(
bytes calldata params,
Expand All @@ -71,7 +76,7 @@
{
require(msg.value == PROVER_BOND, "Prover bond not expected");

_block = taiko.proposeBlock(params, txList);
_block = TaikoL1(resolve("taiko", false)).proposeBlock(params, txList);
Brechtpd marked this conversation as resolved.
Show resolved Hide resolved

// Check if we have whitelisted proposers
if (!_isProposerPermitted(_block)) {
Expand All @@ -91,25 +96,30 @@
ProofBatch memory proofBatch = abi.decode(data, (ProofBatch));

// Check who can prove the block
TaikoData.Block memory taikoBlock = taiko.getBlock(proofBatch._block.l2BlockNumber);
TaikoData.Block memory taikoBlock =
TaikoL1(resolve("taiko", false)).getBlock(proofBatch.blockMetadata.l2BlockNumber);
if (block.timestamp < taikoBlock.timestamp + PROVING_WINDOW) {
require(
proofBatch.prover == blocks[proofBatch._block.l2BlockNumber].assignedProver,
proofBatch.prover == blocks[proofBatch.blockMetadata.l2BlockNumber].assignedProver,
"assigned prover not the prover"
);
}

VerifierRegistry verifierRegistry = VerifierRegistry(resolve("verifier_registry", false));
TaikoL1 taiko = TaikoL1(resolve("taiko", false));
// Verify the proofs
uint160 prevVerifier = uint160(0);
for (uint256 i = 0; i < proofBatch.proofs.length; i++) {
IVerifier verifier = proofBatch.proofs[i].verifier;
// Make sure each verifier is unique
require(prevVerifier >= uint160(address(verifier)), "duplicated verifier");
if (prevVerifier >= uint160(address(verifier))) {
revert L1_INVALID_OR_DUPLICATE_VERIFIER();
}
// Make sure it's a valid verifier
require(verifierRegistry.isVerifier(address(verifier)), "invalid verifier");
// Verify the proof
verifier.verifyProof(
proofBatch._block,
proofBatch.blockMetadata,
proofBatch.transition,
proofBatch.prover,
proofBatch.proofs[i].proof
Expand All @@ -124,16 +134,23 @@
// Only allow an already proven block to be overwritten when the verifiers used are now
// invalid
// Get the currently stored transition
TaikoData.TransitionState memory storedTransition =
taiko.getTransition(proofBatch._block.l2BlockNumber, proofBatch.transition.parentHash);
if (storedTransition.blockHash != proofBatch.transition.blockHash) {
// TODO(Brecht): Check that one of the verifiers is now poissoned
} else {
TaikoData.TransitionState memory storedTransition = taiko.getTransition(
proofBatch.blockMetadata.l2BlockNumber, proofBatch.transition.parentBlockHash
);

// Somehow we need to check if this is proven already and IF YES and transition is trying to
// prove the same, then revert with "block already proven".
if (
storedTransition.isProven == true
&& storedTransition.blockHash == proofBatch.transition.blockHash
) {
revert("block already proven");
} else {
// TODO(Brecht): Check that one of the verifiers is now poissoned
}

// Prove the block
taiko.proveBlock(proofBatch._block, proofBatch.transition, proofBatch.prover);
taiko.proveBlock(proofBatch.blockMetadata, proofBatch.transition, proofBatch.prover);

// Verify some blocks
_verifyBlocks(MAX_BLOCKS_TO_VERIFY);
Expand All @@ -144,6 +161,7 @@
}

function _verifyBlocks(uint256 maxBlocksToVerify) internal {
TaikoL1 taiko = TaikoL1(resolve("taiko", false));
uint256 lastVerifiedBlockIdBefore = taiko.getLastVerifiedBlockId();
// Verify the blocks
taiko.verifyBlocks(maxBlocksToVerify);
Expand All @@ -157,7 +175,7 @@
) {
Block storage blk = blocks[blockId];

// TODO(Brecht): Verify that all the verifers used to prove the block are still valid

Check failure on line 178 in packages/protocol/contracts/L1/BasedOperator.sol

View workflow job for this annotation

GitHub Actions / codespell

verifers ==> verifiers

// Find out who the prover is
TaikoData.Block memory previousBlock = taiko.getBlock(uint64(blockId) - 1);
Expand All @@ -173,7 +191,7 @@
}
}

// Additinal proposer rules

Check failure on line 194 in packages/protocol/contracts/L1/BasedOperator.sol

View workflow job for this annotation

GitHub Actions / codespell

Additinal ==> Additional
function _isProposerPermitted(TaikoData.BlockMetadata memory _block) private returns (bool) {
if (_block.l2BlockNumber == 1) {
// Only proposer_one can propose the first block after genesis
Expand Down
6 changes: 4 additions & 2 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ library TaikoData {
/// @dev Struct containing data only required for proving a block
struct BlockMetadata {
bytes32 blockHash;
bytes32 parentBlockHash;
bytes32 parentMetaHash;
bytes32 l1Hash;
uint256 difficulty;
Expand All @@ -40,16 +41,17 @@ library TaikoData {

/// @dev Struct representing transition to be proven.
struct Transition {
bytes32 parentHash;
bytes32 parentBlockHash;
bytes32 blockHash;
}

/// @dev Struct representing state transition data.
struct TransitionState {
bytes32 blockHash;
bytes32 blockHash; //Might be removed..
uint64 timestamp;
address prover;
uint64 verifiableAfter;
bool isProven;
}

/// @dev Struct containing data required for verifying a block.
Expand Down
2 changes: 2 additions & 0 deletions packages/protocol/contracts/L1/TaikoErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
pragma solidity ^0.8.20;

/// @title TaikoErrors
/// @notice This abstract contract provides custom error declartions used in

Check failure on line 10 in packages/protocol/contracts/L1/TaikoErrors.sol

View workflow job for this annotation

GitHub Actions / codespell

declartions ==> declarations
/// the Taiko protocol. Each error corresponds to specific situations where
/// exceptions might be thrown.
abstract contract TaikoErrors {
Expand All @@ -20,13 +20,15 @@
error L1_BLOB_NOT_FOUND();
error L1_BLOB_NOT_REUSEABLE();
error L1_BLOCK_MISMATCH();
error L1_INCORRECT_BLOCK();
error L1_INSUFFICIENT_TOKEN();
error L1_INVALID_ADDRESS();
error L1_INVALID_AMOUNT();
error L1_INVALID_BLOCK_ID();
error L1_INVALID_CONFIG();
error L1_INVALID_ETH_DEPOSIT();
error L1_INVALID_L1_STATE_BLOCK();
error L1_INVALID_OR_DUPLICATE_VERIFIER();
error L1_INVALID_PARAM();
error L1_INVALID_PAUSE_STATUS();
error L1_INVALID_PROOF();
Expand Down
39 changes: 24 additions & 15 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors {
// Verify L1 data
// TODO(Brecht): needs to be more configurable for preconfirmations
require(_block.l1Hash == blockhash(_block.l1StateBlockNumber), "INVALID_L1_BLOCKHASH");
require(_block.blockHash != 0x0, "INVALID_L2_BLOCKHASH");
require(_block.difficulty == block.prevrandao, "INVALID_DIFFICULTY");
require(_block.timestamp == uint64(block.timestamp), "INVALID_TIMESTAMP");
// Verify misc data
require(_block.gasLimit == config.blockMaxGasLimit, "INVALID_GAS_LIMIT");

require(_block.blobUsed == (txList.length == 0), "INVALID_BLOB_USED");
// Verify DA data
if (_block.blobUsed) {
Expand All @@ -93,7 +94,8 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors {
);

TaikoData.Block storage parentBlock = state.blocks[(state.numBlocks - 1)];
require(_block.parentMetaHash == parentBlock.metaHash, "invalid parentMetaHash");

require(_block.parentMetaHash == parentBlock.metaHash, "invalid parentHash");

// Verify the passed in L1 state block number.
// We only allow the L1 block to be 4 epochs old.
Expand Down Expand Up @@ -130,11 +132,10 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors {
// Store the block
state.blocks[state.numBlocks] = blk;

// Store the passed in block hash as in
state.transitions[blk.blockId][_block.parentMetaHash].blockHash = _block.blockHash;
// For now it does not matter - we are not going to prove anyways
state.transitions[blk.blockId][_block.parentMetaHash].verifiableAfter =
uint64(block.timestamp) + 365 days;
// Store the passed in block hash as is
state.transitions[blk.blockId][_block.parentBlockHash].blockHash = _block.blockHash;
// Big enough number so that we are sure we don't hit that deadline in the future.
state.transitions[blk.blockId][_block.parentBlockHash].verifiableAfter = type(uint64).max;

// Increment the counter (cursor) by 1.
state.numBlocks++;
Expand Down Expand Up @@ -167,14 +168,17 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors {
TaikoData.Block storage blk = state.blocks[_block.l2BlockNumber];

// Make sure the correct block was proven
require(blk.metaHash != keccak256(abi.encode(_block)), "incorrect block");
if (blk.metaHash != keccak256(abi.encode(_block))) {
revert L1_INCORRECT_BLOCK();
}

// Store the transition
TaikoData.TransitionState storage storedTransition =
state.transitions[_block.l2BlockNumber][transition.parentHash];
state.transitions[_block.l2BlockNumber][transition.parentBlockHash];
storedTransition.blockHash = transition.blockHash;
storedTransition.prover = prover;
storedTransition.verifiableAfter = uint32(block.timestamp + SECURITY_DELAY_AFTER_PROVEN);
storedTransition.isProven = true;

emit TransitionProved({ blockId: _block.l2BlockNumber, tran: transition, prover: prover });
}
Expand All @@ -190,21 +194,22 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors {
// Get the last verified blockhash
TaikoData.Block storage blk = state.blocks[state.lastVerifiedBlockId];
bytes32 blockHash = blk.blockHash;
// Go to the first unverified block
uint256 blockId = uint256(state.lastVerifiedBlockId) + 1;
uint256 numBlocksVerified;

while (blockId < state.numBlocks && numBlocksVerified < maxBlocksToVerify) {
blk = state.blocks[blockId];

// Check if the parent block hash matches the actual block hash of the parent
// Check if the timestamp is older than required
if (
state.transitions[blockId][blockHash].blockHash == bytes32(0)
|| block.timestamp < state.transitions[blockId][blockHash].verifiableAfter
block
// Genesis is already verified with initialization so if we do not allow to set
// blockHash = bytes32(0), then we can remove the bytes32(0) check.
/*state.transitions[blockId][blockHash].blockHash == bytes32(0)
|| */
.timestamp < state.transitions[blockId][blockHash].verifiableAfter
) {
break;
}

// Copy the blockhash to the block
blk.blockHash = state.transitions[blockId][blockHash].blockHash;
// Update latest block hash
Expand Down Expand Up @@ -256,6 +261,10 @@ contract TaikoL1 is EssentialContract, TaikoEvents, TaikoErrors {
return uint256(state.lastVerifiedBlockId);
}

function getNumOfBlocks() public view returns (uint256) {
return uint256(state.numBlocks);
}

/// @notice Gets the configuration of the TaikoL1 contract.
/// @return Config struct containing configuration parameters.
function getConfig() public view virtual returns (TaikoData.Config memory) {
Expand Down
10 changes: 8 additions & 2 deletions packages/protocol/contracts/L1/VerifierBattleRoyale.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ contract VerifierBattleRoyale is EssentialContract {

TaikoData.Transition memory transitionA = proofBatch.proofs[0].transition;
TaikoData.Transition memory transitionB = proofBatch.proofs[1].transition;
require(transitionA.parentHash == transitionB.parentHash, "parentHash not the same");
require(
transitionA.parentBlockHash == transitionB.parentBlockHash,
"parentHash not the same"
);
require(transitionA.blockHash != transitionB.blockHash, "blockhash the same");
} else if (proofBatch.proofs.length == 3) {
/* Multiple verifiers in a consensus show that another verifier is faulty */
Expand All @@ -105,7 +108,10 @@ contract VerifierBattleRoyale is EssentialContract {
for (uint256 i = 0; i < proofBatch.proofs.length - 1; i++) {
TaikoData.Transition memory transitionA = proofBatch.proofs[i].transition;
TaikoData.Transition memory transitionB = proofBatch.proofs[i + 1].transition;
require(transitionA.parentHash == transitionB.parentHash, "parentHash not the same");
require(
transitionA.parentBlockHash == transitionB.parentBlockHash,
"parentHash not the same"
);
if (i < proofBatch.proofs.length - 2) {
require(transitionA.blockHash == transitionB.blockHash, "blockhash the same");
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/L1/actors/PBSActor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract PBSActor {
bytes calldata params,
bytes calldata txList,
bytes memory proverPaymentData,
bytes32 parentMetaHash,
bytes32 parentHash,
uint256 tip
)
external
Expand All @@ -35,7 +35,7 @@ contract PBSActor {
operator.proposeBlock{ value: msg.value - tip }(params, txList, proverPaymentData);

// Check if parent block has the right meta hash
require(keccak256(abi.encode(_block)) == parentMetaHash, "unexpected parent");
require(keccak256(abi.encode(_block)) == parentHash, "unexpected parent");

// Do conditional payment
address(block.coinbase).sendEther(tip);
Expand Down
12 changes: 8 additions & 4 deletions packages/protocol/deployments/deploy_l1.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"address_manager": "0x19A827174F66B3c66ad7063951D7b4F94f996e77",
"bridge": "0x1a76F7BA873f90805B49A51cBA617E699Cf142B0",
"erc721_vault": "0x9D46a79Ad6e0dcb36AbAb982e608e186E6826b7C",
"signal_service": "0x798684a55404079b77E19A86325e0c11eA5BB09D"
"address_manager": "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9",
"based_operator": "0xa0Cb889707d426A7A386870A03bc70d1b0697598",
"sgx1": "0xD6BbDE9174b1CdAa358d2Cf4D57D1a9F7178FBfF",
"sgx2": "0x15cF58144EF33af1e14b5208015d11F9143E27b9",
"sgx3": "0x212224D2F2d262cd093eE13240ca4873fcCBbA3C",
"taiko": "0x2e234DAe75C793f67A35089C9d99245E1C58470b",
"taiko_token": "0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7",
"verifier_registry": "0xA4AD4f68d0b91CFD19687c881e50f3A00242828c"
}
Loading
Loading