BlockVectra

ArbOS L1 计价历史数据集(arbos_l1_pricing / daily_l1_base_fee_stats)

来源:schema/clickhouse/derived/022_arbos_l1_pricing.sql。 面向内部用 SQL 查询 Arbitrum Orbit L1 计价状态、L1 区块对齐与跨链批量发布历史的团队。

This content is sourced from upstream and is currently available in Chinese only.

来源:schema/clickhouse/derived/022_arbos_l1_pricing.sql。 面向内部用 SQL 查询 Arbitrum Orbit L1 计价状态、L1 区块对齐与跨链批量发布历史的团队。


1. 概述与背景原理

1.1 Arbitrum Nitro / ArbOS 内部交易(startBlock)

在 Arbitrum Orbit 架构中,ArbOS 会在每个 L2 区块的最前端(tx_index = 0)自动注入一笔类型为 0x6a(十进制 106,ArbitrumInternalTx)的系统内部交易。该交易的发送方(from)和接收方(to)均为 ArbOS 预编译合约地址:

  • 地址:0x00000000000000000000000000000000000A4B05(即 0x...A4B05,十六进制中字符 A4B05 谐音 ARBOS)。
  • 函数选择器:0x6bf6a42d。

1.2 Nitro 源码签名核验

经直接对照 Offchain Labs 官方 Nitro 核心源码(precompiles/ArbosActs.go 与 arbos/internal_tx.go),确认该交易调用的标准函数签名与参数顺序:

// precompiles/ArbosActs.go
func (con ArbosActs) StartBlock(c ctx, evm mech, l1BaseFee huge, l1BlockNumber, l2BlockNumber, timeLastBlock uint64) error {
    return con.CallerNotArbOSError()
}

// arbos/internal_tx.go
func InternalTxStartBlock(chainId, l1BaseFee *big.Int, l1BlockNum uint64, header, lastHeader *types.Header) *types.ArbitrumInternalTx {
    l2BlockNum := header.Number.Uint64()
    timePassed := header.Time - lastHeader.Time
    if l1BaseFee == nil {
        l1BaseFee = big.NewInt(0)
    }
    data, err := util.PackInternalTxDataStartBlock(l1BaseFee, l1BlockNum, l2BlockNum, timePassed)
    ...
}

标准规范 ABI 签名:

startBlock(uint256,uint64,uint64,uint64)

Keccak-256 哈希计算:keccak256("startBlock(uint256,uint64,uint64,uint64)")[:4] == 0x6bf6a42d,与链上实测选择器完全一致。

1.3 Calldata 32 字节字排布与解码逻辑

该交易的 input 长度恒为 132 字节(4 字节选择器 + 4 个 32 字节 ABI 编码字):

偏移区间(字节)ABI 类型字段名含义说明
[0..4) (4B)bytes4selector固定为 0x6bf6a42d
[4..36) (32B)uint256l1_base_feeArbOS 评估的 L1 父链 calldata base fee(单位:wei)
[36..68) (32B)uint64l1_block_number此时对齐的 L1 父链区块号(与 blocks.l1_block_number 一致)
[68..100) (32B)uint64l2_block_numberL2 区块号(与 block_number 完全一致)
[100..132) (32B)uint64time_passed距离上一个区块经过的秒数(header.Time - lastHeader.Time)

ClickHouse SQL 解码实现: EVM ABI 采用大端序(big-endian),而 ClickHouse 的 reinterpretAsUInt256 / reinterpretAsUInt64 采用小端序(little-endian)。因此在 ClickHouse 中解码 32 字节字必须结合 reverse():

  • l1_base_fee: reinterpretAsUInt256(reverse(substring(input, 5, 32)))
  • l1_block_number: toUInt64(reinterpretAsUInt256(reverse(substring(input, 37, 32))))
  • time_passed: toUInt64(reinterpretAsUInt256(reverse(substring(input, 101, 32))))

2. 表结构

2.1 arbos_l1_pricing(逐块表)

CREATE TABLE IF NOT EXISTS {db}.arbos_l1_pricing
(
    block_number     UInt64 CODEC(Delta, ZSTD),
    block_timestamp  DateTime('UTC') CODEC(Delta, ZSTD),
    l1_base_fee      UInt256,
    l1_block_number  UInt64,
    time_passed      UInt64,
    version          UInt64,
    is_deleted       UInt8
)
ENGINE = ReplacingMergeTree(version, is_deleted)
PARTITION BY intDiv(block_number, 5000000)
ORDER BY (block_number);

2.2 daily_l1_base_fee_stats(每日收盘聚合表)

CREATE TABLE IF NOT EXISTS {db}.daily_l1_base_fee_stats
(
    date                 Date,
    block_count          UInt64,
    min_l1_base_fee      UInt256,
    max_l1_base_fee      UInt256,
    avg_l1_base_fee      UInt256,
    median_l1_base_fee   UInt256,
    non_zero_blocks      UInt64,
    version              UInt64 DEFAULT 1,
    is_deleted           UInt8 DEFAULT 0
)
ENGINE = ReplacingMergeTree(version, is_deleted)
ORDER BY (date);

3. CORRECTNESS RULE 正确性与安全机制

3.1 arbos_l1_pricing: 方案 (a)

  • 1:1 结构映射:Arbitrum Orbit 上每个区块必定且仅有一笔 tx_index = 0 的 startBlock 交易,源表 {db}.transactions 与目标表行数严格 1:1。
  • 幂等与重组免疫:物化视图 mv_arbos_l1_pricing 直接透传源表的 version 与 is_deleted。
    • 断点续跑导致重复插入时,同版本多行在 FINAL 上折叠。
    • 发生链重组写下墓碑(is_deleted = 1、更高版本)时,{db}.arbos_l1_pricing FINAL WHERE is_deleted = 0 会精确同步剔除回滚区块。
    • 重组后新区块再入库,更高版本的新行在 FINAL 上胜出。

3.2 daily_l1_base_fee_stats: 方案 (b)

  • 收盘日刷新机制:聚合表使用可刷新物化视图(REFRESH EVERY 1 DAY OFFSET 1 HOUR RANDOMIZE FOR 10 MINUTE),仅对已完全收盘的自然日(toDate(block_timestamp) < today())从 {db}.arbos_l1_pricing FINAL 整体重算。
  • 整表原子替换:ClickHouse TO 目标表的可刷新物化视图在每次调度时原子替换目标分区/数据,不采用增量累加,彻底根除重复插入或重组墓碑引发的双重计数风险。

3.3 货币与数度安全性(Money Safety)

  • 涉及金额与费用的字段(l1_base_fee、min_l1_base_fee、max_l1_base_fee、avg_l1_base_fee、median_l1_base_fee)全程采用 UInt256 整数。
  • avg_l1_base_fee 采用整数整除 intDiv(sum(l1_base_fee), toUInt256(count())),严禁任何 Float64 浮点参与。
  • 中位数采用精确分位数计算 assumeNotNull(quantileExact(0.5)(l1_base_fee))。

4. 验证与核验结果

4.1 独立 Python ABI 解码对比(5 个样本区块精确核验)

通过独立 Python 环境(eth_abi.abi.decode)直接解码 RPC 原始 input,与 ClickHouse SQL 抽取字段逐字节比对:

区块高度 (block_number)内部交易哈希 (tx_hash)Python 解码 (l1_base_fee, l1_block_number, time_passed)ClickHouse SQL 解码 (l1_base_fee, l1_block_number, time_passed)匹配结果
720390000x2e09dd46b35273270a4187f36ccae74d3d2c719494c4172f1a65a9a97c917c0f(0, 26052892, 1)(0, 26052892, 1)EXACT MATCH
720393220xae958959b4635c7903036ba2c876bdbdeee3dc748c8a32e2dc0ff3a3f1cf8ba9(899928553, 26052831, 0)(899928553, 26052831, 0)EXACT MATCH
720393240x9ba2b4639a83f8755eaf3f142e8c0ed49147c5c00f8cc52c24593d67c28e4980(777241581, 26052834, 0)(777241581, 26052834, 0)EXACT MATCH
720393270xe5194dd81baef05f23d0ce0f3d2119090ab7cf89ca319ad894767ec71306fb8a(900392014, 26052844, 0)(900392014, 26052844, 0)EXACT MATCH
720393310x7280e21d536a0e68586510d9fc81446d7c4ab615dd03d5badcaa12633450a942(413370186, 26052858, 0)(413370186, 26052858, 0)EXACT MATCH

所有 5 笔交易的 from 与 to 均为 0x00000000000000000000000000000000000a4b05,类型均为 0x6a(106)。

4.2 ArbGasInfo 预编译合约交叉核验

  • 预编译地址:0x000000000000000000000000000000000000006c(ArbGasInfo)
  • 核验方法:getL1BaseFeeEstimate(),函数选择器 0xf5d6ded7。
  • 调用结果:在节点链头高度(区块 72151045)发起 eth_call:
    • 返回结果:0x0000000000000000000000000000000000000000000000000000000000000000(0 wei)。
    • 对应该区块 startBlock 解码的 l1_base_fee 亦为 0。两者完全吻合。

4.3 本地回填全量统计与 Python 独立求和比对

在本地 Docker ClickHouse(区块 [72039000, 72040999],共 2000 个块)执行全量比对:

  • 总块数 vs 行数:2000 块生成 2000 行 arbos_l1_pricing(100% 覆盖,0 丢失,0 膨胀)。
  • l2_block_number == block_number 一致率:2000 / 2000(100%)。
  • blocks.l1_block_number == l1_block_number 一致率:2000 / 2000(100%)。
  • 日度聚合指标比对:
    指标项ClickHouse SQL 计算结果Python 独立累加/计算结果匹配核对
    block_count20002000精确一致
    min_l1_base_fee00精确一致
    max_l1_base_fee900392014900392014精确一致
    sum(l1_base_fee)68126707036812670703精确一致
    avg_l1_base_fee34063353406335精确一致
    median_l1_base_fee00精确一致
    non_zero_blocks1010精确一致

4.4 生产库只读抽样调研

在生产 ClickHouse 库对区间 [72000000, 72010000)(10,000 个连续区块)执行只读查询:

  • 对应区块内 type = 106 AND tx_index = 0 AND selector = '6BF6A42D' 交易数量严格为 10000 笔(每个区块严格一笔)。
  • 其中非零费率区块共 56 个(占比 0.56%)。

5. 安装与补算

5.1 部署方式

将 {db} 替换为目标链数据库名(如 robinhood)后执行:

sed 's/{db}/robinhood/g' schema/clickhouse/derived/022_arbos_l1_pricing.sql \
  | clickhouse-client --multiquery

若使用 HTTP 接口(因不支持多语句批处理),请按分号将文件拆分为 6 条独立 SQL 语句依次提交:

  1. CREATE TABLE IF NOT EXISTS {db}.arbos_l1_pricing ...
  2. CREATE MATERIALIZED VIEW IF NOT EXISTS {db}.mv_arbos_l1_pricing ...
  3. INSERT INTO {db}.arbos_l1_pricing SELECT ...(补算历史区块)
  4. CREATE TABLE IF NOT EXISTS {db}.daily_l1_base_fee_stats ...
  5. CREATE MATERIALIZED VIEW IF NOT EXISTS {db}.mv_daily_l1_base_fee_stats ...
  6. INSERT INTO {db}.daily_l1_base_fee_stats SELECT ...(补算已收盘历史日)

5.2 状态监测与刷新

  • 每日聚合视图状态查看:
    SELECT view, status, last_success_time, exception
    FROM system.view_refreshes
    WHERE database = 'robinhood' AND view = 'mv_daily_l1_base_fee_stats';
  • 手动触发刷新:
    SYSTEM REFRESH VIEW robinhood.mv_daily_l1_base_fee_stats;

6. 查询示例

6.1 查询最近非零 L1 费率变动与衰减过程

SELECT
    block_number,
    block_timestamp,
    l1_base_fee,
    l1_block_number,
    time_passed
FROM robinhood.arbos_l1_pricing FINAL
WHERE is_deleted = 0
  AND l1_base_fee > 0
ORDER BY block_number DESC
LIMIT 20;

6.2 查询某段时间每日 L1 Base Fee 趋势

SELECT
    date,
    block_count,
    min_l1_base_fee,
    max_l1_base_fee,
    avg_l1_base_fee,
    median_l1_base_fee,
    non_zero_blocks
FROM robinhood.daily_l1_base_fee_stats FINAL
WHERE is_deleted = 0
ORDER BY date DESC;

On this page