Skip to content

Commit

Permalink
Revert "init"
Browse files Browse the repository at this point in the history
This reverts commit 40efbb7.
  • Loading branch information
dantaik committed Feb 6, 2025
1 parent 40efbb7 commit fb34324
Show file tree
Hide file tree
Showing 8 changed files with 2,036 additions and 0 deletions.
403 changes: 403 additions & 0 deletions packages/protocol/contracts/layer1/based/ITaikoInbox.sol

Large diffs are not rendered by default.

823 changes: 823 additions & 0 deletions packages/protocol/contracts/layer1/based/TaikoInbox.sol

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "src/shared/common/EssentialContract.sol";
import "src/shared/libs/LibMath.sol";
import "src/shared/libs/LibAddress.sol";
import "src/shared/libs/LibStrings.sol";
import "src/layer1/based/ITaikoInbox.sol";
import "./IForcedInclusionStore.sol";

/// @title ForcedInclusionStore
/// @dev A contract for storing and managing forced inclusion requests. Forced inclusions allow
/// users to pay a fee
/// to ensure their transactions are included in a block. The contract maintains a FIFO queue
/// of inclusion requests.
/// @custom:security-contact
contract ForcedInclusionStore is EssentialContract, IForcedInclusionStore {
using LibAddress for address;
using LibMath for uint256;

uint256 private constant SECONDS_PER_BLOCK = 12;

uint8 public immutable inclusionDelay;
uint64 public immutable feeInGwei;

mapping(uint256 id => ForcedInclusion inclusion) public queue; // slot 1
uint64 public head; // slot 2
uint64 public tail;
uint64 public lastProcessedAtBatchId;
uint64 private __reserved1;

uint256[48] private __gap;

constructor(
address _resolver,
uint8 _inclusionDelay,
uint64 _feeInGwei
)
EssentialContract(_resolver)
{
require(_inclusionDelay != 0 && _inclusionDelay % SECONDS_PER_BLOCK == 0, InvalidParams());
require(_feeInGwei != 0, InvalidParams());

inclusionDelay = _inclusionDelay;
feeInGwei = _feeInGwei;
}

function init(address _owner) external initializer {
__Essential_init(_owner);
}

function storeForcedInclusion(
uint8 blobIndex,
uint32 blobByteOffset,
uint32 blobByteSize
)
external
payable
nonReentrant
{
bytes32 blobHash = _blobHash(blobIndex);
require(blobHash != bytes32(0), BlobNotFound());
require(msg.value == feeInGwei * 1 gwei, IncorrectFee());

ITaikoInbox inbox = ITaikoInbox(resolve(LibStrings.B_TAIKO, false));

ForcedInclusion memory inclusion = ForcedInclusion({
blobHash: blobHash,
feeInGwei: uint64(msg.value / 1 gwei),
createdAtBatchId: inbox.getStats2().numBatches,
blobByteOffset: blobByteOffset,
blobByteSize: blobByteSize
});

queue[tail++] = inclusion;

emit ForcedInclusionStored(inclusion);
}

function consumeOldestForcedInclusion(address _feeRecipient)
external
nonReentrant
onlyFromNamed(LibStrings.B_TAIKO_WRAPPER)
returns (ForcedInclusion memory inclusion_)
{
// we only need to check the first one, since it will be the oldest.
uint64 _head = head;
ForcedInclusion storage inclusion = queue[_head];
require(inclusion.createdAtBatchId != 0, NoForcedInclusionFound());

ITaikoInbox inbox = ITaikoInbox(resolve(LibStrings.B_TAIKO, false));

inclusion_ = inclusion;
delete queue[_head];

unchecked {
lastProcessedAtBatchId = inbox.getStats2().numBatches;
head = _head + 1;
}

emit ForcedInclusionConsumed(inclusion_);
_feeRecipient.sendEtherAndVerify(inclusion_.feeInGwei * 1 gwei);
}

function getForcedInclusion(uint256 index) external view returns (ForcedInclusion memory) {
return queue[index];
}

function getOldestForcedInclusionDeadline() public view returns (uint256) {
unchecked {
ForcedInclusion storage inclusion = queue[head];
return inclusion.createdAtBatchId == 0
? type(uint64).max
: uint256(lastProcessedAtBatchId).max(inclusion.createdAtBatchId) + inclusionDelay;
}
}

function isOldestForcedInclusionDue() external view returns (bool) {
ITaikoInbox inbox = ITaikoInbox(resolve(LibStrings.B_TAIKO, false));
return inbox.getStats2().numBatches >= getOldestForcedInclusionDeadline();
}

// @dev Override this function for easier testing blobs
function _blobHash(uint8 blobIndex) internal view virtual returns (bytes32) {
return blobhash(blobIndex);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @title IForcedInclusionStore
/// @custom:security-contact [email protected]
interface IForcedInclusionStore {
/// @dev Error thrown when a blob is not found.
error BlobNotFound();
/// @dev Error thrown when the parameters are invalid.
error InvalidParams();
/// @dev Error thrown when the fee is incorrect.
error IncorrectFee();

error NoForcedInclusionFound();

/// @dev Event emitted when a forced inclusion is stored.
event ForcedInclusionStored(ForcedInclusion forcedInclusion);
/// @dev Event emitted when a forced inclusion is consumed.
event ForcedInclusionConsumed(ForcedInclusion forcedInclusion);

struct ForcedInclusion {
bytes32 blobHash;
uint64 feeInGwei;
uint64 createdAtBatchId;
uint32 blobByteOffset;
uint32 blobByteSize;
}

/// @dev Retrieve a forced inclusion request by its index.
/// @param index The index of the forced inclusion request in the queue.
/// @return The forced inclusion request at the specified index.
function getForcedInclusion(uint256 index) external view returns (ForcedInclusion memory);

/// @dev Get the deadline for the oldest forced inclusion.
/// @return The deadline for the oldest forced inclusion.
function getOldestForcedInclusionDeadline() external view returns (uint256);

/// @dev Check if the oldest forced inclusion is due.
/// @return True if the oldest forced inclusion is due, false otherwise.
function isOldestForcedInclusionDue() external view returns (bool);

/// @dev Consume a forced inclusion request.
/// The inclusion request must be marked as processed and the priority fee must be paid to the
/// caller.
/// @param _feeRecipient The address to receive the priority fee.
/// @return inclusion_ The forced inclusion request.
function consumeOldestForcedInclusion(address _feeRecipient)
external
returns (ForcedInclusion memory);

/// @dev Store a forced inclusion request.
/// The priority fee must be paid to the contract.
/// @param blobIndex The index of the blob that contains the transaction data.
/// @param blobByteOffset The byte offset in the blob
/// @param blobByteSize The size of the blob in bytes
function storeForcedInclusion(
uint8 blobIndex,
uint32 blobByteOffset,
uint32 blobByteSize
)
external
payable;
}
113 changes: 113 additions & 0 deletions packages/protocol/contracts/layer1/forced-inclusion/TaikoWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "src/shared/common/EssentialContract.sol";
import "src/shared/based/ITaiko.sol";
import "src/shared/libs/LibMath.sol";
import "src/shared/libs/LibNetwork.sol";
import "src/shared/libs/LibStrings.sol";
import "src/shared/signal/ISignalService.sol";
import "src/layer1/verifiers/IVerifier.sol";
import "src/layer1/based/TaikoInbox.sol";
import "./ForcedInclusionStore.sol";

/// @title TaikoWrapper
/// @dev This contract is part of a delayed inbox implementation to enforce the inclusion of
/// transactions.
/// The current design is a simplified and can be improved with the following ideas:
/// 1. **Fee-Based Request Prioritization**:
/// - Proposers can selectively fulfill pending requests based on transaction fees.
/// - Requests not yet due can be processed earlier if fees are attractive, incentivizing timely
/// execution.
///
/// 2. **Rate Limit Control**:
/// - A rate-limiting mechanism ensures a minimum interval of 12*N seconds between request
/// fulfillments.
/// - Prevents proposers from being overwhelmed during high request volume, ensuring system
/// stability.
///
/// 3. **Calldata and Blob Support**:
/// - Supports both calldata and blobs in the transaction list.
///
/// 4. **Gas-Efficient Request Storage**:
/// - Avoids storing full request data in contract storage.
/// - Saves only the request hash and its timestamp.
/// - Leverages Ethereum events to store request details off-chain.
/// - Proposers can reconstruct requests as needed, minimizing on-chain storage and gas
/// consumption.
///
/// @custom:security-contact [email protected]

contract TaikoWrapper is EssentialContract {
using LibMath for uint256;

/// @dev Event emitted when a forced inclusion is processed.
event ForcedInclusionProcessed(IForcedInclusionStore.ForcedInclusion);
/// @dev Error thrown when the oldest forced inclusion is due.

error OldestForcedInclusionDue();

uint16 public constant MAX_FORCED_TXS_PER_FORCED_INCLUSION = 512;

uint256[50] private __gap;

constructor(address _resolver) EssentialContract(_resolver) { }

function init(address _owner) external initializer {
__Essential_init(_owner);
}

/// @notice Proposes a batch of blocks with forced inclusion.
/// @param _forcedInclusionParams An optional ABI-encoded BlockParams for the forced inclusion
/// batch.
/// @param _params ABI-encoded BlockParams.
/// @param _txList The transaction list in calldata. If the txList is empty, blob will be used
/// for data availability.
/// @return info_ The info of the proposed batch.
/// @return meta_ The metadata of the proposed batch.
function proposeBatchWithForcedInclusion(
bytes calldata _forcedInclusionParams,
bytes calldata _params,
bytes calldata _txList
)
external
nonReentrant
returns (ITaikoInbox.BatchInfo memory info_, ITaikoInbox.BatchMetadata memory meta_)
{
ITaikoInbox inbox = ITaikoInbox(resolve(LibStrings.B_TAIKO, false));

IForcedInclusionStore store =
IForcedInclusionStore(resolve(LibStrings.B_FORCED_INCLUSION_STORE, false));

if (_forcedInclusionParams.length == 0) {
require(!store.isOldestForcedInclusionDue(), OldestForcedInclusionDue());
} else {
IForcedInclusionStore.ForcedInclusion memory inclusion =
store.consumeOldestForcedInclusion(msg.sender);

ITaikoInbox.BatchParams memory params =
abi.decode(_forcedInclusionParams, (ITaikoInbox.BatchParams));

// Overwrite the batch params to have only 1 block and up to
// MAX_FORCED_TXS_PER_FORCED_INCLUSION transactions
if (params.blocks.length == 0) {
params.blocks = new ITaikoInbox.BlockParams[](1);
}

if (params.blocks[0].numTransactions < MAX_FORCED_TXS_PER_FORCED_INCLUSION) {
params.blocks[0].numTransactions = MAX_FORCED_TXS_PER_FORCED_INCLUSION;
}

params.blobParams.blobHashes = new bytes32[](1);
params.blobParams.blobHashes[0] = inclusion.blobHash;
params.blobParams.byteOffset = inclusion.blobByteOffset;
params.blobParams.byteSize = inclusion.blobByteSize;

inbox.proposeBatch(abi.encode(params), "");
emit ForcedInclusionProcessed(inclusion);
}

(info_, meta_) = inbox.proposeBatch(_params, _txList);
}
}
60 changes: 60 additions & 0 deletions packages/protocol/contracts/layer1/fork-router/ForkRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

/// @title ForkRouter
/// @custom:security-contact [email protected]
/// @notice This contract routes calls to the current fork.
///
/// +--> newFork
/// PROXY -> FORK_ROUTER--|
/// +--> oldFork
contract ForkRouter is UUPSUpgradeable, Ownable2StepUpgradeable {
address public immutable oldFork;
address public immutable newFork;

error InvalidParams();
error ZeroForkAddress();

constructor(address _oldFork, address _newFork) {
require(_newFork != address(0) && _newFork != _oldFork, InvalidParams());

oldFork = _oldFork;
newFork = _newFork;

_disableInitializers();
}

fallback() external payable virtual {
_fallback();
}

receive() external payable virtual {
_fallback();
}

/// @notice Returns true if a function should be routed to the old fork
/// @dev This function should be overridden by the implementation contract
function shouldRouteToOldFork(bytes4) public pure virtual returns (bool) {
return false;
}

function _fallback() internal virtual {
address fork = shouldRouteToOldFork(msg.sig) ? oldFork : newFork;
require(fork != address(0), ZeroForkAddress());

assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), fork, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())

switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}

function _authorizeUpgrade(address) internal virtual override onlyOwner { }
}
Loading

0 comments on commit fb34324

Please sign in to comment.