Skip to content

Funding tx is rejected for insufficient fees when the acceptor's lock-group witness is still empty #1663

Description

@sunchengzhu

English | 中文

Summary

On FNN 0.9.1, a dual-funded SUDT channel fails to open at the default fee rate. FNN builds a funding transaction with a fee of 1077 shannons, but CKB requires 1162 and rejects it.

In this transaction, FNN does not reserve witness space for the channel acceptor's lock when calculating the fee.

Environment

Item Value
FNN Official macOS arm64 release, fnn Fiber v0.9.1 (9a561b3 2026-09-10).
CKB 0.208.0, local devnet, tx_pool.min_fee_rate = 1000.
Setup Two connected FNN nodes with different funding locks.

funding_fee_rate is omitted, so Fiber uses DEFAULT_FEE_RATE = 1000. The channel initiator opens with 5000 SUDT (0x1388) and the acceptor accepts with 10000 SUDT (0x2710), both from their own secp locks:

{
  "pubkey": "<acceptor_pubkey>",
  "funding_amount": "0x1388",
  "funding_udt_type_script": {
    "code_hash": "0x6283a479a3cf5d4276cd93594de9f1827ab9b55c7b05b3d28e4c2e0a696cfefd",
    "hash_type": "type",
    "args": "0x4472b33b4e1845ebe82f2ce5f511bbe012f144c5f3d7b539909adffc83ccda61"
  }
}

Actual and expected results

Expected: the channel opens at the default fee rate.

Actual: after the acceptor accepts, CKB rejects the funding transaction. The channel stays unusable. Node log:

  2026-09-24T10:29:02.165872Z ERROR fnn::ckb::actor: [ckb] send transaction Byte32(0xbc45c7633b1b89063bc43e3a714cbc6064d7b34e194d29dbad3e22f3a897fee3) failed: Rpc(Error { code: ServerError(-1104), message: "PoolRejectedTransactionByMinFeeRate: The min fee rate is 1000 shannons/KW, requiring a transaction fee of at least 1162 shannons, but the fee provided is only 1077", data: Some(String("LowFeeRate(FeeRate(1000), 1162, 1077)")) })

The same scenario at funding_fee_rate 2000 opens successfully; the fee is then 2154 shannons and the size underestimate is still there.

Cause

The two peers use different secp locks. The acceptor's UDT input is already present when balancing runs and is its first input, at index 2, while Fiber reserves signature space only at index 0. The fee is therefore fixed while the acceptor's witness entry is still empty, and the signature written at signing time grows the transaction by 85 bytes without changing the fee.

The fee is fixed during transaction construction by CapacityBalancer; the CKB actor's LocalSigner::sign_funding_tx only signs and does not recalculate it.

This issue is not specific to SUDT. In this funding transaction construction path, the acceptor's secp script group already has an input before balancing, but the lock field of its witness entry is still empty when the fee is calculated. Other UDTs, including xUDT, are expected to be affected under the same conditions; the reproduction reported here uses SUDT.

What is counted Size used to calculate the fee Fee required at rate 1000
Before signing: space reserved for only one of the two required signatures 1077 bytes 1077 shannons
After signing: both required signatures are present 1162 bytes 1162 shannons
Difference 85 bytes 85 shannons

Code locations

Location What goes wrong
Fiber build_base_from_funding_cell, lines 501–550 Builds the transaction from both peers' funds, but resets witnesses to one entry. The first entry, used by the initiator's account, reserves signature space. The SDK later adds an empty entry for the acceptor, with no space reserved for its signature.
Fiber build_and_balance_tx, lines 552–575 Creates the SDK signing helper with no private keys, so the SDK's automatic placeholder filling matches neither account.
SDK has_provider check and signature-data branch The acceptor's UDT input is already in the transaction, so the SDK skips the branch that would reserve signature space when adding the first CKB input from that account.
SDK fee calculation, then Fiber LocalSigner::sign_funding_tx The fee is calculated with the acceptor's field still empty; signing fills it later without rebalancing.

中文版

接受方 script group 的 witness lock 字段为空,导致开通道交易因手续费不足被拒绝

问题概述

在 FNN 0.9.1 中,双方共同出资的 SUDT 通道在默认费率下无法开通。FNN 构造的交易手续费为 1077 shannons,而 CKB 要求 1162,因此拒绝了这笔交易。

本次交易中,FNN 计算手续费时没有为通道接受方的账户锁预留 witness 空间。

测试环境

项目 配置
FNN 官方 macOS arm64 发布包,fnn Fiber v0.9.1 (9a561b3 2026-09-10)。
CKB 0.208.0,本地开发链,tx_pool.min_fee_rate = 1000。
节点 两个已连接的 FNN 节点,使用不同的出资账户锁。

未指定 funding_fee_rate,Fiber 使用 DEFAULT_FEE_RATE = 1000。通道发起方以 5000 SUDT(0x1388)发起,通道接受方以 10000 SUDT(0x2710)接受,双方各自使用自己的 secp 账户锁:

{
  "pubkey": "<acceptor_pubkey>",
  "funding_amount": "0x1388",
  "funding_udt_type_script": {
    "code_hash": "0x6283a479a3cf5d4276cd93594de9f1827ab9b55c7b05b3d28e4c2e0a696cfefd",
    "hash_type": "type",
    "args": "0x4472b33b4e1845ebe82f2ce5f511bbe012f144c5f3d7b539909adffc83ccda61"
  }
}

预期与实际结果

预期:通道能以默认费率正常开通。

实际:通道接受方接受后,CKB 拒绝了开通道交易,通道无法使用。节点日志:

  2026-09-24T10:29:02.165872Z ERROR fnn::ckb::actor: [ckb] send transaction Byte32(0xbc45c7633b1b89063bc43e3a714cbc6064d7b34e194d29dbad3e22f3a897fee3) failed: Rpc(Error { code: ServerError(-1104), message: "PoolRejectedTransactionByMinFeeRate: The min fee rate is 1000 shannons/KW, requiring a transaction fee of at least 1162 shannons, but the fee provided is only 1077", data: Some(String("LowFeeRate(FeeRate(1000), 1162, 1077)")) })

同一场景改用 funding_fee_rate 2000 可以正常开通,此时手续费为 2154 shannons,交易大小仍然少算了。

问题原因

双方使用不同的 secp 账户锁。估费时,通道接受方的 UDT 输入已经在交易中,且是它的首个输入、位于索引 2,而 Fiber 只为索引 0 预留了签名空间。手续费因此在接受方 witness 仍为空时就已定死,签名阶段写入后交易增加 85 字节,手续费不变。

手续费在构造交易时由 CapacityBalancer 算好,CKB actor 调用的 LocalSigner::sign_funding_tx 只签名,不重新计算。

此问题并非 SUDT 特有。在这条开通道交易构造路径中,接受方的 secp script group 在平衡交易前已有输入,但计算手续费时,它对应的 witness 里 lock 字段仍为空。在相同条件下,其他 UDT(包括 xUDT)预计也会受影响;本文实际复现使用的是 SUDT。

计算时机 用来计算手续费的交易大小 按费率 1000 应付的手续费
签名前:需要两份签名,只预留了一份的空间 1077 字节 1077 shannons
签名后:需要的两份签名都已加入 1162 字节 1162 shannons
少算的部分 85 字节 85 shannons

具体代码位置

代码位置 问题说明
Fiber build_base_from_funding_cell,501–550 行 把双方出资合成交易,但将 witnesses 列表重置为只有一项。第一项用于发起方账户,预留了签名所需的空间。SDK 后续虽然补上了接受方对应的项,但其中是空值,没有预留签名空间。
Fiber build_and_balance_tx,552–575 行 创建 SDK 签名工具时没有传入任何私钥,SDK 自动补充占位数据的步骤因此无法匹配双方账户。
SDK 的 has_provider 判断及补充签名数据的分支 接受方的代币已经作为交易输入加入,SDK 因此跳过了“首次加入该账户的 CKB 输入时预留签名空间”的分支。
SDK 计算手续费,随后 Fiber LocalSigner::sign_funding_tx 签名 接受方的签名字段还是空的,手续费就已经算好了;签名阶段才填入,且不会重新平衡。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions