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(protocol): add preconfirmation support [helder] #17654

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e69e360
make block metadata predictable longer + sequencer registry
Brechtpd Jun 23, 2024
f27e0b7
feedback
Brechtpd Jun 24, 2024
ee84153
order alphabetically
Brechtpd Jun 24, 2024
d59db31
Allow proposer to also choose the timestamp
Brechtpd Jun 25, 2024
fac9ba8
fix issue with difficulty not being independent of L1 slot or proposing
Brechtpd Jun 25, 2024
6ef1726
fix timestamp range check
Brechtpd Jun 25, 2024
de08795
fix ci
Brechtpd Jun 27, 2024
b5036c3
Merge remote-tracking branch 'origin/helder' into preconfirmations
Brechtpd Jun 27, 2024
3e0c74c
forge fmt & update contract layout table
Brechtpd Jun 27, 2024
015ed34
increase allowed L1 data window
Brechtpd Jun 28, 2024
23351f8
comment out some checks/unused functionality to make TaikoL1 deployable
Brechtpd Jun 29, 2024
d021b0d
forge fmt & update contract layout table
Brechtpd Jun 29, 2024
9e97bbf
add back simplified EOA check for now
Brechtpd Jun 30, 2024
507149b
Merge remote-tracking branch 'origin/preconfirmations' into preconfir…
Brechtpd Jun 30, 2024
4bbe16e
fix check
Brechtpd Jun 30, 2024
c29759f
nevermind disable check again to keep tests running
Brechtpd Jun 30, 2024
a68a163
enable check (I can't read)
Brechtpd Jun 30, 2024
d9fbe44
set proving window to 0 so anybody can prove blocks directly (with TKO)
Brechtpd Jul 1, 2024
0986e22
deploy and register sequencer registry
cyberhorsey Jul 2, 2024
3628ae7
forge fmt & update contract layout table
cyberhorsey Jul 2, 2024
ed709d7
.
cyberhorsey Jul 2, 2024
999e5d4
.
cyberhorsey Jul 2, 2024
0a89fa0
forge fmt & update contract layout table
cyberhorsey Jul 2, 2024
6a0f394
rm unused stuff
cyberhorsey Jul 3, 2024
f4e602a
forge fmt & update contract layout table
cyberhorsey Jul 3, 2024
63ba193
rm bridge copy
cyberhorsey Jul 3, 2024
2052946
Merge branch 'preconfirmations' of github.com:taikoxyz/taiko-mono int…
cyberhorsey Jul 3, 2024
967f7ee
forge fmt & update contract layout table
cyberhorsey Jul 3, 2024
5962ba9
calldata check fix
cyberhorsey Jul 5, 2024
157cd13
feat(protocol): allow contracts to propose (#17744)
cyberhorsey Jul 5, 2024
7451a5a
feat(protocol): propose multiple blocks in one call (#17787)
cyberhorsey Jul 13, 2024
c00bfb5
conversion fix
cyberhorsey Jul 13, 2024
8439b00
lint
cyberhorsey Jul 13, 2024
d1ecef2
forge fmt & update contract layout table
cyberhorsey Jul 13, 2024
4699f96
.
cyberhorsey Jul 13, 2024
14f5b94
forge fmt & update contract layout table
cyberhorsey Jul 13, 2024
0b2a987
feat(taiko-client): updates for protocol preconfirmation changes (#17…
davidtaikocha Jul 16, 2024
c4fe319
.
cyberhorsey Jul 16, 2024
73048c2
proposeBlocks as well as proposeBlock
cyberhorsey Jul 16, 2024
4205b96
lint
cyberhorsey Jul 16, 2024
c0f3b38
rm prove/verify block for code size temporarily, not being used
cyberhorsey Jul 16, 2024
0e03c3c
forge fmt & update contract layout table
cyberhorsey Jul 16, 2024
b38bd37
update taikoevents
cyberhorsey Jul 16, 2024
1b6fd86
Merge branch 'preconfirmations' of github.com:taikoxyz/taiko-mono int…
cyberhorsey Jul 16, 2024
a383b19
add offset/len
cyberhorsey Jul 17, 2024
1d1c41f
.
cyberhorsey Jul 17, 2024
284ef1a
feat(taiko-client): preconfirmation indexing (#17866)
cyberhorsey Aug 12, 2024
6579417
proposeblocks api fix
cyberhorsey Aug 14, 2024
915f888
log
cyberhorsey Aug 14, 2024
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
10 changes: 10 additions & 0 deletions packages/protocol/contracts/L1/ISequencerRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @title ISequencerRegistry
/// @custom:security-contact [email protected]
interface ISequencerRegistry {
/// @notice Return true if the specified address can propose blocks, false otherwise
/// @param _proposer The address proposing a block
function isEligibleSigner(address _proposer) external returns (bool);
}
6 changes: 3 additions & 3 deletions packages/protocol/contracts/L1/ITaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import "./TaikoData.sol";
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.
/// @param _txLists txList data if calldata is used for DA.
/// @return meta_ The metadata of the proposed L2 block.
/// @return deposits_ The Ether deposits processed.
function proposeBlock(
bytes calldata _params,
bytes calldata _txList
bytes[] calldata _params,
bytes[] calldata _txLists
)
external
payable
Expand Down
50 changes: 50 additions & 0 deletions packages/protocol/contracts/L1/SequencerRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../common/EssentialContract.sol";
import "./ISequencerRegistry.sol";

/// @title SequencerRegistry
/// A dummy implementation that only whitelist some trusted addresses. A real
/// implementation would only allow a single proposer address to propose a block
/// using some selection mechanism.
/// @custom:security-contact [email protected]
contract SequencerRegistry is EssentialContract, ISequencerRegistry {
dantaik marked this conversation as resolved.
Show resolved Hide resolved
/// @dev Emitted when the status of a sequencer is updated.
/// @param sequencer The address of the sequencer whose state has updated.
/// @param enabled If the sequencer is now enabled or not.
event SequencerUpdated(address indexed sequencer, bool enabled);

/// @notice Whitelisted sequencers
mapping(address sequencer => bool enabled) public sequencers;

uint256[49] private __gap;

/// @notice Initializes the contract with the provided address manager.
/// @param _owner The address of the owner.
function init(address _owner) external initializer {
__Essential_init(_owner);
}

/// @notice Sets/unsets an the imageId as trusted entity
/// @param _sequencers The list of sequencers
/// @param _enabled The corresponding list of the new status of the sequencers
function setSequencers(
address[] memory _sequencers,
bool[] memory _enabled
)
external
onlyOwner
{
require(_sequencers.length == _enabled.length, "invalid input data");
for (uint256 i = 0; i < _sequencers.length; i++) {
sequencers[_sequencers[i]] = _enabled[i];
Brechtpd marked this conversation as resolved.
Show resolved Hide resolved
emit SequencerUpdated(_sequencers[i], _enabled[i]);
}
}

/// @inheritdoc ISequencerRegistry
function isEligibleSigner(address _proposer) external view returns (bool) {
return sequencers[_proposer];
}
}
13 changes: 13 additions & 0 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ library TaikoData {
bytes32 parentMetaHash;
HookCall[] hookCalls; // DEPRECATED, value ignored.
bytes signature;
uint32 l1StateBlockNumber;
dantaik marked this conversation as resolved.
Show resolved Hide resolved
Brechtpd marked this conversation as resolved.
Show resolved Hide resolved
uint64 timestamp;
uint64 blobTxListOffset;
uint64 blobTxListLength;
uint8 blobIndex;
}

/// @dev Struct containing data only required for proving a block
Expand All @@ -85,6 +90,9 @@ library TaikoData {
bool blobUsed;
bytes32 parentMetaHash;
address sender; // a.k.a proposer
uint64 blobTxListOffset;
uint64 blobTxListLength;
uint8 index; // index of the block in the tx, ie: first block proposed is 0, next is 1, etc
}

/// @dev Struct representing transition to be proven.
Expand Down Expand Up @@ -124,6 +132,11 @@ library TaikoData {
// this block is not verified as the last block in a batch, verifiedTransitionId
// will remain zero.
uint32 verifiedTransitionId;
// The block's timestamp
uint64 timestamp; // slot 4
// This block number is the value for _l1BlockId (and related values) in TaikoL2's anchor()
// function.
uint32 l1StateBlockNumber;
}

/// @dev Struct representing an Ethereum deposit.
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/contracts/L1/TaikoErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ abstract contract TaikoErrors {
error L1_INVALID_GENESIS_HASH();
error L1_INVALID_PARAM();
error L1_INVALID_PAUSE_STATUS();
error L1_INVALID_PROPOSER();
error L1_INVALID_SIG();
error L1_INVALID_TIER();
error L1_INVALID_TRANSITION();
Expand Down
26 changes: 22 additions & 4 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "./libs/LibVerifying.sol";
import "./ITaikoL1.sol";
import "./TaikoErrors.sol";
import "./TaikoEvents.sol";
import "./ISequencerRegistry.sol";

/// @title TaikoL1
/// @notice This contract serves as the "base layer contract" of the Taiko protocol, providing
Expand Down Expand Up @@ -68,8 +69,8 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {

/// @inheritdoc ITaikoL1
function proposeBlock(
bytes calldata _params,
bytes calldata _txList
bytes[] calldata _params,
bytes[] calldata _txLists
)
external
payable
Expand All @@ -78,13 +79,30 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
emitEventForClient
returns (TaikoData.BlockMetadata memory meta_, TaikoData.EthDeposit[] memory deposits_)
{
// If there's a sequencer registry, check if the block can be proposed by the current
// proposer
ISequencerRegistry sequencerRegistry =
ISequencerRegistry(resolve(LibStrings.B_SEQUENCER_REGISTRY, true));
if (sequencerRegistry != ISequencerRegistry(address(0))) {
if (!sequencerRegistry.isEligibleSigner(msg.sender)) {
revert L1_INVALID_PROPOSER();
}
}

require(_params.length == _txLists.length, "mismatched params length");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to think, it might be better like this below if we wanna support calldata efficiently.

        if(txLists.length != 0) {
            require(params.length == _txLists.length, "mismatched params length"));
        }

So currently protocol can handle both, but with the above change, we would have to create empty _txLists -matched to the length of _params, even if we use blob OR some empty if we use blob + calldata both, separately, paralel with one L1 TXN.

I dont think it is practical to use different data avail. layer for multiple L2 blocks but one L1 TXN. (Maybe i'm wrong in here, and in this case inefficiency is i guess negligible).


TaikoData.Config memory config = getConfig();
TaikoToken tko = TaikoToken(resolve(LibStrings.B_TAIKO_TOKEN, false));

(meta_, deposits_) = LibProposing.proposeBlock(state, tko, config, this, _params, _txList);
for (uint8 i = 0; i < _params.length; i++) {
(meta_, deposits_) =
LibProposing.proposeBlock(state, tko, config, this, _params[i], _txLists[i], i);
}

if (LibUtils.shouldVerifyBlocks(config, meta_.id, true) && !state.slotB.provingPaused) {
LibVerifying.verifyBlocks(state, tko, config, this, config.maxBlocksToVerify);
LibVerifying.verifyBlocks(
state, tko, config, this, config.maxBlocksToVerify * uint64(_params.length)
);
}
}

Expand Down
116 changes: 81 additions & 35 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ library LibProposing {
address indexed assignedProver,
uint96 livenessBond,
TaikoData.BlockMetadata meta,
TaikoData.EthDeposit[] depositsProcessed
TaikoData.EthDeposit[] depositsProcessed,
uint64 blobTxListOffset,
uint64 blobTxListLength,
uint8 blobIndex
);

/// @notice Emitted when a block's txList is in the calldata.
Expand All @@ -46,7 +49,9 @@ library LibProposing {
// Warning: Any errors defined here must also be defined in TaikoErrors.sol.
error L1_BLOB_NOT_AVAILABLE();
error L1_BLOB_NOT_FOUND();
error L1_INVALID_L1_STATE_BLOCK();
error L1_INVALID_SIG();
error L1_INVALID_TIMESTAMP();
error L1_LIVENESS_BOND_NOT_RECEIVED();
error L1_TOO_MANY_BLOCKS();
error L1_UNEXPECTED_PARENT();
Expand All @@ -55,17 +60,17 @@ library LibProposing {
/// @param _state Current TaikoData.State.
/// @param _tko The taiko token.
/// @param _config Actual TaikoData.Config.
/// @param _resolver Address resolver interface.
/// @param _data Encoded data bytes containing the block params.
/// @param _txList Transaction list bytes (if not blob).
/// @return meta_ The constructed block's metadata.
function proposeBlock(
TaikoData.State storage _state,
TaikoToken _tko,
TaikoData.Config memory _config,
IAddressResolver _resolver,
IAddressResolver, /*_resolver*/
bytes calldata _data,
bytes calldata _txList
bytes calldata _txList,
uint8 index
)
internal
returns (TaikoData.BlockMetadata memory meta_, TaikoData.EthDeposit[] memory deposits_)
Expand All @@ -77,6 +82,18 @@ library LibProposing {
local.params.coinbase = msg.sender;
}

// If no L1 state block is specified, fall back to the previous L1 block
// (the most recent block that has its block hash available in the EVM through the blockhash
// opcode).
if (local.params.l1StateBlockNumber == 0) {
local.params.l1StateBlockNumber = uint32(block.number) - 1;
}

// If no timestamp specified, use the current timestamp
if (local.params.timestamp == 0) {
local.params.timestamp = uint64(block.timestamp);
}

// Taiko, as a Based Rollup, enables permissionless block proposals.
local.b = _state.slotB;

Expand All @@ -86,9 +103,9 @@ library LibProposing {
revert L1_TOO_MANY_BLOCKS();
}

local.parentMetaHash =
_state.blocks[(local.b.numBlocks - 1) % _config.blockRingBufferSize].metaHash;
// assert(parentMetaHash != 0);
TaikoData.Block storage parentBlock =
_state.blocks[(local.b.numBlocks - 1) % _config.blockRingBufferSize];
local.parentMetaHash = parentBlock.metaHash;

// Check if parent block has the right meta hash. This is to allow the proposer to make sure
// the block builds on the expected latest chain state.
Expand All @@ -97,38 +114,62 @@ library LibProposing {
revert L1_UNEXPECTED_PARENT();
}

// Verify the passed in L1 state block number.
// We only allow the L1 block to be 4 epochs old.
// The other constraint is that the L1 block number needs to be larger than or equal the one
// in the previous L2 block.
if (
dantaik marked this conversation as resolved.
Show resolved Hide resolved
local.params.l1StateBlockNumber + 128 < block.number
|| local.params.l1StateBlockNumber >= block.number
|| local.params.l1StateBlockNumber < parentBlock.l1StateBlockNumber
) {
revert L1_INVALID_L1_STATE_BLOCK();
}

// Verify the passed in timestamp.
// We only allow the timestamp to be 4 epochs old.
// The other constraint is that the timestamp needs to be larger than or equal the one
// in the previous L2 block.
if (
local.params.timestamp + 128 * 12 < block.timestamp
|| local.params.timestamp > block.timestamp
|| local.params.timestamp < parentBlock.timestamp
) {
revert L1_INVALID_TIMESTAMP();
}

// Initialize metadata to compute a metaHash, which forms a part of
// the block data to be stored on-chain for future integrity checks.
// If we choose to persist all data fields in the metadata, it will
// require additional storage slots.
unchecked {
meta_ = TaikoData.BlockMetadata({
l1Hash: blockhash(block.number - 1),
l1Hash: blockhash(local.params.l1StateBlockNumber),
difficulty: 0, // to be initialized below
blobHash: 0, // to be initialized below
extraData: local.params.extraData,
depositsHash: _EMPTY_ETH_DEPOSIT_HASH,
coinbase: local.params.coinbase,
id: local.b.numBlocks,
gasLimit: _config.blockMaxGasLimit,
timestamp: uint64(block.timestamp),
l1Height: uint64(block.number - 1),
// Use the timestamp one block after the chosen L1 state block
timestamp: local.params.timestamp,
l1Height: local.params.l1StateBlockNumber,
minTier: 0, // to be initialized below
blobUsed: _txList.length == 0,
parentMetaHash: local.parentMetaHash,
sender: msg.sender
sender: msg.sender,
blobTxListOffset: local.params.blobTxListOffset,
blobTxListLength: local.params.blobTxListLength,
index: index
});
}

// Update certain meta fields
if (meta_.blobUsed) {
if (!LibNetwork.isDencunSupported(block.chainid)) revert L1_BLOB_NOT_AVAILABLE();
//if (!LibNetwork.isDencunSupported(block.chainid)) revert L1_BLOB_NOT_AVAILABLE();

// Always use the first blob in this transaction. If the
// proposeBlock functions are called more than once in the same
// L1 transaction, these multiple L2 blocks will share the same
// blob.
meta_.blobHash = blobhash(0);
meta_.blobHash = blobhash(local.params.blobIndex);
if (meta_.blobHash == 0) revert L1_BLOB_NOT_FOUND();
} else {
meta_.blobHash = keccak256(_txList);
Expand All @@ -137,30 +178,30 @@ library LibProposing {
// the node to extract the calldata easily.
// We cannot rely on `msg.sender != tx.origin` for EOA check, as it will break after EIP
// 7645: Alias ORIGIN to SENDER
if (
_config.checkEOAForCalldataDA
&& ECDSA.recover(meta_.blobHash, local.params.signature) != msg.sender
) {
revert L1_INVALID_SIG();
}
// if (
// _config.checkEOAForCalldataDA
// && ECDSA.recover(meta_.blobHash, local.params.signature) != msg.sender
// ) {
// revert L1_INVALID_SIG();
// }

emit CalldataTxList(meta_.id, _txList);
}

// Following the Merge, the L1 mixHash incorporates the
// prevrandao value from the beacon chain. Given the possibility
// of multiple Taiko blocks being proposed within a single
// Ethereum block, we choose to introduce a salt to this random
// number as the L2 mixHash.
meta_.difficulty =
keccak256(abi.encodePacked(block.prevrandao, local.b.numBlocks, block.number));
// Generate a random value from the L1 state block hash and the L2 block ID
meta_.difficulty = keccak256(
abi.encodePacked(blockhash(local.params.l1StateBlockNumber), local.b.numBlocks)
);

{
ITierRouter tierRouter = ITierRouter(_resolver.resolve(LibStrings.B_TIER_ROUTER, false));
ITierProvider tierProvider = ITierProvider(tierRouter.getProvider(local.b.numBlocks));
// ITierRouter tierRouter = ITierRouter(_resolver.resolve(LibStrings.B_TIER_ROUTER,
// false));
// ITierProvider tierProvider =
// ITierProvider(tierRouter.getProvider(local.b.numBlocks));

// Use the difficulty as a random number
meta_.minTier = tierProvider.getMinTier(uint256(meta_.difficulty));
// meta_.minTier = tierProvider.getMinTier(uint256(meta_.difficulty));
meta_.minTier = 100;
}

// Create the block that will be stored onchain
Expand All @@ -177,7 +218,9 @@ library LibProposing {
// For a new block, the next transition ID is always 1, not 0.
nextTransitionId: 1,
// For unverified block, its verifiedTransitionId is always 0.
verifiedTransitionId: 0
verifiedTransitionId: 0,
l1StateBlockNumber: local.params.l1StateBlockNumber,
timestamp: local.params.timestamp
});

// Store the block in the ring buffer
Expand All @@ -202,7 +245,10 @@ library LibProposing {
assignedProver: msg.sender,
livenessBond: _config.livenessBond,
meta: meta_,
depositsProcessed: deposits_
depositsProcessed: deposits_,
blobTxListOffset: local.params.blobTxListOffset,
blobTxListLength: local.params.blobTxListLength,
blobIndex: local.params.blobIndex
});
}
}
Loading