# 交易 trace：debug_traceTransaction 与 Data API 的 trace 接口

> 原文地址: https://docs.blockvectra.com/zh/guides/transaction-traces/

## 重建调用树的两种方式 [#重建调用树的两种方式]

交易 trace 是一次执行重建出来的调用树：调用了哪个合约、传入什么参数、消耗多少 gas、又发起了哪些子调用。BlockVectra 通过两个入口提供：

* **JSON-RPC `debug_trace*`** —— 通过 JSON-RPC 端点在这条链的节点上执行，因此可以追踪节点仍保留的最新状态。
* **Data API trace 接口** —— `GET /{chain}/transactions/{hash}/trace` 与 `GET /{chain}/blocks/{number}/traces` 通过 REST 返回已存储、已索引的调用树。

两者使用同一个 API key，并按方法权重以 CU 计量（见下方权重表）。选哪一种，取决于要查单笔交易还是整个区块、目标有多新，以及是否要一次取回整个区块。

## debug\_trace\* 受到的限制 [#debug_trace-受到的限制]

`debug_trace*` 请求需要该链的方法策略允许对应方法，且 `tracer` 取值在允许范围内：

* **允许的 tracer**：`tracer` 参数仅允许内置原生 tracer —— `callTracer`、`flatCallTracer`、`prestateTracer`、`4byteTracer`、`noopTracer`，或缺省使用默认 struct logger。其余取值返回 JSON-RPC 错误 `-32602 tracer not allowed`，不转发、不计费。
* **trace 超时**：`timeout` 参数需为合法 duration 且不超过 30s，否则返回 `-32602 trace timeout not allowed`，不转发、不计费。
* **节点同步门**：当某条链的节点未同步时，除 `eth_chainId` 外的所有方法（包括 `debug_trace*` 与按哈希/高度的历史查询）一律返回 `-32010`；该调用不转发、不计费。
* **状态窗口**：`debug_traceCall`、`debug_traceBlockByNumber`、`debug_traceTransaction`、`debug_traceBlockByHash` 的目标区块必须在链的状态窗口内；早于窗口，或使用 `safe`、`finalized`、`earliest` 标签，返回 `-32011`。节点自身对窗口外状态返回的错误不计费。区块、收据等非状态数据不受状态窗口限制，但受节点保留历史限制。
* **哈希预解析**：`debug_traceTransaction` 与 `debug_traceBlockByHash` 会先把哈希解析为区块高度，再套用状态窗口。不是 `0x` + 64 位十六进制的哈希返回 `-32000 transaction not found` / `block not found`，且不会查询节点；格式正确但节点查不到的哈希同样返回 `-32000`；解析查询失败返回 `-32603 upstream unavailable`（可重试）。以上都不转发、不计费。
* **逐链方法策略**：某条链允许哪些 `debug_trace*` 方法，由公开的 `GET /v1/chains` 响应公布。请在运行时读取，不要写死方法列表；链列表见[支持的链](/zh/chains/)，方法参考见 [JSON-RPC 方法](/zh/api/json-rpc/methods/)页面。

### 请求 debug\_traceTransaction 并使用 callTracer [#请求-debug_tracetransaction-并使用-calltracer]

规格中的示例请求只带交易哈希。下面的调用在此基础上增加 `tracer` 参数以请求调用树；取值来自上面的 tracer 白名单，因此这里不编造任何响应体。

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY=rgw_your_api_key

    # 规格示例（reqTraceTx）只传交易哈希：
    #   {"jsonrpc":"2.0","id":1,"method":"debug_traceTransaction",
    #    "params":["0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"]}
    # 增加 "tracer"，用允许的内置原生 tracer 请求调用树。
    curl -s "https://dev-api.blockvectra.network/v1/robinhood_mainnet" \
      -H 'Content-Type: application/json' \
      -H "x-api-key: $BLOCKVECTRA_API_KEY" \
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "debug_traceTransaction",
        "params": [
          "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
          { "tracer": "callTracer" }
        ]
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    // 规格示例中端点是按链划分的：…/v1/{chain}。
    const RPC_ENDPOINT = "https://dev-api.blockvectra.network/v1/robinhood_mainnet";

    const res = await fetch(RPC_ENDPOINT, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "debug_traceTransaction",
        params: [
          "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
          { tracer: "callTracer" },
        ],
      }),
    });

    const body = (await res.json()) as {
      result?: unknown;
      error?: { code: number; message: string };
    };

    if (body.error) {
      throw new Error(`debug_traceTransaction error ${body.error.code}: ${body.error.message}`);
    }
    console.log(body.result);

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    # 规格示例中端点是按链划分的：…/v1/{chain}。
    RPC_ENDPOINT = "https://dev-api.blockvectra.network/v1/robinhood_mainnet"

    res = requests.post(
        RPC_ENDPOINT,
        headers={
            "Content-Type": "application/json",
            "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "debug_traceTransaction",
            "params": [
                "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",
                {"tracer": "callTracer"},
            ],
        },
    )
    res.raise_for_status()
    body = res.json()

    if "error" in body:
        err = body["error"]
        raise RuntimeError(f"debug_traceTransaction error {err.get('code')}: {err.get('message')}")
    print(body["result"])
    ```
  </Tab>
</Tabs>

## Data API trace 接口提供什么 [#data-api-trace-接口提供什么]

Data API 为两种范围返回已存储的调用树。两者都不分页：绝不会有 `next_cursor`。

* `GET /{chain}/transactions/{hash}/trace` —— 按交易哈希查询单笔交易的调用帧。
* `GET /{chain}/blocks/{number}/traces` —— 区块内每笔交易一棵调用树，按 `tx_index` 顺序排列。一个真实已终局且没有任何交易的区块返回 `data: []`。

响应信封为：

* `TxTraceEnvelope`：`data` 直接是一个 `CallFrame`，另有 `meta`。
* `BlockTracesEnvelope`：`data` 是 `BlockTraceItem` 数组，每项含 `txHash` 与 `result`（一个 `CallFrame`），另有 `meta`。

两个 trace 接口都返回标准的以太坊 `callTracer` 格式。这是 Data API「金额安全」编码的一处例外：其他接口会把可能超过 `2^53` 的数值序列化为十进制字符串；这两个接口的 `value`、`gas`、`gasUsed` 是 `0x` 前缀的十六进制数量，而不是十进制字符串。每个 `CallFrame` 都带 `type`、`from`、`gas`、`gasUsed`、`input`；`type` 为 `CALL`、`DELEGATECALL`、`STATICCALL`、`CREATE`、`CREATE2`、`SELFDESTRUCT` 之一。`CREATE`/`CREATE2` 帧的目标地址没有 `to`，`STATICCALL` 帧没有 `value`。可选成员有 `output`（调用没有返回数据时缺失）、`error`（成功时缺失）、`revertReason`（仅在以 `Error(string)` 回滚时出现）、`calls`（按调用顺序排列的嵌套子调用）。调用帧里的其他成员会原样保留。

为便于理解结构，下面给出 `CallFrame` 的字段骨架——只是带注释的结构说明，不是实测响应。规格没有为这两个 trace 接口提供响应示例，因此这里不给出任何具体数值：

```jsonc
{
  "type": "CALL | DELEGATECALL | STATICCALL | CREATE | CREATE2 | SELFDESTRUCT",
  "from": "0x…",                      // 20 字节地址
  "to": "0x…",                        // CREATE/CREATE2 的目标地址缺失
  "value": "0x…",                     // 0x 前缀十六进制数量；STATICCALL 帧缺失
  "gas": "0x…",                       // 0x 前缀十六进制数量
  "gasUsed": "0x…",                   // 0x 前缀十六进制数量
  "input": "0x…",
  "output": "0x…",                    // 调用没有返回数据时缺失
  "error": "…",                       // 成功时缺失
  "revertReason": "…",                // 仅当以 Error(string) 回滚时出现
  "calls": []                         // 按调用顺序排列的嵌套子调用；叶子帧缺失
}
```

### 参数 [#参数]

* `{chain}`（路径参数，必填）：链标识，即 `GET /chains` 中某项的 `chain` 值。精确匹配且区分大小写；别名与数字 chain ID 均不接受。
* `{hash}`（路径参数，交易 trace 必填）：32 字节交易哈希，`0x` 前缀可选，大小写不限。
* `{number}`（路径参数，区块 traces 必填）：非负区块高度。

### 覆盖范围与最终性 [#覆盖范围与最终性]

* 两个接口都属于 `traces` 能力。未提供该能力的链返回 `422 no_coverage`。提供该数据集的链以[支持的链](/zh/chains/)页面为准。
* trace 数据可能晚于该链其余已索引历史开始。早于该链首个已追踪区块的请求、落在已记录缺口内的请求，或交易已被索引但从未被追踪且已落后索引链头太远的请求，均返回 `422 no_coverage`；`GET /chains` 以 `coverage.traces_from_block` 公布这一边界，早于它的 trace 请求（或落在无法追踪的区间内）返回 `422 no_coverage`。
* 交易 trace 先把哈希解析为区块，再用 `finalized_block` 检查：解析出的区块高于它时返回 `409 finality_exceeded`。哈希查询没有可用于判断「尚未索引」的水位线，因此刚提交、索引尚未追上的交易同样返回 `404 not_found`；请稍后重试，再当作永久不存在处理。
* 区块 traces 接口接收区块高度。`{number}` 高于 `as_of_block` 时返回 `409 not_indexed_yet`，并带 `indexed_through`；不高于 `as_of_block` 但高于 `finalized_block` 时返回 `409 finality_exceeded`。
* 有交易但尚无 trace 数据的新近区块返回 `503 unavailable`，并带 `Retry-After` 头。等它落后索引链头超过该链的 trace 窗口后，就改为 `422 no_coverage`。

### 用 Data API 请求 trace [#用-data-api-请求-trace]

<Tabs groupId="code-lang" items="['cURL', 'TypeScript', 'Python']">
  <Tab value="cURL">
    ```bash
    export BLOCKVECTRA_API_KEY=rgw_your_api_key

    # 单笔交易的调用帧。
    curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/transactions/0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd/trace" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY"

    # 某个已终局区块内，每笔交易一棵调用树。
    curl -s "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/blocks/1/traces" \
      -H "x-api-key: $BLOCKVECTRA_API_KEY"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const hash = "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd";

    // 先读任意一个按链响应的 meta.finalized_block：区块 traces 接口只提供不高于它的区块。
    const txRes = await fetch(
      `https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/transactions/${hash}/trace`,
      { headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! } },
    );
    const txBody = await txRes.json();
    console.log(txBody.data, txBody.meta);

    const blockRes = await fetch("https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/blocks/1/traces", {
      headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
    });
    const blockBody = await blockRes.json();
    console.log(blockBody.data.map((item: { txHash: string }) => item.txHash));

    // npx tsx example.mts
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os
    import requests

    hash_ = "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
    headers = {"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]}

    # 先读任意一个按链响应的 meta.finalized_block：区块 traces 接口只提供不高于它的区块。
    tx = requests.get(
        f"https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/transactions/{hash_}/trace",
        headers=headers,
    )
    tx.raise_for_status()
    tx_body = tx.json()
    print(tx_body["data"], tx_body["meta"])

    block = requests.get(
        "https://dev-api.blockvectra.network/v1/data/robinhood_mainnet/blocks/1/traces",
        headers=headers,
    )
    block.raise_for_status()
    block_body = block.json()
    print([item["txHash"] for item in block_body["data"]])
    ```
  </Tab>
</Tabs>

## 该选哪一个 [#该选哪一个]

| 典型任务               | 更适合                                      | 理由                                                         |
| ------------------ | ---------------------------------------- | ---------------------------------------------------------- |
| 交易刚落链就需要重建它        | `debug_traceTransaction`                 | 它针对节点当前状态执行，因此不限于 `finalized_block` 及以下的区块；可用性取决于该链的方法策略。  |
| 读取单笔交易已存储的调用树      | `GET /{chain}/transactions/{hash}/trace` | 通过 REST 直接返回该交易的 `CallFrame`；解析出的区块须不高于 `finalized_block`。 |
| 一次请求取回整个区块的全部调用树   | `GET /{chain}/blocks/{number}/traces`    | 不分页返回整个区块，按 `tx_index` 顺序排列；区块须不高于 `finalized_block`。      |
| 追踪节点仍有、但数据集尚未存储的状态 | `debug_trace*`                           | Data API 只提供截止 `finalized_block` 的已存储数据；节点可以为更新的区块作答。      |

## 单次调用的 CU [#单次调用的-cu]

每个方法都按其 CU 权重计费。下方权重在构建时从平台计划接口读取：

<TransactionTracesCu lang="zh" />

被拒绝的请求不计费。完整计费规则见[哪些请求不计费：错误码与计费规则](/zh/guides/billing-rules/)。

## 下一步 [#下一步]

* [浏览数据集目录](https://blockvectra.com/zh/data/)，查看 BlockVectra 索引的全部数据集。
* [查看免费额度与定价](https://blockvectra.com/zh/pricing/#free)，确认账户可用的方案。
* [登录控制台](https://console.blockvectra.com/zh/login/?next=%2Fzh%2Fkeys%2F)创建 API key。
