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 preconf from taiko-mono helder branch #15

Merged
merged 21 commits into from
Jul 15, 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
3 changes: 3 additions & 0 deletions packages/protocol/.env_sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
L2_GENESIS_HASH=0xdf90a9c4daa571aa308e967c9a6b4bf21ba8842d95d73d28be112b6fe0618e8c
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
MAINNET_CONTRACT_OWNER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 #It shall belong to the PK above. I got this example ADDR-PK from local anvil.
131 changes: 81 additions & 50 deletions packages/protocol/contracts/L1/BasedOperator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../common/AddressResolver.sol";
import "../common/EssentialContract.sol";
import "../libs/LibAddress.sol";
import "./preconfs/ISequencerRegistry.sol";
import "./TaikoL1.sol";
import "./TaikoData.sol";
import "./TaikoErrors.sol";
import "./VerifierRegistry.sol";
import "./verifiers/IVerifier.sol";

/// @title BasedOperator
/// @notice A based operator for Taiko.
contract BasedOperator is EssentialContract {
contract BasedOperator is EssentialContract, TaikoErrors {
using LibAddress for address;

struct Block {
Expand All @@ -38,22 +40,27 @@ contract BasedOperator is EssentialContract {

/// @dev Struct representing transition to be proven.
struct ProofBatch {
TaikoData.BlockMetadata _block;
TaikoData.BlockMetadata blockMetadata;
TaikoData.Transition transition;
ProofData[] proofs;
address prover;
}

uint public constant PROVER_BOND = 1 ether / 10;
uint public constant MAX_GAS_PROVER_PAYMENT = 50_000;
uint public constant MAX_BLOCKS_TO_VERIFY = 5;
uint public constant PROVING_WINDOW = 1 hours;
uint256 public constant PROVER_BOND = 1 ether / 10;
uint256 public constant MAX_GAS_PROVER_PAYMENT = 50_000;
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(uint => Block) public blocks;
mapping(uint256 => Block) public blocks;

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

/// @dev Proposes a Taiko L2 block.
function proposeBlock(
Expand All @@ -69,81 +76,103 @@ contract BasedOperator is EssentialContract {
{
require(msg.value == PROVER_BOND, "Prover bond not expected");

_block = taiko.proposeBlock(params, txList);
_block = TaikoL1(resolve("taiko", false)).proposeBlock(params, txList);

// Check if we have whitelisted proposers
require(_isProposerPermitted(_block), "proposer not allowed");
if (!_isProposerPermitted(_block)) {
revert L1_INVALID_PROPOSER();
}

// Store who paid for proving the block
blocks[_block.id] = Block({
assignedProver: prover,
bond: uint96(PROVER_BOND)
});
blocks[_block.l2BlockNumber] = Block({ assignedProver: prover, bond: uint96(PROVER_BOND) });

// Verify some blocks
_verifyBlocks(MAX_BLOCKS_TO_VERIFY);
}

/// @dev Proposes a Taiko L2 block.
function proveBlock(bytes calldata data)
external
nonReentrant
whenNotPaused
{
function proveBlock(bytes calldata data) external nonReentrant whenNotPaused {
// Decode the block data
ProofBatch memory proofBatch = abi.decode(data, (ProofBatch));

// Check who can prove the block
TaikoData.Block memory taikoBlock = taiko.getBlock(proofBatch._block.id);
if (block.timestamp < taikoBlock.proposedAt + PROVING_WINDOW) {
require(proofBatch.prover == blocks[proofBatch._block.id].assignedProver, "assigned prover not the prover");
TaikoData.Block memory taikoBlock =
TaikoL1(resolve("taiko", false)).getBlock(proofBatch.blockMetadata.l2BlockNumber);
if (block.timestamp < taikoBlock.timestamp + PROVING_WINDOW) {
require(
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 (uint i = 0; i < proofBatch.proofs.length; i++) {
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.transition, proofBatch.prover, proofBatch.proofs[i].proof);
verifier.verifyProof(
proofBatch.transition,
keccak256(abi.encode(proofBatch.blockMetadata)),
proofBatch.prover,
proofBatch.proofs[i].proof
);
prevVerifier = uint160(address(verifier));
}

// Make sure the supplied proofs are sufficient.
// Can use some custom logic here. but let's keep it simple
require(proofBatch.proofs.length >= 3, "insufficient number of proofs");

// Only allow an already proven block to be overwritten when the verifiers used are now invalid
// 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.id, 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);
}

function verifyBlocks(uint maxBlocksToVerify) external nonReentrant whenNotPaused {
function verifyBlocks(uint256 maxBlocksToVerify) external nonReentrant whenNotPaused {
_verifyBlocks(maxBlocksToVerify);
}

function _verifyBlocks(uint maxBlocksToVerify) internal {
uint lastVerifiedBlockIdBefore = taiko.getLastVerifiedBlockId();
function _verifyBlocks(uint256 maxBlocksToVerify) internal {
TaikoL1 taiko = TaikoL1(resolve("taiko", false));
uint256 lastVerifiedBlockIdBefore = taiko.getLastVerifiedBlockId();
// Verify the blocks
taiko.verifyBlocks(maxBlocksToVerify);
uint lastVerifiedBlockIdAfter = taiko.getLastVerifiedBlockId();
uint256 lastVerifiedBlockIdAfter = taiko.getLastVerifiedBlockId();

// So some additional checks on top of the standard checks done in the rollup contract
for (uint blockId = lastVerifiedBlockIdBefore + 1; blockId <= lastVerifiedBlockIdAfter; blockId++) {
for (
uint256 blockId = lastVerifiedBlockIdBefore + 1;
blockId <= lastVerifiedBlockIdAfter;
blockId++
) {
Block storage blk = blocks[blockId];

// TODO(Brecht): Verify that all the verifers used to prove the block are still valid
Expand All @@ -156,29 +185,31 @@ contract BasedOperator is EssentialContract {
uint256 bondToReturn = blk.bond;
if (prover != blk.assignedProver) {
bondToReturn >>= 1;
treasury.sendEther(bondToReturn, MAX_GAS_PROVER_PAYMENT);
treasury.sendEtherAndVerify(bondToReturn, MAX_GAS_PROVER_PAYMENT);
}
prover.sendEther(bondToReturn, MAX_GAS_PROVER_PAYMENT);
prover.sendEtherAndVerify(bondToReturn, MAX_GAS_PROVER_PAYMENT);
}
}

// Additinal proposer rules
function _isProposerPermitted(
TaikoData.BlockMetadata memory _block
)
private
view
returns (bool)
{
if (_block.id == 1) {
function _isProposerPermitted(TaikoData.BlockMetadata memory _block) private returns (bool) {
if (_block.l2BlockNumber == 1) {
// Only proposer_one can propose the first block after genesis
address proposerOne = resolve("proposer_one", true);
if (proposerOne != address(0) && msg.sender != proposerOne) {
return false;
}
}

address proposer = resolve("proposer", true);
return proposer == address(0) || msg.sender == proposer;
// If there's a sequencer registry, check if the block can be proposed by the current
// proposer
ISequencerRegistry sequencerRegistry =
ISequencerRegistry(resolve("sequencer_registry", true));
if (sequencerRegistry != ISequencerRegistry(address(0))) {
if (!sequencerRegistry.isEligibleSigner(msg.sender)) {
return false;
}
}
return true;
}
}
53 changes: 53 additions & 0 deletions packages/protocol/contracts/L1/ITaikoL1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./TaikoData.sol";

/// @title ITaikoL1
/// @custom:security-contact security@taiko.xyz
interface ITaikoL1 {
/// @notice Proposes a Taiko L2 block.
/// @param _params Block parameters, currently an encoded BlockParams object.
/// @param _txList txList data if calldata is used for DA.
/// @return meta_ The metadata of the proposed L2 block.
function proposeBlock(
bytes calldata _params,
bytes calldata _txList
)
external
payable
returns (TaikoData.BlockMetadata memory meta_);

/// @notice Proves or contests a block transition.
/// @param _blockId The index of the block to prove. This is also used to
/// select the right implementation version.
/// @param _input An abi-encoded (TaikoData.BlockMetadata, TaikoData.Transition,
/// TaikoData.TierProof) tuple.
function proveBlock(uint64 _blockId, bytes calldata _input) external;

/// @notice Verifies up to a certain number of blocks.
/// @param _maxBlocksToVerify Max number of blocks to verify.
function verifyBlocks(uint64 _maxBlocksToVerify) external;

/// @notice Pause block proving.
/// @param _pause True if paused.
function pauseProving(bool _pause) external;

/// @notice Deposits Taiko token to be used as bonds.
/// @param _amount The amount of Taiko token to deposit.
function depositBond(uint256 _amount) external;

/// @notice Withdraws Taiko token.
/// @param _amount The amount of Taiko token to withdraw.
function withdrawBond(uint256 _amount) external;

// /// @notice Gets the prover that actually proved a verified block.
// /// @param _blockId The index of the block.
// /// @return The prover's address. If the block is not verified yet, address(0) will be
// returned.
// function getVerifiedBlockProver(uint64 _blockId) external view returns (address);

/// @notice Gets the configuration of the TaikoL1 contract.
/// @return Config struct containing configuration parameters.
function getConfig() external pure returns (TaikoData.Config memory);
}
23 changes: 11 additions & 12 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,56 @@ library TaikoData {
/// @dev Struct containing data only required for proving a block
struct BlockMetadata {
bytes32 blockHash;
bytes32 parentBlockHash;
bytes32 parentMetaHash;
bytes32 l1Hash;
uint difficulty;
uint256 difficulty;
bytes32 blobHash;
bytes32 extraData;
address coinbase;
uint64 id;
uint64 l2BlockNumber;
uint32 gasLimit;
uint32 l1StateBlockNumber;
uint64 timestamp;
uint64 l1Height;
uint24 txListByteOffset;
uint24 txListByteSize;
bool blobUsed;
}

/// @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.
struct Block {
bytes32 blockHash;
bytes32 metaHash;
uint64 blockId;
uint64 proposedAt;
uint64 proposedIn;
uint64 timestamp;
uint32 l1StateBlockNumber;
}

/// @dev Struct holding the state variables for the {TaikoL1} contract.
struct State {
mapping(uint blockId => Block) blocks;
mapping(uint blockId => mapping(bytes32 parentBlockHash => TransitionState)) transitions;

mapping(uint256 blockId => Block) blocks;
mapping(uint256 blockId => mapping(bytes32 parentBlockHash => TransitionState)) transitions;
uint64 genesisHeight;
uint64 genesisTimestamp;

uint64 numBlocks;
uint64 lastVerifiedBlockId;
bool provingPaused;
uint64 lastUnpausedAt;

uint256[143] __gap;
}
}
5 changes: 5 additions & 0 deletions packages/protocol/contracts/L1/TaikoErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,22 @@ abstract contract TaikoErrors {
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();
error L1_INVALID_PROPOSER();
error L1_INVALID_PROVER();
error L1_INVALID_TIER();
error L1_INVALID_TIMESTAMP();
error L1_INVALID_TRANSITION();
error L1_LIVENESS_BOND_NOT_RECEIVED();
error L1_NOT_ASSIGNED_PROVER();
Expand Down
Loading
Loading