| name | fiber-test |
| description | Write, review, and debug integration tests for the Fiber Network (CKB Lightning Network) project. Use when creating new test cases, understanding test patterns, doing test gap analysis, working with Fiber RPC APIs, testing channel operations, payment flows, invoice management, watchtower, or any testing task related to the fiber-py-integration-test project. Also use when the user asks about Fiber architecture, Bitcoin Lightning Network comparison, test coverage gaps, or needs to write regression tests for new PRs. |
Fiber Network Integration Test Skill
Project Overview
Fiber Network Node (FNN) is a Lightning Network implementation on Nervos CKB blockchain. This test project provides Python integration tests covering channel lifecycle, payments, invoices, watchtower, cross-chain hub (CCH), and more.
For detailed Lightning Network concepts mapped to Fiber, see references/lightning-concepts.md.
Test Framework Architecture
framework/
├── basic.py # CkbTest base (unittest.TestCase → CKB node)
├── basic_fiber.py # FiberTest base (2 Fiber nodes + helpers)
├── basic_fiber_with_cch.py # FiberCchTest (+ BTC + LND for cross-chain)
├── fiber_rpc.py # FiberRPCClient (JSON-RPC 2.0)
├── rpc.py # RPCClient for CKB
├── test_fiber.py # Fiber node lifecycle
├── test_node.py # CKB node lifecycle
├── config.py # Constants (DEFAULT_MIN_DEPOSIT_CKB = 99 * 100000000)
├── util.py # Utilities (run_command, generate_account, change_time)
└── helper/ # miner.py, ckb_cli.py, contract.py, udt_contract.py, tx.py
Inheritance: unittest.TestCase → CkbTest → FiberTest → FiberCchTest
Each test method auto-gets: CKB dev node (self.node), two connected Fiber nodes (self.fiber1, self.fiber2), UDT contract (self.udtContract).
Writing Tests - Quick Template
import time
import pytest
from framework.basic_fiber import FiberTest
class TestMyFeature(FiberTest):
def test_basic_scenario(self):
self.open_channel(self.fiber1, self.fiber2, 200 * 100000000, 100 * 100000000)
payment_hash = self.send_payment(self.fiber1, self.fiber2, 10 * 100000000)
result = self.fiber1.get_client().get_payment({"payment_hash": payment_hash})
assert result["status"] == "Success"
def test_error_scenario(self):
with pytest.raises(Exception) as exc_info:
self.fiber1.get_client().open_channel({
"pubkey": self.fiber2.get_pubkey(),
"funding_amount": hex(0), "public": True,
})
assert "should be greater than or equal to" in exc_info.value.args[0]
Key Helper Methods
| Method | Purpose |
|---|
self.open_channel(f1, f2, bal1, bal2, udt=None) | Open channel with balances |
self.send_payment(f1, f2, amount) | Keysend with retry |
self.send_invoice_payment(f1, f2, amount) | Invoice payment with retry |
self.wait_for_channel_state(client, pubkey, state) | Wait channel state |
self.wait_payment_state(fiber, hash, status) | Wait payment Success/Failed |
self.wait_invoice_state(client, hash, status) | Wait invoice status |
self.generate_account(ckb_balance) | Create funded account |
self.start_new_fiber(private_key) | Start fiber3, fiber4, ... |
self.generate_random_preimage() | Random 32-byte hex |
self.get_fiber_balance(fiber) | Chain + channel balances |
self.wait_and_check_tx_pool_fee(rate, check) | Wait for tx in pool |
self.get_ln_tx_trace(tx_hash) | Trace on-chain LN txs |
Amounts: All in Shannon (1 CKB = 100000000). Use hex() for RPC.
States: Channel: NEGOTIATING_FUNDING → CHANNEL_READY → SHUTTING_DOWN → CLOSED. Payment: Created → Inflight → Success/Failed. Invoice: Open → Received → Paid/Cancelled/Expired.
For complete API reference, see references/api-reference.md.
For detailed test patterns, see references/test-patterns.md.
Test Coverage Gap Analysis: Fiber vs Bitcoin LND
The following is a systematic comparison between LND's integration test suite (160+ test cases) and Fiber's current coverage (500+ test methods). Gaps are categorized by priority.
For the complete gap analysis with recommended test cases, see references/gap-analysis.md.
Critical Gaps (P0 - Must Fix)
| Gap Area | LND Coverage | Fiber Status | Impact |
|---|
| Cooperative close with pending TLCs | testCoopCloseWithHtlcs, testCoopCloseWithHtlcsWithRestart | shutdown_channel/ empty, test_pending_tlc.py commented out | Fund loss risk |
| Channel update tests | testUpdateChanStatus, testSendUpdateDisableChannel | update_channel/ directory has NO test files | Routing broken |
| Offline payment delivery | testSwitchOfflineDelivery* (4 tests) | send_payment/offline/ all commented out | Payment loss |
| Payment error propagation | testHtlcErrorPropagation, testSendToRouteErrorPropagation | No dedicated error propagation test | Silent failures |
| Channel reestablishment | testDataLossProtection | No channel reestablish test after disconnect | State corruption |
High Priority Gaps (P1)
| Gap Area | LND Coverage | Fiber Status | Recommended Tests |
|---|
| Gossip protocol sync | testGraphTopologyNotifications, testNodeAnnouncement | No gossip sync validation tests | Test gossip message propagation, stale message handling |
| Payment retry & backoff | Built-in retry logic (DEFAULT_PAYMENT_TRY_LIMIT=5) | No explicit retry behavior test | Test retry after transient failures, backoff timing |
| Max pending channels | testMaxPendingChannels | No test | Test concurrent channel opens exceed limit |
| Channel balance accounting | testChannelBalance, testChannelUnsettledBalance | Balance checked incidentally, no dedicated test | Test balance accuracy during TLC lifecycle |
| Invoice subscription/streaming | testInvoiceSubscriptions | No subscription test | Test real-time invoice state notifications |
| List payments query | testListPayments | Only get_payment tested | Test payment history query, filtering |
| Connection timeout | testNetworkConnectionTimeout | No timeout test | Test peer connection timeout behavior |
| Reconnect after address change | testReconnectAfterIPChange | No IP change test | Test node reconnection after address update |
Medium Priority Gaps (P2)
| Gap Area | LND Coverage | Fiber Status | Recommended Tests |
|---|
| Revoked close retribution (remote hodl) | testRevokedCloseRetributionRemoteHodl | test_revert_tx.py covers basic case only | Test with pending TLCs during revoked close |
| Circuit persistence | testSwitchCircuitPersistence | No test | Test payment circuit survives node restart |
| Payment address mismatch | testWrongPaymentAddr | No test | Test payment with wrong payment secret |
| Funding expiry edge cases | testFundingExpiryBlocksOnPending, testFundingManagerFundingTimeout | test_funding_timeout.py basic only | Test various funding timeout scenarios |
| Max channel size | testMaxChannelSize, testWumboChannels | No max size test | Test channel size limits |
| Hold invoice persistence | testHoldInvoicePersistence | test_settle_invoice.py has restart test | Enhance with multi-hop hold persistence |
| Sphinx replay persistence | testSphinxReplayPersistence | No test | Test onion packet replay protection |
| Async bidirectional payments | testBidirectionalAsyncPayments | test_send_payment_each_other partial | Test high-throughput bidirectional stress |
| Fee estimation (route) | testEstimateRouteFee | test_dry_run.py basic | Enhance dry_run with multi-hop fee estimation |
Fiber-Specific Gaps (Features in code but not tested)
| Feature | Source Location | Current Test Status |
|---|
max_tlc_number_in_flight enforcement | channel.rs (max 125, system 253) | open_channel test only, no enforcement test during payment |
max_tlc_value_in_flight enforcement | channel.rs | open_channel test only, no enforcement test during payment |
| Custom records (max 2KB) | payment.rs | test_custom_records.py basic, no overflow test |
| Trampoline MPP restriction | payment.rs (only 1 hop with MPP) | No test for this constraint |
Payment max_parts limit | payment.rs (PAYMENT_MAX_PARTS_LIMIT) | test_max_parts.py is empty (pass) |
| Gossip message ordering | gossip.rs | No test |
| Gossip stale message handling | gossip.rs (SOFT_BROADCAST_MESSAGES_CONSIDERED_STALE_DURATION) | No test |
| Watchtower external (standalone) | config: standalone_watchtower_rpc_url | No test |
| CCH order expiry | cch/ (DEFAULT_ORDER_EXPIRY_DELTA_SECONDS) | No test |
| CCH order pruning | cch/ (PRUNE_DELAY_SECONDS = 21 days) | No test |
| CCH fee calculation | cch/ (base_fee_sats, fee_rate_per_million_sats) | No test |
| Biscuit token time-based validation | rpc/biscuit.rs | No time-based auth test |
| Channel funding_timeout_seconds | channel.rs | Basic test, no edge cases |
| TLC waiting ACK timeout (30s) | channel.rs | No test |
| Commitment number sequence validation | channel.rs | No test |
| Network max_service_protocol_data_size (130KB) | network.rs | No test |
| Payment session retry from Failed state | payment.rs | No test |
| Graph cursor pagination | graph.rs (default 500) | test_graph_nodes.py tests pagination, graph_channels not tested |
Writing New Tests for PR Regression
When a new PR lands, follow this workflow:
1. Identify Changed Components
git diff develop..PR_BRANCH --stat
git diff develop..PR_BRANCH -- fiber/fiber-lib/src/
2. Map Changes to Test Categories
| Changed File | Test Directory |
|---|
fiber/channel.rs | open_channel/, shutdown_channel/, update_channel/, list_channels/ |
fiber/payment.rs | send_payment/, send_payment_with_router/ |
fiber/network.rs | connect_peer/, disconnect_peer/, general integration |
fiber/invoice.rs | new_invoice/, get_invoice/, settle_invoice/, cancel_invoice/ |
fiber/graph.rs | graph_channels/, graph_nodes/, build_router/, send_payment/path/ |
fiber/gossip.rs | graph_channels/, graph_nodes/ (gossip sync) |
watchtower/ | watch_tower/, watch_tower_wit_tlc/, watch_tower_debug/ |
cch/ | cch/ |
rpc/ | Corresponding feature directory |
3. Test Template for New PR
import time
import pytest
from framework.basic_fiber import FiberTest
class TestPR<number>(FiberTest):
"""
PR-<number>: <title>
Test coverage for: <brief description of changes>
"""
def test_<feature>_happy_path(self):
"""Test the normal/expected behavior introduced by this PR."""
self.open_channel(self.fiber1, self.fiber2, 200 * 100000000, 100 * 100000000)
def test_<feature>_edge_case(self):
"""Test boundary conditions."""
pass
def test_<feature>_error_handling(self):
"""Test error cases."""
with pytest.raises(Exception) as exc_info:
pass
assert "expected error" in exc_info.value.args[0]
def test_<feature>_backward_compatible(self):
"""Ensure existing functionality still works."""
pass
4. Test Naming Convention
- File:
test_cases/fiber/devnet/<category>/test_<feature>.py
- Class:
Test<Feature>(FiberTest) or <FeatureName>(FiberTest)
- Method:
test_<scenario_description>(self)
5. CI Integration
Add new test to Makefile test targets if new category. Existing categories auto-discover.
Running Tests
pytest test_cases/fiber/devnet/open_channel/test_funding_amount.py -v -s
pytest test_cases/fiber/devnet/open_channel/test_funding_amount.py::FundingAmount::test_funding_amount_ckb_is_zero -v -s
make fiber_test
python -m pytest test_cases/fiber/devnet/ --html=report/report.html