Context
Since #11219 the batcher can dynamically switch between calldata and blobs, depending on the economically best option at the time of starting a new channel. This works reasonably well for high-throughput chains, where the delay between starting and submitting a channel is usually only a few minutes. But it is still not optimal, as the most economical option might have changed since building up the channel. Furthermore, for low-throughput chains, this behavior is useless, as a da-type decision delay of multiple hours is ineffective. And lastly, the decision is currently made under the assumption that the channel will be full to its size limit. But the channel may not be full, e.g. using less frames (=blobs) with a half-empty last frame because of a short max-channel-duration setting that closed it (especially relevant on testnets).
Change Description
To address these shortcomings, the batcher should perform a second/the check at the time that it would start submitting a channel (i.e., when starting to post the first batcher transaction for that channel (almost always a channel fits into a single batcher transaction, unless the first block added to it doesn't fit into the target-num-frames frames)), and possibly rebuild the channel using different da-type settings.
The goal of this improvement is to always select the best da-type for a channel at the time of tx submission, and make the dynamic da-type selection feature useable for low-throughput chains.
Implementation Hints
It probably makes most sense to move the channel config retrieval via the ChannelConfigProvider from the point at which a new channel is created (
|
cfg := s.cfgProvider.ChannelConfig() |
) to the point at which the first frame of any channel is submitted (places where we return non-empty
txData inside
channelManager.TxData()), and then reuse that config for the following channel. This has two advantages.
- This adds the check for the correct channel config at the point when submission happens. Most of the time this check will confirm the current configuration.
- The channel config of the previous channel is the best estimator for the correct channel config of the following channel, when the exact data content isn't known yet.
Note that a special case is a fresh batcher restart, at which point an estimated configuration has to be retrieved once before the first channel. But this could happen in the batcher's initialization path.
Channel Rebuilding
If the check doesn't confirm the channel config that's currently in use, the channel should be rebuilt using the new config. One good option to accomplish this may be to reinsert the blocks into the L2 blocks queue inside the channelManager, so that the existing code path is then used to build the channel with the new channel config. This has the advantage that e.g. if switching from calldata to blobs, the channel would suddenly have more space, so it wouldn't be immediately submitted and just continue to fill up regularly.
ChannelConfigProvider
It's important to note that the current ChannelConfigProvider has a single method to retrieve the next estimated channel config, and the implementation of the dynamic provider always assumes full channels. This is not optimal for the proposed change, as we will know the exact channel data at the point of submission, so we can make an exact fee calculation. To enable this, I suggest we add a second method to the ChannelConfigProvider to make its interface look similar to the following:
type ChannelConfigProvider interface {
ChannelConfigFull() ChannelConfig // note: renamed old method that estimates full channels, would be used only at startup
ChannelConfig(data []byte) ChannelConfig // new method, takes exact data
}
The new method can take as a parameter the exact data and use core.IntrinsicGas to exactly calculate the calldata gas in the dynamic provider, instead of this manual calculation:
|
// It would be nicer to use core.IntrinsicGas, but we don't have the actual data at hand |
|
calldataBytes := dec.calldataConfig.MaxFrameSize + 1 // + 1 version byte |
|
calldataGas := big.NewInt(int64(calldataBytes*randomByteCalldataGas + params.TxGas)) |
Otherwise, the existing cost comparison code at DynamicEthChannelConfig.ChannelConfig() should be as much reused between the two new methods as possible.
Channel Config Immutability
One important rule that should be observed is to not change the DA-type and channel configuration once the first frame of a channel started being submitted. This is important a) because otherwise we might end up trying to replace a transaction with a different tx type, which is not allowed and would get the batcher into a blocked tx recovery mode and b) in view of the upcoming Holocene features regarding Strict Batch Ordering would invalidate future batcher transactions that might already be in the tx-pool because of parallel transaction sending.
Hardening the batcher's behavior during restarts should be done as a separate task, possibly introducing some form of persistence of in-flight transactions.
Context
Since #11219 the batcher can dynamically switch between calldata and blobs, depending on the economically best option at the time of starting a new channel. This works reasonably well for high-throughput chains, where the delay between starting and submitting a channel is usually only a few minutes. But it is still not optimal, as the most economical option might have changed since building up the channel. Furthermore, for low-throughput chains, this behavior is useless, as a da-type decision delay of multiple hours is ineffective. And lastly, the decision is currently made under the assumption that the channel will be full to its size limit. But the channel may not be full, e.g. using less frames (=blobs) with a half-empty last frame because of a short max-channel-duration setting that closed it (especially relevant on testnets).
Change Description
To address these shortcomings, the batcher should perform a second/the check at the time that it would start submitting a channel (i.e., when starting to post the first batcher transaction for that channel (almost always a channel fits into a single batcher transaction, unless the first block added to it doesn't fit into the
target-num-framesframes)), and possibly rebuild the channel using different da-type settings.The goal of this improvement is to always select the best da-type for a channel at the time of tx submission, and make the dynamic da-type selection feature useable for low-throughput chains.
Implementation Hints
It probably makes most sense to move the channel config retrieval via the
ChannelConfigProviderfrom the point at which a new channel is created (optimism/op-batcher/batcher/channel_manager.go
Line 206 in a36291d
txDatainsidechannelManager.TxData()), and then reuse that config for the following channel. This has two advantages.Note that a special case is a fresh batcher restart, at which point an estimated configuration has to be retrieved once before the first channel. But this could happen in the batcher's initialization path.
Channel Rebuilding
If the check doesn't confirm the channel config that's currently in use, the channel should be rebuilt using the new config. One good option to accomplish this may be to reinsert the blocks into the L2 blocks queue inside the
channelManager, so that the existing code path is then used to build the channel with the new channel config. This has the advantage that e.g. if switching from calldata to blobs, the channel would suddenly have more space, so it wouldn't be immediately submitted and just continue to fill up regularly.ChannelConfigProviderIt's important to note that the current
ChannelConfigProviderhas a single method to retrieve the next estimated channel config, and the implementation of the dynamic provider always assumes full channels. This is not optimal for the proposed change, as we will know the exact channel data at the point of submission, so we can make an exact fee calculation. To enable this, I suggest we add a second method to theChannelConfigProviderto make its interface look similar to the following:The new method can take as a parameter the exact data and use
core.IntrinsicGasto exactly calculate the calldata gas in the dynamic provider, instead of this manual calculation:optimism/op-batcher/batcher/channel_config_provider.go
Lines 66 to 68 in a36291d
Otherwise, the existing cost comparison code at
DynamicEthChannelConfig.ChannelConfig()should be as much reused between the two new methods as possible.Channel Config Immutability
One important rule that should be observed is to not change the DA-type and channel configuration once the first frame of a channel started being submitted. This is important a) because otherwise we might end up trying to replace a transaction with a different tx type, which is not allowed and would get the batcher into a blocked tx recovery mode and b) in view of the upcoming Holocene features regarding Strict Batch Ordering would invalidate future batcher transactions that might already be in the tx-pool because of parallel transaction sending.
Hardening the batcher's behavior during restarts should be done as a separate task, possibly introducing some form of persistence of in-flight transactions.