Skip to content

Commit

Permalink
fix(protocol)!: apply bug fixes and introduce a few new features (#16543
Browse files Browse the repository at this point in the history
)
  • Loading branch information
dantaik committed Mar 28, 2024
1 parent ed756a4 commit 37fa853
Show file tree
Hide file tree
Showing 38 changed files with 712 additions and 336 deletions.
2 changes: 1 addition & 1 deletion packages/protocol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* **protocol:** add ERC20Airdrop test and deployment script ([#15752](https://github.com/taikoxyz/taiko-mono/issues/15752)) ([e60588c](https://github.com/taikoxyz/taiko-mono/commit/e60588cd5d455d0237ba7f7860d575a727f52103))
* **protocol:** add GuardianApproval event to GuardianProver ([#15817](https://github.com/taikoxyz/taiko-mono/issues/15817)) ([78f0481](https://github.com/taikoxyz/taiko-mono/commit/78f04812de1bcb22ed40c9ae9b16e42d3d3783c2))
* **protocol:** add message owner parameter to vault operations ([#15770](https://github.com/taikoxyz/taiko-mono/issues/15770)) ([136bdb7](https://github.com/taikoxyz/taiko-mono/commit/136bdb7395f4a30a76884c70310c02645ebaead2))
* **protocol:** add one missing `replaceUUPSImmutableVaules` in genesis generation script ([#15479](https://github.com/taikoxyz/taiko-mono/issues/15479)) ([24d73e7](https://github.com/taikoxyz/taiko-mono/commit/24d73e7e8a2bc324068f296cdcaadd0d87441586))
* **protocol:** add one missing `replaceUUPSImmutableValues` in genesis generation script ([#15479](https://github.com/taikoxyz/taiko-mono/issues/15479)) ([24d73e7](https://github.com/taikoxyz/taiko-mono/commit/24d73e7e8a2bc324068f296cdcaadd0d87441586))
* **protocol:** Add parent's metaHash to assignment ([#15498](https://github.com/taikoxyz/taiko-mono/issues/15498)) ([267e9a0](https://github.com/taikoxyz/taiko-mono/commit/267e9a083033d19adc7a78af1a191cbfa16937b6))
* **protocol:** add QuillAudits report ([#16186](https://github.com/taikoxyz/taiko-mono/issues/16186)) ([b0ce62e](https://github.com/taikoxyz/taiko-mono/commit/b0ce62ed8c55acce04660b39ae1eb677858aabf1))
* **protocol:** Add TaikoGovernor ([#15228](https://github.com/taikoxyz/taiko-mono/issues/15228)) ([f4a007b](https://github.com/taikoxyz/taiko-mono/commit/f4a007b024e5a868a59e9c97125dd9b9d884b45f))
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = {
{ "0x79fcdef22feed20eddacbb2587640e45491b757f": 1024 },
],
// Owner Chain ID, Security Council, and Timelock Controller
ownerChainId: 31337,
l1ChainId: 31337,
ownerSecurityCouncil: "0xDf08F82De32B8d460adbE8D72043E3a7e25A3B39",
ownerTimelockController: "0xDf08F82De32B8d460adbE8D72043E3a7e25A3B39",
// L2 EIP-1559 baseFee calculation related fields.
Expand Down
5 changes: 5 additions & 0 deletions packages/protocol/contracts/L1/TaikoEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ abstract contract TaikoEvents {
uint8 contestations
);

/// @notice Emitted when some state variable values changed.
/// @dev This event is currently used by Taiko node/client for block proposal/proving.
/// @param slotB The SlotB data structure.
event StateVariablesUpdated(TaikoData.SlotB slotB);

/// @dev Emitted when a block transition is proved or re-proved.
/// @param blockId The ID of the proven block.
/// @param tran The verified transition.
Expand Down
21 changes: 16 additions & 5 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
_;
}

modifier emitEventForClient() {
_;
LibVerifying.emitEventForClient(state);
}

/// @dev Fallback function to receive Ether from Hooks
receive() external payable {
if (!_inNonReentrant()) revert L1_RECEIVE_DISABLED();
Expand Down Expand Up @@ -60,6 +65,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
payable
nonReentrant
whenNotPaused
emitEventForClient
returns (TaikoData.BlockMetadata memory meta_, TaikoData.EthDeposit[] memory deposits_)
{
TaikoData.Config memory config = getConfig();
Expand All @@ -80,6 +86,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
nonReentrant
whenNotPaused
whenProvingNotPaused
emitEventForClient
{
(
TaikoData.BlockMetadata memory meta,
Expand All @@ -102,6 +109,7 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
nonReentrant
whenNotPaused
whenProvingNotPaused
emitEventForClient
{
LibVerifying.verifyBlocks(state, getConfig(), this, _maxBlocksToVerify);
}
Expand Down Expand Up @@ -184,14 +192,17 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
// - anchorGasLimit: 250_000 (based on internal devnet, its ~220_000
// after 256 L2 blocks)
return TaikoData.Config({
chainId: 167_008,
// Assume the block time is 3s, the protocol will allow ~1 month of
chainId: 167_009,
// Assume the block time is 3s, the protocol will allow ~90 days of
// new blocks without any verification.
blockMaxProposals: 864_000,
blockRingBufferSize: 864_100,
blockMaxProposals: 3_000_000,
blockRingBufferSize: 3_010_000,
// Can be overridden by the tier config.
maxBlocksToVerifyPerProposal: 10,
blockMaxGasLimit: 15_000_000,
// This value is set based on `gasTargetPerL1Block = 15_000_000 * 4` in TaikoL2.
// We use 8x rather than 4x here to handle the scenario where the average number of
// Taiko blocks proposed per Ethereum block is smaller than 1.
blockMaxGasLimit: 30_000_000 * 8,
livenessBond: 250e18, // 250 Taiko token
// ETH deposit related.
ethDepositRingBufferSize: 1024,
Expand Down
8 changes: 3 additions & 5 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,9 @@ library LibProposing {
meta_.blobHash = blobhash(0);
if (meta_.blobHash == 0) revert L1_BLOB_NOT_FOUND();
} else {
// The proposer must be an Externally Owned Account (EOA) for
// calldata usage. This ensures that the transaction is not an
// internal one, making calldata retrieval more straightforward for
// Taiko node software.
if (!LibAddress.isSenderEOA()) revert L1_PROPOSER_NOT_EOA();
// This function must be called as the outmost transaction (not an internal one) for
// the node to extract the calldata easily.
if (msg.sender != tx.origin) revert L1_PROPOSER_NOT_EOA();

meta_.blobHash = keccak256(_txList);
}
Expand Down
66 changes: 47 additions & 19 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ library LibProving {
error L1_INVALID_TRANSITION();
error L1_MISSING_VERIFIER();
error L1_NOT_ASSIGNED_PROVER();
error L1_CANNOT_CONTEST();

/// @notice Pauses or unpauses the proving process.
/// @param _state Current TaikoData.State.
Expand Down Expand Up @@ -143,7 +144,7 @@ library LibProving {
ITierProvider(_resolver.resolve("tier_provider", false)).getTier(_proof.tier);

// Check if this prover is allowed to submit a proof for this block
_checkProverPermission(_state, blk, ts, tid, tier);
_checkProverPermission(blk, ts, tid, tier, b.lastUnpausedAt);

// We must verify the proof, and any failure in proof verification will
// result in a revert.
Expand Down Expand Up @@ -223,7 +224,7 @@ library LibProving {
if (isTopTier) {
// The top tier prover re-proves.
assert(tier.validityBond == 0);
assert(ts.validityBond == 0 && ts.contestBond == 0 && ts.contester == address(0));
assert(ts.validityBond == 0 && ts.contester == address(0));

ts.prover = msg.sender;
ts.blockHash = _tran.blockHash;
Expand All @@ -240,6 +241,13 @@ library LibProving {
// Contesting but not on the highest tier
if (ts.contester != address(0)) revert L1_ALREADY_CONTESTED();

// Making it a non-sliding window, relative when ts.timestamp was registered (or to
// lastUnpaused if that one is bigger)
if (LibUtils.isPostDeadline(ts.timestamp, b.lastUnpausedAt, tier.cooldownWindow)) {
revert L1_CANNOT_CONTEST();
}

// _checkIfContestable(/*_state,*/ tier.cooldownWindow, ts.timestamp);
// Burn the contest bond from the prover.
tko.safeTransferFrom(msg.sender, address(this), tier.contestBond);

Expand Down Expand Up @@ -349,6 +357,15 @@ library LibProving {
}

/// @dev Handles what happens when there is a higher proof incoming
///
/// Assume Alice is the initial prover, Bob is the contester, and Cindy is the subsequent
/// prover. The validity bond `V` is set at 100, and the contestation bond `C` at 500. If Bob
/// successfully contests, he receives a reward of 65.625, calculated as 3/4 of 7/8 of 100. Cindy
/// receives 21.875, which is 1/4 of 7/8 of 100, while the protocol retains 12.5 as friction.
/// Bob's Return on Investment (ROI) is 13.125%, calculated from 65.625 divided by 500.
// To establish the expected ROI `r` for valid contestations, where the contestation bond `C` to
// validity bond `V` ratio is `C/V = 21/(32*r)`, and if `r` set at 10%, the C/V ratio will be
// 6.5625.
function _overrideWithHigherProof(
TaikoData.TransitionState storage _ts,
TaikoData.Transition memory _tran,
Expand All @@ -360,22 +377,27 @@ library LibProving {
private
{
// Higher tier proof overwriting lower tier proof
uint256 reward;
uint256 reward; // reward to the new (current) prover

if (_ts.contester != address(0)) {
if (_sameTransition) {
// The contested transition is proven to be valid, contestor loses the game
reward = _ts.contestBond >> 2;
_tko.safeTransfer(_ts.prover, _ts.validityBond + reward);
// The contested transition is proven to be valid, contester loses the game
reward = _rewardAfterFriction(_ts.contestBond);

// We return the validity bond back, but the original prover doesn't get any reward.
_tko.safeTransfer(_ts.prover, _ts.validityBond);
} else {
// The contested transition is proven to be invalid, contestor wins the game
reward = _ts.validityBond >> 2;
_tko.safeTransfer(_ts.contester, _ts.contestBond + reward);
// The contested transition is proven to be invalid, contester wins the game.
// Contester gets 3/4 of reward, the new prover gets 1/4.
reward = _rewardAfterFriction(_ts.validityBond) >> 2;

_tko.safeTransfer(_ts.contester, _ts.contestBond + reward * 3);
}
} else {
if (_sameTransition) revert L1_ALREADY_PROVED();
// Contest the existing transition and prove it to be invalid
reward = _ts.validityBond >> 1;
// Contest the existing transition and prove it to be invalid. The new prover get all
// rewards.
reward = _rewardAfterFriction(_ts.validityBond);
_ts.contestations += 1;
}

Expand All @@ -401,29 +423,35 @@ library LibProving {

/// @dev Check the msg.sender (the new prover) against the block's assigned prover.
function _checkProverPermission(
TaikoData.State storage _state,
TaikoData.Block storage _blk,
TaikoData.TransitionState storage _ts,
uint32 _tid,
ITierProvider.Tier memory _tier
ITierProvider.Tier memory _tier,
uint64 _lastUnpausedAt
)
private
view
{
// The highest tier proof can always submit new proofs
if (_tier.contestBond == 0) return;

bool inProvingWindow = uint256(_ts.timestamp).max(_state.slotB.lastUnpausedAt)
+ _tier.provingWindow * 60 >= block.timestamp;
bool isAssignedPover = msg.sender == _blk.assignedProver;
bool isAssignedProver = msg.sender == _blk.assignedProver;

// The assigned prover can only submit the very first transition.
if (_tid == 1 && _ts.tier == 0 && inProvingWindow) {
if (!isAssignedPover) revert L1_NOT_ASSIGNED_PROVER();
if (
_tid == 1 && _ts.tier == 0
&& !LibUtils.isPostDeadline(_ts.timestamp, _lastUnpausedAt, _tier.provingWindow)
) {
if (!isAssignedProver) revert L1_NOT_ASSIGNED_PROVER();
} else {
// Disallow the same address to prove the block so that we can detect that the
// assigned prover should not receive his liveness bond back
if (isAssignedPover) revert L1_ASSIGNED_PROVER_NOT_ALLOWED();
if (isAssignedProver) revert L1_ASSIGNED_PROVER_NOT_ALLOWED();
}
}

/// @dev Returns the reward after applying 12.5% friction.
function _rewardAfterFriction(uint256 _amount) private pure returns (uint256) {
return (_amount * 7) >> 3;
}
}
16 changes: 16 additions & 0 deletions packages/protocol/contracts/L1/libs/LibUtils.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../../libs/LibMath.sol";
import "../TaikoData.sol";

/// @title LibUtils
/// @notice A library that offers helper functions.
/// @custom:security-contact [email protected]
library LibUtils {
using LibMath for uint256;

// Warning: Any errors defined here must also be defined in TaikoErrors.sol.
error L1_BLOCK_MISMATCH();
error L1_INVALID_BLOCK_ID();
Expand Down Expand Up @@ -85,4 +88,17 @@ library LibUtils {

if (tid_ >= _blk.nextTransitionId) revert L1_UNEXPECTED_TRANSITION_ID();
}

function isPostDeadline(
uint256 _tsTimestamp,
uint256 _lastUnpausedAt,
uint256 _windowMinutes
)
internal
view
returns (bool)
{
uint256 deadline = _tsTimestamp.max(_lastUnpausedAt) + _windowMinutes * 60;
return block.timestamp >= deadline;
}
}
18 changes: 16 additions & 2 deletions packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ library LibVerifying {
uint8 contestations
);

/// @notice Emitted when some state variable values changed.
/// @dev This event is currently used by Taiko node/client for block proposal/proving.
/// @param slotB The SlotB data structure.
event StateVariablesUpdated(TaikoData.SlotB slotB);

// Warning: Any errors defined here must also be defined in TaikoErrors.sol.
error L1_BLOCK_MISMATCH();
error L1_INVALID_CONFIG();
Expand Down Expand Up @@ -150,9 +155,13 @@ library LibVerifying {
if (tierProvider == address(0)) {
tierProvider = _resolver.resolve("tier_provider", false);
}

if (
uint256(ITierProvider(tierProvider).getTier(ts.tier).cooldownWindow) * 60
+ uint256(ts.timestamp).max(_state.slotB.lastUnpausedAt) > block.timestamp
!LibUtils.isPostDeadline(
ts.timestamp,
b.lastUnpausedAt,
ITierProvider(tierProvider).getTier(ts.tier).cooldownWindow
)
) {
// If cooldownWindow is 0, the block can theoretically
// be proved and verified within the same L1 block.
Expand Down Expand Up @@ -223,6 +232,11 @@ library LibVerifying {
}
}

/// @notice Emit events used by client/node.
function emitEventForClient(TaikoData.State storage _state) internal {
emit StateVariablesUpdated({ slotB: _state.slotB });
}

function _syncChainData(
TaikoData.Config memory _config,
IAddressResolver _resolver,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ pragma solidity 0.8.24;
import "../../common/EssentialContract.sol";
import "./ITierProvider.sol";

/// @title TestnetTierProvider
/// @title TierProviderV1
/// @dev Labeled in AddressResolver as "tier_provider"
/// @custom:security-contact [email protected]
contract TestnetTierProvider is EssentialContract, ITierProvider {
contract TierProviderV1 is EssentialContract, ITierProvider {
uint256[50] private __gap;

/// @notice Initializes the contract.
Expand All @@ -32,8 +32,8 @@ contract TestnetTierProvider is EssentialContract, ITierProvider {
if (_tierId == LibTiers.TIER_SGX) {
return ITierProvider.Tier({
verifierName: "tier_sgx",
validityBond: 500 ether, // TKO
contestBond: 1000 ether, // TKO
validityBond: 250 ether, // TKO
contestBond: 1640 ether, // =250TKO * 6.5625
cooldownWindow: 1440, //24 hours
provingWindow: 60, // 1 hours
maxBlocksToVerifyPerProof: 8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ pragma solidity 0.8.24;
import "../../common/EssentialContract.sol";
import "./ITierProvider.sol";

/// @title MainnetTierProvider
/// @title TierProviderV2
/// @dev Labeled in AddressResolver as "tier_provider"
/// @custom:security-contact [email protected]
contract MainnetTierProvider is EssentialContract, ITierProvider {
contract TierProviderV2 is EssentialContract, ITierProvider {
uint256[50] private __gap;

/// @notice Initializes the contract.
Expand All @@ -22,7 +22,7 @@ contract MainnetTierProvider is EssentialContract, ITierProvider {
return ITierProvider.Tier({
verifierName: "tier_sgx",
validityBond: 250 ether, // TKO
contestBond: 500 ether, // TKO
contestBond: 1640 ether, // =250TKO * 6.5625
cooldownWindow: 1440, //24 hours
provingWindow: 60, // 1 hours
maxBlocksToVerifyPerProof: 8
Expand All @@ -33,7 +33,7 @@ contract MainnetTierProvider is EssentialContract, ITierProvider {
return ITierProvider.Tier({
verifierName: "tier_sgx_zkvm",
validityBond: 500 ether, // TKO
contestBond: 1000 ether, // TKO
contestBond: 3280 ether, // =500TKO * 6.5625
cooldownWindow: 1440, //24 hours
provingWindow: 240, // 4 hours
maxBlocksToVerifyPerProof: 4
Expand Down
Loading

0 comments on commit 37fa853

Please sign in to comment.