Sequencer Registry Protocol
Decentralized sequencer registry with stake-based selection and rotation mechanisms
Abstract
This LP defines a decentralized sequencer registry protocol for L2 rollups on Lux Network, implementing stake-based selection, rotation mechanisms, and performance monitoring. The system enables permissionless sequencer participation with economic incentives, supports multiple sequencer selection strategies including round-robin, weighted random, and auction-based models, and provides specialized mechanisms for AI workload scheduling and distributed training coordination. The protocol ensures liveness, censorship resistance, and fair transaction ordering through cryptographic commitments and threshold decryption.
Activation
| Parameter | Value |
|---|---|
| Flag string | lp504-sequencer-registry |
| Default in code | false until block X |
| Deployment branch | v0.0.0-lp504 |
| Roll‑out criteria | 10+ active sequencers |
| Back‑off plan | Fallback to L1 sequencing |
Motivation
Centralized sequencers present critical risks:
- Single Point of Failure: Downtime affects entire L2
- Censorship Risk: Can exclude transactions arbitrarily
- MEV Extraction: Unfair value extraction from users
- Trust Requirements: Users must trust sequencer operator
- Limited Scalability: Single sequencer bottlenecks throughput
Decentralized sequencing enables:
- Liveness Guarantees: Multiple sequencers ensure availability
- Censorship Resistance: Transactions eventually included
- Fair Ordering: Cryptographic mechanisms prevent manipulation
- Horizontal Scaling: Multiple sequencers increase throughput
- Specialized Scheduling: AI workloads get appropriate resources
Specification
Core Registry Interface
interface ISequencerRegistry {
enum SequencerStatus {
Inactive,
Active,
Suspended,
Ejected,
Exiting
}
enum SelectionMode {
RoundRobin,
WeightedRandom,
Auction,
Performance,
Hybrid
}
struct SequencerInfo {
address operator;
string endpoint; // P2P endpoint
bytes publicKey; // For encrypted mempool
uint256 stake;
uint256 delegatedStake;
SequencerStatus status;
uint256 activationBlock;
uint256 lastActiveBlock;
uint256 performanceScore;
bytes metadata; // Additional config
}
struct RegistryConfig {
uint256 minStake;
uint256 maxSequencers;
uint256 rotationPeriod; // Blocks per rotation
uint256 exitDelay; // Delay before stake withdrawal
SelectionMode selectionMode;
uint256 slashingPercentage;
}
// Registration and staking
function registerSequencer(
string calldata endpoint,
bytes calldata publicKey,
bytes calldata metadata
) external payable returns (uint256 sequencerId);
function increaseStake(uint256 sequencerId) external payable;
function delegateStake(uint256 sequencerId) external payable;
function initiateExit(uint256 sequencerId) external;
function completeExit(uint256 sequencerId) external;
// Selection and rotation
function selectNextSequencer() external returns (address);
function rotateSequencer() external;
function getActiveSequencer() external view returns (address);
function getSequencerSet() external view returns (address[] memory);
// Performance and slashing
function updatePerformance(uint256 sequencerId, uint256 score) external;
function reportMisbehavior(uint256 sequencerId, bytes calldata evidence) external;
function slash(uint256 sequencerId, uint256 amount) external;
// Events
event SequencerRegistered(uint256 indexed sequencerId, address operator);
event SequencerRotated(address indexed oldSequencer, address indexed newSequencer);
event SequencerSlashed(uint256 indexed sequencerId, uint256 amount);
event StakeDelegated(uint256 indexed sequencerId, address delegator, uint256 amount);
}
Selection Mechanisms
interface ISequencerSelection {
struct RoundRobinState {
uint256 currentIndex;
address[] sequencers;
uint256 lastRotation;
}
struct WeightedRandomState {
uint256 totalStake;
uint256 seed;
mapping(address => uint256) weights;
}
struct AuctionState {
uint256 currentRound;
address highestBidder;
uint256 highestBid;
uint256 auctionEnd;
mapping(address => uint256) bids;
}
struct PerformanceBasedState {
mapping(address => uint256) scores;
uint256 scoreDecayRate;
uint256 minScore;
}
// Round-robin selection
function selectRoundRobin(
RoundRobinState storage state
) external returns (address);
// Weighted random selection
function selectWeightedRandom(
WeightedRandomState storage state,
uint256 blockHash
) external returns (address);
// Auction-based selection
function initAuction(
uint256 duration
) external returns (uint256 auctionId);
function placeBid(
uint256 auctionId
) external payable;
function finalizeAuction(
uint256 auctionId
) external returns (address winner);
// Performance-based selection
function selectByPerformance(
PerformanceBasedState storage state
) external returns (address);
// Hybrid selection (combines multiple methods)
function selectHybrid(
uint256[] calldata weights, // Weight for each method
bytes calldata params
) external returns (address);
// VRF for randomness
function requestRandomness() external returns (uint256 requestId);
function fulfillRandomness(uint256 requestId, uint256 randomness) external;
}
MEV Mitigation
interface IMEVMitigation {
struct EncryptedTransaction {
bytes encryptedData;
bytes32 commitment; // Hash of plaintext
uint256 revealDeadline;
address sender;
}
struct ThresholdDecryption {
uint256 threshold; // Min validators for decryption
address[] validators;
mapping(address => bytes) keyShares;
}
struct TimelockedTransaction {
bytes32 txHash;
uint256 unlockTime;
bytes encryptedTx;
bytes timeProof; // VDF proof
}
// Threshold encryption for mempool
function submitEncrypted(
bytes calldata encryptedTx,
bytes32 commitment
) external returns (bytes32 txId);
function revealTransaction(
bytes32 txId,
bytes calldata plaintext,
uint256 nonce
) external;
// Threshold decryption
function initThresholdDecryption(
uint256 threshold,
address[] calldata validators
) external;
function submitKeyShare(
bytes32 txId,
bytes calldata keyShare
) external;
function decryptTransaction(
bytes32 txId
) external returns (bytes memory);
// Time-locked transactions
function submitTimelocked(
bytes calldata encryptedTx,
uint256 lockDuration
) external returns (bytes32);
function proveTimeElapsed(
bytes32 txId,
bytes calldata vdfProof
) external;
// Fair ordering
function determineFairOrder(
bytes32[] calldata txHashes,
uint256 blockNumber
) external pure returns (bytes32[] memory);
// Events
event EncryptedTxSubmitted(bytes32 indexed txId, address sender);
event TransactionRevealed(bytes32 indexed txId);
event ThresholdDecrypted(bytes32 indexed txId);
}
AI Workload Scheduling
interface IAIWorkloadScheduler {
struct ComputeJob {
bytes32 jobId;
uint256 requiredFlops;
uint256 memoryRequirement;
uint256 deadline;
uint256 priority;
address requester;
bytes jobSpec;
}
struct SequencerCapabilities {
uint256 sequencerId;
uint256 availableFlops;
uint256 availableMemory;
uint256 networkBandwidth;
string[] supportedModels;
bool hasTEE;
bool hasGPU;
}
struct SchedulingPolicy {
enum PolicyType { FIFO, Priority, SJF, RoundRobin, Fair }
PolicyType policyType;
bytes policyParams;
}
// Job submission
function submitComputeJob(
ComputeJob calldata job
) external returns (bytes32);
function cancelJob(bytes32 jobId) external;
// Sequencer capabilities
function registerCapabilities(
uint256 sequencerId,
SequencerCapabilities calldata capabilities
) external;
function updateCapabilities(
uint256 sequencerId,
SequencerCapabilities calldata capabilities
) external;
// Scheduling
function scheduleJob(
bytes32 jobId,
uint256 sequencerId
) external returns (bool);
function getOptimalSequencer(
ComputeJob calldata job
) external view returns (uint256);
function rebalanceWorkload() external;
// Load balancing
function getSequencerLoad(uint256 sequencerId) external view returns (uint256);
function migrateJob(bytes32 jobId, uint256 newSequencerId) external;
// Priority management
function boostPriority(bytes32 jobId, uint256 amount) external payable;
function preemptJob(bytes32 lowPriorityJob, bytes32 highPriorityJob) external;
// Events
event JobScheduled(bytes32 indexed jobId, uint256 sequencerId);
event JobCompleted(bytes32 indexed jobId, uint256 gasUsed);
event WorkloadRebalanced(uint256 numMigrations);
}
Distributed Training Coordination
interface ITrainingCoordination {
struct TrainingCluster {
bytes32 clusterId;
uint256[] sequencerIds;
address coordinator;
bytes32 modelHash;
uint256 totalEpochs;
uint256 currentEpoch;
}
struct DataShard {
bytes32 shardId;
uint256 sequencerId;
bytes32 dataHash;
uint256 numSamples;
uint256 startIndex;
uint256 endIndex;
}
struct GradientUpdate {
bytes32 clusterId;
uint256 sequencerId;
uint256 epoch;
bytes32 gradientHash;
uint256 loss;
uint256 timestamp;
}
// Cluster management
function createTrainingCluster(
uint256[] calldata sequencerIds,
bytes32 modelHash
) external returns (bytes32 clusterId);
function joinCluster(bytes32 clusterId, uint256 sequencerId) external;
function leaveCluster(bytes32 clusterId, uint256 sequencerId) external;
// Data distribution
function assignDataShard(
bytes32 clusterId,
uint256 sequencerId,
DataShard calldata shard
) external;
function replicateShard(
bytes32 shardId,
uint256 targetSequencer
) external;
// Synchronization
function submitGradient(
GradientUpdate calldata update
) external;
function aggregateGradients(
bytes32 clusterId,
uint256 epoch
) external returns (bytes32 aggregatedHash);
function broadcastWeights(
bytes32 clusterId,
bytes32 weightsHash
) external;
// Fault tolerance
function reportFailure(
bytes32 clusterId,
uint256 failedSequencer
) external;
function reassignWork(
bytes32 clusterId,
uint256 failedSequencer,
uint256 newSequencer
) external;
// Checkpointing
function saveCheckpoint(
bytes32 clusterId,
uint256 epoch,
bytes32 stateHash
) external;
function restoreFromCheckpoint(
bytes32 clusterId,
uint256 checkpointEpoch
) external;
// Events
event ClusterCreated(bytes32 indexed clusterId, uint256 numSequencers);
event GradientSubmitted(bytes32 indexed clusterId, uint256 epoch, uint256 sequencerId);
event WeightsBroadcast(bytes32 indexed clusterId, bytes32 weightsHash);
}
Performance Monitoring
interface IPerformanceMonitor {
struct PerformanceMetrics {
uint256 blocksProduced;
uint256 transactionsProcessed;
uint256 avgBlockTime;
uint256 avgGasUsed;
uint256 uptime; // Percentage (basis points)
uint256 missedSlots;
uint256 reorgCount;
uint256 latency; // Average in ms
}
struct QualityScore {
uint256 throughput; // TPS
uint256 availability; // Uptime percentage
uint256 correctness; // Valid blocks percentage
uint256 timeliness; // On-time block production
uint256 overall; // Weighted average
}
struct SLAConfig {
uint256 minThroughput; // Minimum TPS
uint256 minUptime; // Minimum uptime (basis points)
uint256 maxLatency; // Maximum latency (ms)
uint256 maxMissedSlots; // Per epoch
}
// Metrics collection
function recordBlockProduction(
uint256 sequencerId,
uint256 blockNumber,
uint256 gasUsed,
uint256 txCount
) external;
function recordMissedSlot(uint256 sequencerId, uint256 slot) external;
function recordReorg(uint256 sequencerId, uint256 depth) external;
// Score calculation
function calculateQualityScore(
uint256 sequencerId
) external view returns (QualityScore memory);
function getPerformanceMetrics(
uint256 sequencerId,
uint256 period
) external view returns (PerformanceMetrics memory);
// SLA enforcement
function checkSLACompliance(
uint256 sequencerId,
SLAConfig calldata sla
) external view returns (bool compliant, bytes memory violations);
function penalizeSLAViolation(
uint256 sequencerId,
bytes calldata violations
) external;
// Reputation system
function updateReputation(uint256 sequencerId, int256 change) external;
function getReputation(uint256 sequencerId) external view returns (uint256);
// Events
event MetricsRecorded(uint256 indexed sequencerId, uint256 blockNumber);
event SLAViolation(uint256 indexed sequencerId, bytes violations);
event ReputationUpdated(uint256 indexed sequencerId, uint256 newScore);
}
Rationale
Selection Mechanism Design
Multiple selection modes for different requirements:
- Round-Robin: Simple, predictable, fair distribution
- Weighted Random: Stake-proportional, Sybil-resistant
- Auction: Market-driven, revenue-generating
- Performance: Merit-based, incentivizes quality
- Hybrid: Combines benefits of multiple approaches
MEV Mitigation Strategies
Following Flashbots and Chainlink FSS research:
- Threshold Encryption: Transactions hidden until ordering determined
- Time-lock Puzzles: VDF-based commit-reveal schemes
- Fair Ordering: Deterministic ordering via consensus
AI Workload Optimization
Specialized scheduling for AI workloads:
- Resource Matching: Match jobs to capable sequencers
- Priority Scheduling: Deadline-aware scheduling
- Load Balancing: Distribute work across cluster
- Fault Tolerance: Automatic failover and recovery
Backwards Compatibility
Compatible with existing L2 designs:
- Optional decentralization (can start centralized)
- Gradual migration path
- Fallback to L1 sequencing if needed
- Standard interfaces for integration
Test Cases
Registry Operations
describe("Sequencer Registry", () => {
it("should register sequencer with stake", async () => {
const stake = ethers.parseEther("100000"); // 100k LUX
await registry.registerSequencer(
"enode://[email protected]:30303",
publicKey,
metadata,
{ value: stake }
);
const info = await registry.getSequencer(1);
expect(info.stake).to.equal(stake);
expect(info.status).to.equal(SequencerStatus.Active);
});
it("should rotate sequencers based on period", async () => {
// Register multiple sequencers
for(let i = 0; i < 5; i++) {
await registerSequencer(accounts[i]);
}
const initialSequencer = await registry.getActiveSequencer();
// Advance time
await time.increase(rotationPeriod);
await registry.rotateSequencer();
const newSequencer = await registry.getActiveSequencer();
expect(newSequencer).to.not.equal(initialSequencer);
});
it("should slash misbehaving sequencer", async () => {
const sequencerId = 1;
const evidence = generateFraudProof();
await registry.reportMisbehavior(sequencerId, evidence);
await registry.slash(sequencerId, ethers.parseEther("10000"));
const info = await registry.getSequencer(sequencerId);
expect(info.status).to.equal(SequencerStatus.Suspended);
expect(info.stake).to.be.lt(initialStake);
});
});
Selection Mechanism Tests
describe("Sequencer Selection", () => {
it("should select via weighted random", async () => {
// Register sequencers with different stakes
const stakes = [100, 200, 300, 400]; // Different weights
for(let i = 0; i < stakes.length; i++) {
await registry.registerSequencer(
endpoint,
publicKey,
metadata,
{ value: ethers.parseEther(stakes[i].toString()) }
);
}
// Run selection many times to verify distribution
const selections = {};
for(let i = 0; i < 1000; i++) {
const selected = await selection.selectWeightedRandom(state, blockHash);
selections[selected] = (selections[selected] || 0) + 1;
}
// Verify roughly proportional to stakes
expect(selections[accounts[3]] / selections[accounts[0]])
.to.be.closeTo(4, 0.5);
});
it("should run sequencer auction", async () => {
const auctionId = await selection.initAuction(3600); // 1 hour
// Place bids
await selection.placeBid(auctionId, { value: ethers.parseEther("100") });
await selection.connect(accounts[1]).placeBid(
auctionId,
{ value: ethers.parseEther("150") }
);
await time.increase(3601);
const winner = await selection.finalizeAuction(auctionId);
expect(winner).to.equal(accounts[1].address);
});
});
MEV Mitigation Tests
describe("MEV Mitigation", () => {
it("should handle threshold encrypted transactions", async () => {
const tx = createTransaction();
const encrypted = encrypt(tx, thresholdKey);
const commitment = keccak256(tx);
const txId = await mev.submitEncrypted(encrypted, commitment);
// Validators submit key shares
for(let i = 0; i < threshold; i++) {
await mev.connect(validators[i]).submitKeyShare(
txId,
keyShares[i]
);
}
// Decrypt and verify
const decrypted = await mev.decryptTransaction(txId);
expect(keccak256(decrypted)).to.equal(commitment);
});
it("should enforce fair ordering", async () => {
const txHashes = [
"0xaaa...",
"0xbbb...",
"0xccc..."
];
const fairOrder = await mev.determineFairOrder(txHashes, blockNumber);
// Verify deterministic ordering
const fairOrder2 = await mev.determineFairOrder(txHashes, blockNumber);
expect(fairOrder).to.deep.equal(fairOrder2);
});
});
AI Workload Tests
describe("AI Workload Scheduling", () => {
it("should schedule job to optimal sequencer", async () => {
// Register sequencers with different capabilities
await scheduler.registerCapabilities(1, {
availableFlops: ethers.parseUnits("100", 12), // 100 TFLOPS
availableMemory: 128 * 1024 * 1024 * 1024, // 128 GB
hasGPU: true,
hasTEE: false
});
await scheduler.registerCapabilities(2, {
availableFlops: ethers.parseUnits("50", 12),
availableMemory: 64 * 1024 * 1024 * 1024,
hasGPU: false,
hasTEE: true
});
const job = {
requiredFlops: ethers.parseUnits("80", 12),
memoryRequirement: 100 * 1024 * 1024 * 1024,
deadline: Date.now() + 3600,
priority: 10
};
const jobId = await scheduler.submitComputeJob(job);
const sequencer = await scheduler.getOptimalSequencer(job);
expect(sequencer).to.equal(1); // First sequencer has enough resources
});
it("should rebalance workload", async () => {
// Submit multiple jobs
for(let i = 0; i < 10; i++) {
await submitJob(i);
}
// Check initial distribution
const load1 = await scheduler.getSequencerLoad(1);
const load2 = await scheduler.getSequencerLoad(2);
// Trigger rebalance
await scheduler.rebalanceWorkload();
// Verify more even distribution
const newLoad1 = await scheduler.getSequencerLoad(1);
const newLoad2 = await scheduler.getSequencerLoad(2);
expect(Math.abs(newLoad1 - newLoad2)).to.be.lt(Math.abs(load1 - load2));
});
});
Distributed Training Tests
describe("Distributed Training", () => {
it("should coordinate training cluster", async () => {
const sequencerIds = [1, 2, 3, 4, 5];
const modelHash = keccak256("bert-large");
const clusterId = await training.createTrainingCluster(
sequencerIds,
modelHash
);
// Assign data shards
for(let i = 0; i < 5; i++) {
await training.assignDataShard(clusterId, sequencerIds[i], {
shardId: keccak256(`shard${i}`),
sequencerId: sequencerIds[i],
dataHash: keccak256(`data${i}`),
numSamples: 10000,
startIndex: i * 10000,
endIndex: (i + 1) * 10000
});
}
// Submit gradients
for(let i = 0; i < 5; i++) {
await training.submitGradient({
clusterId,
sequencerId: sequencerIds[i],
epoch: 1,
gradientHash: keccak256(`gradient${i}`),
loss: 100 - i * 10,
timestamp: Date.now()
});
}
// Aggregate gradients
const aggregated = await training.aggregateGradients(clusterId, 1);
expect(aggregated).to.not.equal(ZERO_HASH);
// Broadcast new weights
await training.broadcastWeights(clusterId, aggregated);
});
it("should handle sequencer failure", async () => {
const clusterId = await createCluster();
const failedSequencer = 3;
await training.reportFailure(clusterId, failedSequencer);
await training.reassignWork(clusterId, failedSequencer, 6);
const cluster = await training.getCluster(clusterId);
expect(cluster.sequencerIds).to.not.include(failedSequencer);
expect(cluster.sequencerIds).to.include(6);
});
});
Reference Implementation
Available at:
- https://github.com/luxfi/sequencer-registry
- https://github.com/luxfi/mev-mitigation
- https://github.com/luxfi/ai-scheduler
Key components:
contracts/SequencerRegistry.sol: Main registry contractcontracts/SelectionMechanisms.sol: Various selection strategiescontracts/MEVMitigation.sol: Anti-MEV mechanismscontracts/AIScheduler.sol: Workload schedulingp2p/: Sequencer P2P network implementation
Security Considerations
Sybil Resistance
- Minimum Stake: High barrier to entry (100k LUX)
- Reputation System: Long-term performance tracking
- Identity Verification: Optional KYC for operators
- Network Diversity: Geographic and infrastructure requirements
Liveness
- Multiple Sequencers: No single point of failure
- Automatic Failover: Quick replacement of failed sequencers
- L1 Fallback: Users can submit directly to L1
- Timeout Mechanisms: Automatic rotation on inactivity
Censorship Resistance
- Rotation: Regular sequencer changes
- Forced Inclusion: L1 can force transaction inclusion
- Encrypted Mempool: Sequencers can't discriminate
- Slashing: Penalties for censorship
Economic Security
- Stake at Risk: Misbehavior leads to slashing
- Delegation Limits: Cap on delegated stake
- Gradual Exits: Time delay for stake withdrawal
- Insurance Fund: Coverage for user losses
Economic Impact
Revenue Streams
- Sequencer Fees: 0.01-0.1% of transaction value
- Priority Fees: Users pay for faster inclusion
- MEV Auctions: Sequencer slot auctions
- Compute Premiums: Higher fees for AI workloads
Cost Structure
- Infrastructure: $10,000/month per sequencer
- Stake Opportunity Cost: 5-10% APY
- Insurance: 1% of revenue to insurance fund
- Development: Ongoing protocol improvements
Market Dynamics
- Competition: Multiple sequencers compete on quality
- Specialization: Some focus on AI, others on DeFi
- Economies of Scale: Larger operators more efficient
- Decentralization Incentives: Bonuses for geographic diversity
Open Questions
- Optimal Rotation Period: Balance between stability and decentralization
- Stake Requirements: Minimum stake vs accessibility
- Cross-L2 Sequencing: Shared sequencer sets
- Regulatory Compliance: KYC/AML for sequencer operators
References
- Kelkar, M., et al. (2021). "Order-Fairness for Byzantine Consensus"
- Flashbots (2021). "MEV-SGX: A Sealed-Bid MEV Auction Design"
- Chainlink (2020). "Fair Sequencing Services: Enabling a Provably Fair DeFi Ecosystem"
- Boneh, D., et al. (2018). "Verifiable Delay Functions"
- Buterin, V. (2021). "Proposer/Builder Separation"
- Espresso Systems (2022). "HotShot: BFT Consensus Designed for Rollups"
- Radius (2023). "Shared Sequencing Layer"
- Astria (2023). "Decentralized Sequencer Network"
Implementation
Status: Specification stage - implementation planned for future release
Planned Locations:
- Sequencer registry:
~/work/lux/sequencer-registry/(to be created) - Selection mechanisms:
~/work/lux/sequencer-registry/selection/ - MEV mitigation:
~/work/lux/sequencer-registry/mev/ - AI scheduling:
~/work/lux/sequencer-registry/scheduler/ - Contracts:
~/work/lux/standard/src/sequencer/
Build on Existing Infrastructure:
- P-Chain validator set management (reference implementation)
- Warp messenger for cross-sequencer communication
- Existing staking mechanisms from Platform VM
- Q-Chain for post-quantum signature validation
Core Components:
-
Registry Smart Contract (~800 LOC Solidity)
- Sequencer registration and staking
- Rotation scheduling
- Performance tracking
-
Selection Engine (~1200 LOC Go)
- Round-robin scheduling
- Weighted random selection (VRF-based)
- Auction mechanism
- Performance-based selection
-
MEV Mitigation (~1500 LOC Go)
- Threshold encryption for transactions
- Time-locked transaction handling
- Fair ordering determinism
-
AI Workload Scheduler (~1300 LOC Go)
- Compute requirement matching
- Load balancing
- Priority queue management
- Fault tolerance and migration
-
Distributed Training Coordinator (~1000 LOC Go)
- Cluster formation and management
- Data shard distribution
- Gradient aggregation
- Checkpoint management
Integration Points:
- P-Chain: Validator set queries and epoch-based selection
- Platform VM: Staking and delegation mechanisms
- Warp: Cross-chain sequencer coordination
- EVM: Precompile interface for dApps
Testing Strategy:
- Selection mechanism distribution testing (statistical analysis)
- MEV mitigation game-theoretic verification
- Load balancing under various workload distributions
- Byzantine sequencer behavior resistance
Performance Targets:
- Block proposal latency: < 500ms
- Selection decision time: < 50ms
- MEV protection overhead: < 5%
- Scheduler decision time: < 100ms
Deployment Phases:
- Phase 1: Basic registry + round-robin selection
- Phase 2: Weighted random + auction mechanisms
- Phase 3: MEV mitigation (threshold encryption)
- Phase 4: AI workload scheduling
- Phase 5: Distributed training coordination
- Phase 6: Cross-rollup sequencer coordination
Copyright
Copyright and related rights waived via CC0.```