Connect Public Nodes
User manual on how to connect to the public Testnet nodes
Overview
This document provides a user manual for connecting to the public Testnet nodes of the Fiber network. It covers running a local node, establishing a channel with a public node, and transferring CKB and RUSD(stablecoin) within the channels. If you haven't read the Run a Fiber Node guide and the Basic Transfer Example, it is highly recommended to read them first.
Version Note
This guide uses Fiber Node v0.8.0+ which introduces breaking changes from v0.7.1:
- Node identifier changed from
peer_id(base58) topubkey(hex-encoded secp256k1) - JSON enum format changed from PascalCase to snake_case
- New
fnn-clitool for convenient command-line operations
Testnet Public Nodes' Addresses
node1
"/ip4/18.162.235.225/tcp/8119/p2p/QmXen3eUHhywmutEzydCsW4hXBoeVmdET2FJvMX69XJ1Eo"node2
"/ip4/18.163.221.211/tcp/8119/p2p/QmbKyzq9qUmymW2Gi8Zq7kKVpPiNA1XUJ6uMvsUC4F3p89"Local Node Deployment
-
Download fnn
Please visit the releases page to download and use the latest version of fnn (v0.8.0 or higher).
mkdir tmp && cd tmp tar xzvf fnn-latest.tar.gzmacOS Security
If you're using macOS, the downloaded binary may be blocked by Gatekeeper. Remove the quarantine attribute:
xattr -d com.apple.quarantine fnn fnn-cli fnn-migrate -
Export the account private key to the fiber node's ckb directory
Here, the ckb-cli is used to create an account, which will later be used to pay for opening channels between the local node and the public testnet node. If ckb-cli is not installed, please download it from the releases.
# Create a local node directory named nodeA mkdir -p testnet-fnn/nodeA/ckb # Create a new CKB account (save the lock_arg output) ./ckb-cli account new # Export the private key (replace with your actual lock_arg) ./ckb-cli account export --lock-arg <YOUR_LOCK_ARG> --extended-privkey-path ./exported-key # Copy the key to the node directory (run from the same directory as ckb-cli) head -n 1 ./exported-key > testnet-fnn/nodeA/ckb/key chmod 600 testnet-fnn/nodeA/ckb/key # Verify the key was copied correctly ./ckb-cli util key-info --privkey-path testnet-fnn/nodeA/ckb/key -
Copy config.yml and fnn-cli
cp config/testnet/config.yml testnet-fnn/nodeA cp fnn-cli testnet-fnn/nodeAHTTP Proxy Issues
If you encounter
503 Service Unavailableerrors when using fnn-cli, it may be due to HTTP proxy settings. SetNO_PROXYto exclude local addresses:export NO_PROXY=127.0.0.1,localhost -
Fund nodeA's address with 10000ckb and 20RUSD via faucet
The RUSD faucet cannot directly fill an address, so you can first claim 20RUSD through a wallet like joyid, then transfer it to nodeA's address from the joyid wallet page.
-
Start the node A
You need to set a
FIBER_SECRET_KEY_PASSWORDenvironment variable in the startup command to encrypt your wallet private key file. I used123here for demo purposes, but I recommend using a strong password.FIBER_SECRET_KEY_PASSWORD='123' RUST_LOG=info ./fnn -c testnet-fnn/nodeA/config.yml -d testnet-fnn/nodeA > testnet-fnn/nodeA/a.log 2>&1 &
Establishing a CKB Channel with Public Node 1
Using fnn-cli vs RPC
This guide provides both fnn-cli (command-line interface) and RPC (HTTP API) examples for each step.
Important notes:
- Default RPC endpoint for fnn-cli is
http://127.0.0.1:8227 - CKB amounts in CLI are in shannons (1 CKB = 100,000,000 shannons)
- RPC amounts must be specified as hex strings (e.g.,
"0x5f5e100"for 100,000,000)
-
Establish a network connection between nodeA and node1
First, get node1's pubkey by connecting to its RPC endpoint:
curl -s --location 'http://18.162.235.225:8227' --header 'Content-Type: application/json' --data '{ "id": 1, "jsonrpc": "2.0", "method": "node_info" }' | grep -o '"pubkey":"[^"]*"'Then connect using the pubkey:
Using CLI:
cd testnet-fnn/nodeA && ./fnn-cli peer connect_peer --address "/ip4/18.162.235.225/tcp/8119/p2p/QmXen3eUHhywmutEzydCsW4hXBoeVmdET2FJvMX69XJ1Eo"HTTP Proxy Issues
If you encounter
503 Service Unavailableor connection errors when using fnn-cli, it may be due to HTTP proxy settings on your system. SetNO_PROXYto exclude local addresses:export NO_PROXY=127.0.0.1,localhostUsing RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 1, "jsonrpc": "2.0", "method": "connect_peer", "params": [ { "pubkey": "<node1_pubkey>", "address": "/ip4/18.162.235.225/tcp/8119/p2p/QmXen3eUHhywmutEzydCsW4hXBoeVmdET2FJvMX69XJ1Eo" } ] }'{"jsonrpc":"2.0","result":null,"id":1} -
Establish a channel with 500ckb: nodeA (500ckb) ⟺ node1 (250ckb)
Node1 has open_channel_auto_accept_min_ckb_funding_amount set at 438ckb, so please input 500ckb or more.
Using CLI:
cd testnet-fnn/nodeA && ./fnn-cli channel open_channel \ --pubkey <node1_pubkey> \ --funding-amount 50000000000 \ --public trueUsing RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 2, "jsonrpc": "2.0", "method": "open_channel", "params": [ { "pubkey": "<node1_pubkey>", "funding_amount": "0xba43b7400", "public": true } ] }'{"jsonrpc":"2.0","result":{"temporary_channel_id":"0x30089ec4c8ce1e1d4930220c2bff856eec7ab44550e15b76d62489fd42eaafe8"},"id":2} -
Query the channels between nodeA and node1
Using CLI:
./fnn-cli channel list_channelsUsing RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 3, "jsonrpc": "2.0", "method": "list_channels", "params": [ { "peer_pubkey": "<node1_pubkey>" } ] }'Wait until the state_name changes to
ChannelReady.Note: When the channel has just changed to the ChannelReady state and you attempt to use send_payment, you may still encounter an error:
Failed to build route. This is because the network graph (gossip protocol) has not fully synced yet, and your node doesn't know the routing paths to other nodes. It is advisable to wait for some time (usually a few minutes) before trying again.{"jsonrpc":"2.0","result":{"channels":[{"channel_id":"0x26ce85d57fb4a1a826cbf4862358862317a83b775090625550d8be12c6ce9569","is_public":true,"channel_outpoint":"0x9bb2a8a4bebaf793a235ba2ec87051ae0018b58736b6741df74009ca8101cb8d00000000","peer_pubkey":"<node1_pubkey>","funding_udt_type_script":null,"state":{"state_name":"CHANNEL_READY","state_flags":[]},"local_balance":"0xa32aef600","offered_tlc_balance":"0x0","remote_balance":"0x460913c00","received_tlc_balance":"0x0","latest_commitment_transaction_hash":"0x18ef541a5a195c0ea4715a7783964b3c4be8fba6bd25542e626f91ef1673e3e4","created_at":"0x195892d237f","enabled":true,"tlc_expiry_delta":"0x5265c00","tlc_fee_proportional_millionths":"0x3e8"}]},"id":3Why is the local_balance 0xa32aef600 (43,800,000,000) and the remote_balance 0x460913c00 (18,800,000,000)?
This channel was established with nodeA contributing 500 ckb and node1 contributing 250 ckb.
Since each cell requires a minimum of 98 ckb, this amount is reserved to ensure that there are sufficient funds to cover cell occupancy costs during on-chain settlement (when the channel closes). These 98 ckb will be returned to their respective nodes at the time of on-chain settlement.
Actual available funds in the channel:
nodeA: 500 ckb - 98 ckb = 402 ckb (local_balance is 0xa32aef600)
node1: 250 ckb - 98 ckb = 152 ckb (remote_balance is 0x460913c00)
-
Call the
new_invoiceAPI on node2 to generate an invoiceMulti-hop Payment Prerequisite
This is a multi-hop payment (nodeA → node1 → node2). For this to work, node1 must already have an established channel with node2. The public testnet nodes (node1 and node2) already have channels between them, so you can proceed directly.
If you're testing with your own nodes, make sure the intermediate node has a channel connected to the target node first.
Set the amount to 0x5f5e100 (100,000,000 shannon), which is equivalent to 1 CKB. The payment_preimage should be a unique 32-byte hexadecimal number.
# Generate a 32-byte random number and represent it in hexadecimal payment_preimage="0x$(openssl rand -hex 32)" echo $payment_preimage0xbc03e507befb33cfd5953a2e7046428e69cb8f0ade65c05d3661128aa4b4fff9Using CLI (on node2's host):
./fnn-cli --url http://18.163.221.211:8227 invoice new_invoice \ --amount 100000000 \ --currency Fibt \ --description "test invoice generated by node2" \ --expiry 3600 \ --payment-preimage 0xbc03e507befb33cfd5953a2e7046428e69cb8f0ade65c05d3661128aa4b4fff9Using RPC:
curl -s --location 'http://18.163.221.211:8227' --header 'Content-Type: application/json' --data '{ "id": 4, "jsonrpc": "2.0", "method": "new_invoice", "params": [ { "amount": "0x5f5e100", "currency": "Fibt", "description": "test invoice generated by node2", "expiry": "0xe10", "payment_preimage": "0xbc03e507befb33cfd5953a2e7046428e69cb8f0ade65c05d3661128aa4b4fff9", "hash_algorithm": "sha256" } ] }'{"jsonrpc":"2.0","result":{"invoice_address":"fibt1000000001peseucdphcxgfw0pnm6vk3uftyc36dakyjchs0p0unk9gaug0h36uhafww9pvy38gcesad084rx48xgx9xts49yp9fn87yfchld3l3qu5n0pfzvvy8c9g7dksrcxyrtk3hymspezmvtx4vg5v6uvt6tyxmq5uhrfejpk0j6wue9ef2pa8mzmrgqaz3wucutujtjcmq2x8f36faxuctg62ny73mhaj7rpwqe0ns0wp5wr4tku7qcl9r4a3swluvd2jqqwmsl7wsz4cwvhhe7p8tr7hz5qkqwr3r38hukckqzjtmntd8zrz0ywux4u8df005hl76thzsp9hz7dyefzk4mqhx4x9el98zjzmhcveqpfeur79","invoice":{"currency":"Fibt","amount":"0x5f5e100","signature":"0e1b101f1e0e100215180e0c1717191e01070b031e1702140016000e0311031107171c1618160002120b1b130b0d070203020f040e1c06151c070d090f0f14171f1e1a0b170210010517021e0d0419090216151b001706150605191f05070212021b17180c190001","data":{"timestamp":"0x1958944fa64","payment_hash":"0xafb604f74c28009732ed4c82983cf1efaddf62ee36442f360fb4a8c79b845432","attrs":[{"description":"test invoice generated by node2"},{"expiry_time":{"secs":3600,"nanos":0}},{"hash_algorithm":"sha256"},{"payee_public_key":"0291a6576bd5a94bd74b27080a48340875338fff9f6d6361fe6b8db8d0d1912fcc"}]}}},"id":4}Record the
invoice_addressfrom the response. -
Before nodeA sends the payment, first query the local_balance and remote_balance of each channel
nodeA ⟺ node1
As shown in Step 3, the response included:
{"local_balance":"0xa32aef600","remote_balance":"0x460913c00"}node1 ⟺ node2
Using CLI:
./fnn-cli --url http://18.162.235.225:8227 channel list_channelsUsing RPC:
curl -s --location 'http://18.162.235.225:8227' --header 'Content-Type: application/json' --data '{ "id": 5, "jsonrpc": "2.0", "method": "list_channels", "params": [ { "peer_pubkey": "<node2_pubkey>" } ] }'{"jsonrpc":"2.0","result":{"channels":[{"channel_id":"0x29a2e93e70fcfcd8b64fd74646b3893247f2a73a9dd8706298b5defa17bfee0a","is_public":true,"channel_outpoint":"0xa065311059be4d2194d9d6dbc428fe794ed3c6d91e08fe1d960d1574c19f88d400000000","peer_pubkey":"<node2_pubkey>","funding_udt_type_script":{"code_hash":"0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a","hash_type":"type","args":"0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b"},"state":{"state_name":"CHANNEL_READY","state_flags":[]},"local_balance":"0x173c0e06bb","offered_tlc_balance":"0x1f5","remote_balance":"0xc68e145","received_tlc_balance":"0x0","latest_commitment_transaction_hash":"0x195e1cbd1dd062752e776a44dd9c12f3b83a69dfdd1e22edff19025572bcbd25","created_at":"0x1944491c154","enabled":true,"tlc_expiry_delta":"0x5265c00","tlc_fee_proportional_millionths":"0x3e8"},{"channel_id":"0x4cd5bdcac419b203fd5752c4daa00a6f24305123d65f7a7fa6b455df82e97eee","is_public":true,"channel_outpoint":"0xe7d8464be26933021810f31252a98e9b2b1ff00f70173fafb134861ce21bccbb00000000","peer_pubkey":"<node2_pubkey>","funding_udt_type_script":{"code_hash":"0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a","hash_type":"type","args":"0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b"},"state":{"state_name":"CHANNEL_READY","state_flags":[]},"local_balance":"0x1748630df7","offered_tlc_balance":"0x0","remote_balance":"0x13da09","received_tlc_balance":"0x0","latest_commitment_transaction_hash":"0x6bd9890fd65297359079d87a995d794becc90bafd5eca9676ccbfd96abcb3ffd","created_at":"0x194448fc295","enabled":true,"tlc_expiry_delta":"0x5265c00","tlc_fee_proportional_millionths":"0x3e8"},{"channel_id":"0x9e72e8dbf7409a5aaf456dbe25f61247450f72079249ee508bb23cb14d0408b1","is_public":true,"channel_outpoint":"0x49f5f1cf664d48df66943989ef87d1316f1dffe5aec96db9ee8f1b6879ccac1b00000000","peer_pubkey":"<node2_pubkey>","funding_udt_type_script":null,"state":{"state_name":"CHANNEL_READY","state_flags":[]},"local_balance":"0x45a9b5cf3","offered_tlc_balance":"0x0","remote_balance":"0x916e2dc010d","received_tlc_balance":"0x0","latest_commitment_transaction_hash":"0x195e1cbd1dd062752e776a44dd9c12f3b83a69dfdd1e22edff19025572bcbd25","created_at":"0x1944491c154","enabled":true,"tlc_expiry_delta":"0x5265c00","tlc_fee_proportional_millionths":"0x3e8"}]},"id":5}Find all entries in the response where
funding_udt_type_scriptis null.{"local_balance":"0x45a9b5cf3","remote_balance":"0x916e2dc010d"} {"local_balance":"0x504f21d045c","remote_balance":"0x4164b5a59a4"} {"local_balance":"0x916d6f044e9","remote_balance":"0x466871917"} {"local_balance":"0x48ab5ace976","remote_balance":"0x49087ca748a"} {"local_balance":"0x48ab5acd200","remote_balance":"0x49087ca8c00"} -
Send a send_payment request to nodeA to pay node2
Pass in the previously recorded invoice_address to the
send_paymentrequestUsing CLI:
./fnn-cli payment send_payment --invoice "fibt1000000001peseucdphcxgfw0pnm6vk3uftyc36dakyjchs0p0unk9gaug0h36uhafww9pvy38gcesad084rx48xgx9xts49yp9fn87yfchld3l3qu5n0pfzvvy8c9g7dksrcxyrtk3hymspezmvtx4vg5v6uvt6tyxmq5uhrfejpk0j6wue9ef2pa8mzmrgqaz3wucutujtjcmq2x8f36faxuctg62ny73mhaj7rpwqe0ns0wp5wr4tku7qcl9r4a3swluvd2jqqwmsl7wsz4cwvhhe7p8tr7hz5qkqwr3r38hukckqzjtmntd8zrz0ywux4u8df005hl76thzsp9hz7dyefzk4mqhx4x9el98zjzmhcveqpfeur79"Using RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 6, "jsonrpc": "2.0", "method": "send_payment", "params": [ { "invoice": "fibt1000000001peseucdphcxgfw0pnm6vk3uftyc36dakyjchs0p0unk9gaug0h36uhafww9pvy38gcesad084rx48xgx9xts49yp9fn87yfchld3l3qu5n0pfzvvy8c9g7dksrcxyrtk3hymspezmvtx4vg5v6uvt6tyxmq5uhrfejpk0j6wue9ef2pa8mzmrgqaz3wucutujtjcmq2x8f36faxuctg62ny73mhaj7rpwqe0ns0wp5wr4tku7qcl9r4a3swluvd2jqqwmsl7wsz4cwvhhe7p8tr7hz5qkqwr3r38hukckqzjtmntd8zrz0ywux4u8df005hl76thzsp9hz7dyefzk4mqhx4x9el98zjzmhcveqpfeur79" } ] }'{"jsonrpc":"2.0","result":{"payment_hash":"0xafb604f74c28009732ed4c82983cf1efaddf62ee36442f360fb4a8c79b845432","status":"Created","created_at":"0x1958957cc7d","last_updated_at":"0x1958957cc7d","failed_error":null,"fee":"0x186a0"},"id":6} -
Repeat Steps 4 and 6 two more times
Performing two additional
new_invoiceandsend_paymentrequests, keeping the amount set to 0x5f5e100. -
Query the local_balance and remote_balance of each channel again
nodeA ⟺ node1
Balances changed from
{"local_balance":"0xa32aef600","remote_balance":"0x460913c00"}to{"local_balance":"0xa2cb78e60","remote_balance":"0x46688a3a0"}.node1 ⟺ node2
Balances changed from
{"local_balance":"0x48ab5acd200","remote_balance":"0x49087ca8c00"}to{"local_balance":"0x48aa3cb2f00","remote_balance":"0x49099ac2f00"}.All other entries remain unchanged.
This means the channel balances have changed as follows before and after the payments:
-
Before payments
nodeA (43800000000) ⟺ node1 (18800000000)
node1 (4993800000000) ⟺ node2 (5018800000000)
-
After payments
nodeA (43499700000) ⟺ node1 (19100300000)
node1 (4993500000000) ⟺ node2 (5019100000000)
Funds changes:
nodeA: 43499700000 - 43800000000 = -300300000
node1: 4993500000000 + 19100300000 - 4993800000000 - 18800000000 = 300000
node2: 5019100000000 - 5018800000000 = 300000000
Conclusion: Three CKB payments of 100,000,000 shannon each from nodeA → node1 → node2 were successfully completed. The intermediate node (node1) earned a total fee of 300,000 shannon.
-
-
Close the channel between nodeA and node1
Pass in the channel_id and the receiving address as parameters.
Using CLI:
./fnn-cli channel shutdown_channel \ --channel-id 0x26ce85d57fb4a1a826cbf4862358862317a83b775090625550d8be12c6ce9569 \ --close-script '{"code_hash":"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8","hash_type":"type","args":"0xcc015401df73a3287d8b2b19f0cc23572ac8b14d"}'Using RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 9, "jsonrpc": "2.0", "method": "shutdown_channel", "params": [ { "channel_id": "0x26ce85d57fb4a1a826cbf4862358862317a83b775090625550d8be12c6ce9569", "close_script": { "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", "hash_type": "type", "args": "0xcc015401df73a3287d8b2b19f0cc23572ac8b14d" }, "fee_rate": "0x3FC" } ] }'{"jsonrpc":"2.0","result":null,"id":9}You can see on the CKB explorer that nodeA's address received a new transaction of +496.99699462 CKB. This indicates that multiple off-chain CKB transfers through Fiber nodes are eventually settled on-chain upon channel closure via the shutdown_channel request.
Establishing a UDT Channel with Public Node 1
-
Establish a network connection between nodeA and node1
Using CLI:
./fnn-cli peer connect_peer --address "/ip4/18.162.235.225/tcp/8119/p2p/QmXen3eUHhywmutEzydCsW4hXBoeVmdET2FJvMX69XJ1Eo"Using RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 1, "jsonrpc": "2.0", "method": "connect_peer", "params": [ { "pubkey": "<node1_pubkey>", "address": "/ip4/18.162.235.225/tcp/8119/p2p/QmXen3eUHhywmutEzydCsW4hXBoeVmdET2FJvMX69XJ1Eo" } ] }' -
Establish a channel with 20 RUSD: nodeA (20 RUSD) ⟺ node1 (0)
Node1 has auto_accept_amount for RUSD set to 20 RUSD, so please input 20 RUSD or more as the funding_amount.
Using CLI:
./fnn-cli channel open_channel \ --pubkey <node1_pubkey> \ --funding-amount 20 \ --public true \ --funding-udt-type-script '{"code_hash":"0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a","hash_type":"type","args":"0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b"}'Using RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 2, "jsonrpc": "2.0", "method": "open_channel", "params": [ { "pubkey": "<node1_pubkey>", "funding_amount": "0x2540be400", "public": true, "funding_udt_type_script": { "code_hash": "0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a", "hash_type": "type", "args": "0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b" } } ] }'{"jsonrpc":"2.0","result":{"temporary_channel_id":"0xa3137338377b67ea90c2f2c15b7d60ad27b3e891095f4b093772d7db3aa79344"},"id":2} -
Query the channels between nodeA and node1
Using CLI:
./fnn-cli channel list_channelsUsing RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 3, "jsonrpc": "2.0", "method": "list_channels", "params": [ { "peer_pubkey": "<node1_pubkey>" } ] }'{"jsonrpc":"2.0","result":{"channels":[{"channel_id":"0x75dce35923a79086afd0f81b0134ac87619756b6c04a15669ce232aa7db142d8","is_public":true,"channel_outpoint":"0x8e133056792766e1fd34e870fb33990b58c4ebb9615526b38dacdf3686cf6d3f00000000","peer_pubkey":"<node1_pubkey>","funding_udt_type_script":{"code_hash":"0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a","hash_type":"type","args":"0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b"},"state":{"state_name":"ChannelReady","state_flags":[]},"local_balance":"0x2540be400","offered_tlc_balance":"0x0","remote_balance":"0x0","received_tlc_balance":"0x0","latest_commitment_transaction_hash":"0x2b0b36c5db14778484358a4641bfe00a4f351660c280255ef8e8538898e399d0","created_at":"0x1958977b7be","enabled":true,"tlc_expiry_delta":"0x5265c00","tlc_fee_proportional_millionths":"0x3e8"}]},"id":3} -
Call the
new_invoiceAPI on node2 to generate an invoiceSet the amount to 0x5f5e100 (100,000,000), which is equivalent to 1 RUSD.
Here, a unique payment_preimage is still required. You can generate one using:
echo "0x$(openssl rand -hex 32)"Using CLI (on node2's host):
./fnn-cli --url http://18.163.221.211:8227 invoice new_invoice \ --amount 100000000 \ --currency Fibt \ --description "test invoice generated by node2" \ --expiry 3600 \ --udt-type-script '{"code_hash":"0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a","hash_type":"type","args":"0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b"}'Using RPC:
curl -s --location 'http://18.163.221.211:8227' --header 'Content-Type: application/json' --data '{ "id": 4, "jsonrpc": "2.0", "method": "new_invoice", "params": [ { "amount": "0x5f5e100", "currency": "Fibt", "description": "test invoice generated by node2", "expiry": "0xe10", "payment_preimage": "0xf7d121b132b4f53bb8301591028b34fccc065f92161bb6e7d41cf6d32ad32a22", "hash_algorithm": "sha256", "udt_type_script": { "code_hash": "0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a", "hash_type": "type", "args": "0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b" } } ] }'{"jsonrpc":"2.0","result":{"invoice_address":"fibt1000000001px88ja42xcmczxzat8lhtuq9f29ga8x244qk737nl4r7lq8aw7y7puhjn6jp50xsd2c6ndfxkmn5wnl4z8clk7fej9trwx0gjlmtvnj2wqwlvcu0eekzqvtehlc42t8lpstmgc7ntskh5ef36f8hgvck8c9pescktlx05fpuaceews94kvyrvgf87gvd9wnmh86puzyz2vp6h6jppt8lsq5u8tc87y6szha9587f90dmlmwt5mtetxz9ekukxu6x7s2fyuuy2re0etzzksqnt8rtr5925qypz2224j5xf56nlscnmtvcvywdxg40hsy5w5xt40d5cdest3kvhqswfftfc3qqs7plhlk7m5n9hyzqws9qlxw2huurg7l6c4q9evyg7fljcl3cqh3h3ecpg3fue3cq4slpxapvc2uye6jl77sfcflc8jf8fvr4qwly9wxuyehqf573hu454qy92wqke0hdgrvm7y83sgspn4a29h69s7ucp4cedle","invoice":{"currency":"Fibt","amount":"0x5f5e100","signature":"161b1405170f18161b1e1c090c0a0a010a190409061c1b1a1107130413040f1a0204080504121212150f1b031b171a040417030102191d010a1317090f1f1c120e1909121b0d041b17101f071e1b020d170a151a15121d0d081d12151816171c0d000215190a1801","data":{"timestamp":"0x1958e785913","payment_hash":"0x6a356ad088b704a9c53728029bd968e894daf5adab1da838bf06f6755239b005","attrs":[{"description":"test invoice generated by node2"},{"expiry_time":{"secs":3600,"nanos":0}},{"udt_script":"0x550000001000000030000000310000001142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a0120000000878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b"},{"hash_algorithm":"sha256"},{"payee_public_key":"0291a6576bd5a94bd74b27080a48340875338fff9f6d6361fe6b8db8d0d1912fcc"}]}}},"id":4} -
Before nodeA sends the payment, first query the local_balance and remote_balance of each channel
nodeA ⟺ node1
As shown in Step 3, the response included:
{"local_balance":"0x77359400","remote_balance":"0x0"}node1 ⟺ node2
Using CLI:
./fnn-cli --url http://18.162.235.225:8227 channel list_channelsUsing RPC:
curl -s --location 'http://18.162.235.225:8227' --header 'Content-Type: application/json' --data '{ "id": 5, "jsonrpc": "2.0", "method": "list_channels", "params": [ { "peer_pubkey": "<node2_pubkey>" } ] }'Find all entries in the response where
funding_udt_type_scriptis not null.{"local_balance":"0x172a2c63bb","remote_balance":"0x1e4a8445"} {"local_balance":"0x1748630df7","remote_balance":"0x13da09"} {"local_balance":"0xa38b9d","remote_balance":"0x1747d35c63"} {"local_balance":"0xc505f","remote_balance":"0x17486a97a1"} -
Send a send_payment request to nodeA to pay node2
Using CLI:
./fnn-cli payment send_payment --invoice "fibt1000000001px88ja42xcmczxzat8lhtuq9f29ga8x244qk737nl4r7lq8aw7y7puhjn6jp50xsd2c6ndfxkmn5wnl4z8clk7fej9trwx0gjlmtvnj2wqwlvcu0eekzqvtehlc42t8lpstmgc7ntskh5ef36f8hgvck8c9pescktlx05fpuaceews94kvyrvgf87gvd9wnmh86puzyz2vp6h6jppt8lsq5u8tc87y6szha9587f90dmlmwt5mtetxz9ekukxu6x7s2fyuuy2re0etzzksqnt8rtr5925qypz2224j5xf56nlscnmtvcvywdxg40hsy5w5xt40d5cdest3kvhqswfftfc3qqs7plhlk7m5n9hyzqws9qlxw2huurg7l6c4q9evyg7fljcl3cqh3h3ecpg3fue3cq4slpxapvc2uye6jl77sfcflc8jf8fvr4qwly9wxuyehqf573hu454qy92wqke0hdgrvm7y83sgspn4a29h69s7ucp4cedle"Using RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 6, "jsonrpc": "2.0", "method": "send_payment", "params": [ { "invoice": "fibt1000000001px88ja42xcmczxzat8lhtuq9f29ga8x244qk737nl4r7lq8aw7y7puhjn6jp50xsd2c6ndfxkmn5wnl4z8clk7fej9trwx0gjlmtvnj2wqwlvcu0eekzqvtehlc42t8lpstmgc7ntskh5ef36f8hgvck8c9pescktlx05fpuaceews94kvyrvgf87gvd9wnmh86puzyz2vp6h6jppt8lsq5u8tc87y6szha9587f90dmlmwt5mtetxz9ekukxu6x7s2fyuuy2re0etzzksqnt8rtr5925qypz2224j5xf56nlscnmtvcvywdxg40hsy5w5xt40d5cdest3kvhqswfftfc3qqs7plhlk7m5n9hyzqws9qlxw2huurg7l6c4q9evyg7fljcl3cqh3h3ecpg3fue3cq4slpxapvc2uye6jl77sfcflc8jf8fvr4qwly9wxuyehqf573hu454qy92wqke0hdgrvm7y83sgspn4a29h69s7ucp4cedle" } ] }'{"jsonrpc":"2.0","result":{"payment_hash":"0x6a356ad088b704a9c53728029bd968e894daf5adab1da838bf06f6755239b005","status":"Created","created_at":"0x1958e7b66b5","last_updated_at":"0x1958e7b66b5","failed_error":null,"fee":"0x186a0"},"id":6} -
Repeat Steps 4 and 6 two more times
Performing two additional
new_invoiceandsend_paymentrequests, keeping the amount set to 0x5f5e100. -
Query the local_balance and remote_balance of each channel again
nodeA ⟺ node1
Balances changed from
{"local_balance":"0x77359400","remote_balance":"0x0"}to{"local_balance":"0x654f5d20","remote_balance":"0x11e636e0"}.node1 ⟺ node2
Balances changed from
{"local_balance":"0x172a2c63bb","remote_balance":"0x1e4a8445"}to{"local_balance":"0x17184ac0bb","remote_balance":"0x302c2745"}.All other entries remain unchanged.
This means the channel balances have changed as follows before and after the payments:
-
Before payments
nodeA (2000000000) ⟺ node1 (0)
node1 (99491799995) ⟺ node2 (508200005)
-
After payments
nodeA (1699700000) ⟺ node1 (300300000)
node1 (99191799995) ⟺ node2 (808200005)
Funds changes:
nodeA: 1699700000 - 2000000000 = -300300000
node1: 99191799995 + 300300000 - 99491799995 = 300000
node2: 808200005 - 508200005 = 300000000
Conclusion: Three UDT payments of 100,000,000 each from nodeA → node1 → node2 were successfully completed. The intermediate node (node1) earned a total fee of 300,000.
-
-
Close the channel between nodeA and node1
Using CLI:
./fnn-cli channel shutdown_channel \ --channel-id 0x75dce35923a79086afd0f81b0134ac87619756b6c04a15669ce232aa7db142d8 \ --close-script '{"code_hash":"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8","hash_type":"type","args":"0xcc015401df73a3287d8b2b19f0cc23572ac8b14d"}'Using RPC:
curl -s --location 'http://127.0.0.1:8227' --header 'Content-Type: application/json' --data '{ "id": 9, "jsonrpc": "2.0", "method": "shutdown_channel", "params": [ { "channel_id": "0x75dce35923a79086afd0f81b0134ac87619756b6c04a15669ce232aa7db142d8", "close_script": { "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", "hash_type": "type", "args": "0xcc015401df73a3287d8b2b19f0cc23572ac8b14d" }, "fee_rate": "0x3FC" } ] }'{"jsonrpc":"2.0","result":null,"id":9}You can see on the CKB explorer that nodeA's address received a new transaction of +16.997 RUSD. This indicates that multiple off-chain UDT transfers through Fiber nodes are eventually settled on-chain upon channel closure via the shutdown_channel request.