-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
preconf router-based forced tx inclusion
- Loading branch information
1 parent
12de2cf
commit 729520b
Showing
5 changed files
with
194 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,24 @@ import "src/layer1/based/ITaikoInbox.sol"; | |
/// @title IPreconfRouter | ||
/// @custom:security-contact [email protected] | ||
interface IPreconfRouter { | ||
|
||
struct ForcedTx { | ||
bytes txList; | ||
uint256 timestamp; | ||
bool included; | ||
uint256 stakeAmount; | ||
} | ||
|
||
error ForcedTxListAlreadyIncluded(); | ||
error ForcedTxListAlreadyStored(); | ||
error ForcedTxListHashNotFound(); | ||
error InsufficientStakeAmount(); | ||
error NotTheOperator(); | ||
error ProposerIsNotTheSender(); | ||
|
||
|
||
event ForcedTxStored(bytes indexed txHash, uint256 timestamp); | ||
|
||
/// @notice Proposes a batch of blocks that have been preconfed. | ||
/// @dev This function only accepts batches from an operator selected to preconf in a particular | ||
/// slot or epoch and routes that batch to the TaikoInbox. | ||
|
@@ -19,8 +34,13 @@ interface IPreconfRouter { | |
function proposePreconfedBlocks( | ||
bytes calldata _params, | ||
bytes calldata _batchParams, | ||
bytes calldata _batchTxList | ||
bytes calldata _batchTxList, | ||
bool force | ||
) | ||
external | ||
returns (ITaikoInbox.BatchMetadata memory meta_); | ||
|
||
function updateBaseStakeAmount(uint256 _newBaseStakeAmount) external; | ||
|
||
function storeForcedTx(bytes calldata _txList) payable external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,33 +4,95 @@ pragma solidity ^0.8.24; | |
import "../iface/IPreconfRouter.sol"; | ||
import "../iface/IPreconfWhitelist.sol"; | ||
import "src/layer1/based/ITaikoInbox.sol"; | ||
import "src/shared/libs/LibAddress.sol"; | ||
import "src/shared/libs/LibStrings.sol"; | ||
import "src/shared/common/EssentialContract.sol"; | ||
|
||
/// @title PreconfRouter | ||
/// @custom:security-contact [email protected] | ||
contract PreconfRouter is EssentialContract, IPreconfRouter { | ||
uint256[50] private __gap; | ||
mapping(bytes32 => ForcedTx) public forcedTxLists; | ||
|
||
constructor(address _resolver) EssentialContract(_resolver) { } | ||
uint256 public pendingForcedTxHashes; | ||
|
||
uint256 public inclusionWindow; | ||
|
||
uint256 public baseStakeAmount; | ||
|
||
uint256[46] private __gap; | ||
|
||
constructor(address _resolver, uint256 _inclusionWindow, uint256 _baseStakeAmount) EssentialContract(_resolver) { | ||
inclusionWindow = _inclusionWindow; | ||
baseStakeAmount = _baseStakeAmount; | ||
} | ||
|
||
function init(address _owner) external initializer { | ||
__Essential_init(_owner); | ||
} | ||
|
||
function updateBaseStakeAmount(uint256 _newBaseStakeAmount) external onlyOwner { | ||
baseStakeAmount = _newBaseStakeAmount; | ||
} | ||
|
||
function getRequiredStakeAmount() public view returns (uint256) { | ||
uint256 multiplier = pendingForcedTxHashes == 0 ? 1 : pendingForcedTxHashes; | ||
return baseStakeAmount * multiplier; | ||
} | ||
|
||
function storeForcedTx(bytes calldata _txList) payable external { | ||
uint256 requiredStake = getRequiredStakeAmount(); | ||
require(msg.value >= requiredStake, InsufficientStakeAmount()); | ||
|
||
bytes32 txListHash = keccak256(_txList); | ||
require(forcedTxLists[txListHash].timestamp == 0, ForcedTxListAlreadyStored()); | ||
|
||
forcedTxLists[txListHash] = ForcedTx({ | ||
txList: _txList, | ||
timestamp: block.timestamp, | ||
included: false, | ||
stakeAmount: msg.value | ||
}); | ||
|
||
pendingForcedTxHashes++; | ||
|
||
emit ForcedTxStored(_txList, block.timestamp); | ||
} | ||
|
||
/// @inheritdoc IPreconfRouter | ||
function proposePreconfedBlocks( | ||
bytes calldata, | ||
bytes calldata _batchParams, | ||
bytes calldata _batchTxList | ||
bytes calldata _batchTxList, | ||
bool force | ||
) | ||
external | ||
returns (ITaikoInbox.BatchMetadata memory meta_) | ||
{ | ||
// Sender must be the selected operator for the epoch | ||
bytes32 forcedTxListHash = keccak256(_batchTxList); | ||
|
||
// Sender must be the selected operator for the epoch, or able to propose a forced txList | ||
// after the inclusion window has expired | ||
address selectedOperator = | ||
IPreconfWhitelist(resolve(LibStrings.B_PRECONF_WHITELIST, false)).getOperatorForEpoch(); | ||
require(msg.sender == selectedOperator, NotTheOperator()); | ||
|
||
if(force) { | ||
require(msg.sender == selectedOperator || canProposeFallback(forcedTxListHash), NotTheOperator()); | ||
} else { | ||
require(msg.sender == selectedOperator, NotTheOperator()); | ||
} | ||
|
||
if (force) { | ||
require(forcedTxLists[forcedTxListHash].timestamp != 0, ForcedTxListHashNotFound()); | ||
require(!forcedTxLists[forcedTxListHash].included, ForcedTxListAlreadyIncluded()); | ||
|
||
// Pay out the stake to the proposer | ||
LibAddress.sendEtherAndVerify(msg.sender, forcedTxLists[forcedTxListHash].stakeAmount); | ||
|
||
pendingForcedTxHashes--; | ||
|
||
forcedTxLists[forcedTxListHash].included = true; | ||
} | ||
|
||
|
||
// Call the proposeBatch function on the TaikoInbox | ||
address taikoInbox = resolve(LibStrings.B_TAIKO, false); | ||
|
@@ -39,4 +101,9 @@ contract PreconfRouter is EssentialContract, IPreconfRouter { | |
// Verify that the sender had set itself as the proposer | ||
require(meta_.proposer == msg.sender, ProposerIsNotTheSender()); | ||
} | ||
|
||
function canProposeFallback(bytes32 _forcedTxHash) public view returns (bool) { | ||
return block.timestamp > forcedTxLists[_forcedTxHash].timestamp + inclusionWindow && | ||
!forcedTxLists[_forcedTxHash].included; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters