-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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(protocol): router based forced tx inclusion #18824
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
} |
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) { | ||||||
return (2 ** pendingForcedTxHashes).max(4096) * baseStakeAmount; | ||||||
return baseStakeAmount * multiplier; | ||||||
} | ||||||
|
||||||
function storeForcedTx(bytes calldata _txList) payable external { | ||||||
uint256 requiredStake = getRequiredStakeAmount(); | ||||||
require(msg.value >= requiredStake, InsufficientStakeAmount()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Otherwise, please send back the overpaid fee. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will add |
||||||
|
||||||
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the preconfer can always use "false" for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can try to query if there is a forced txs request due, if so, force will be automatically true. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. force is there so that a non preconfer can call this function with force = true, and then it will check the forced block inclusion. That way, the user doesn't rely on a preconfer to include it, nor does it block preconfer from proposing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But this will ruin the preconfirmation - the preconfer may already shard some shared blocks before they are proposed on L1, now suddenly a user kicks in with a block which will reorg the chain. |
||||||
) | ||||||
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; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically you need to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea if that you would call this with its own batch, and This function is etiher called by the preconfing gateway, with it's own batch, and is profitable because of the Does this propose a problem? |
||||||
} | ||||||
|
||||||
|
||||||
// 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; | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about
priorityFee
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that's maybe a better name