Skip to content

Validating RGB Assets on Lightning Network ​

A note before reading:

This demo is designed to step-by-step verify the integration of rgbldkd with rgbdex.net in a local Regtest environment, without modifying any production code.

The Stash Labs team has successfully verified a complete end-to-end demo of RGB assets running natively on the Lightning Network, powered by Bitlight Labs' rgbldkd node implementation. This post documents what we validated, how the system is architected, and the key protocol insights we gained along the way.

What We Validated ​

In 13 sequential steps, we demonstrated the full lifecycle of an RGB20 token — from issuance on a regtest Bitcoin chain, through a Lightning channel opening that simultaneously carries BTC and RGB assets, to an atomic swap between BTC and RGB tokens using Hold Invoices. Each step was driven by a React frontend calling a Node.js proxy server that relayed commands to two rgbldkd nodes (Alice and Bob) running in Docker.

All 13 steps passed validation:

#StepStatusVerification Notes
01Check Nodes✅Both Alice and Bob rgbldkd nodes running; Node IDs (secp256k1 pubkeys) retrieved successfully
02Mine 101 Blocks✅Regtest coinbase maturity confirmed; 101 blocks mined so block 1's reward is spendable
03Fund Alice BTC✅1 BTC sent to Alice's LN wallet; balance confirmed after wallet/sync
04Fund Alice RGB UTXO✅0.01 BTC UTXO prepared as Genesis Seal for RGB contract issuance
05Import Issuer Template✅RGB20-Simplest-v0 AluVM bytecode imported into Alice's node via binary upload
06Issue RGB20 Contract✅1,000,000 TTK issued; contract_id and asset_id returned; commitment anchored on-chain
07Share Contract → Bob✅Consignment exported from Alice, verified and imported by Bob — fully off-chain
08Fund Bob BTC✅0.05 BTC sent to Bob's wallet; available for Hold Invoice payment leg
09Connect Peers✅Alice → Bob Noise Protocol TCP connection established over Docker internal network
10Open RGB LN Channel✅Dual-asset Funding TX created: 2,000,000 sats + 500,000 TTK in one output
11Confirm Channel✅6-block confirmation reached; ChannelReady event received on both sides
12RGB LN Payment✅Alice sent Bob 100 TTK instantly via Lightning; no block confirmation required
13Hold Invoice Swap✅Bob locked 10,000 sats via HTLC; Alice delivered 200 TTK; preimage released to settle

System Architecture ​

The demo environment runs entirely on a single host:

┌────────────────────────────────────────────────────────────┐
│                        Host (macOS)                         │
│                                                             │
│  ┌──────────────┐     ┌───────────────────────────────┐    │
│  │  Browser     │     │    Docker Network 172.18.0.0/24│    │
│  │  :5173 Vite  │     │                               │    │
│  └──────┬───────┘     │  bitcoind       172.18.0.2    │    │
│         │ HTTP        │  esplora        172.18.0.3    │    │
│  ┌──────▼───────┐     │  rgb-node-alice 172.18.0.4    │    │
│  │ server.mjs   │◄────►  rgb-node-bob   172.18.0.5    │    │
│  │  :3000 Proxy │     │                               │    │
│  └──────────────┘     └───────────────────────────────┘    │
└────────────────────────────────────────────────────────────┘
ComponentTechnologyRole
bitcoindBitcoin Core 24.0.1, RegtestLocal test chain, BTC transfers, block mining
esploraBitlight custom EsploraBlockchain indexer, serves Electrum protocol to rgbldkd for UTXO sync
rgb-node-alice/bobbitlightlabs/rln-ldk-nodeRGB+LDK fusion node managing BTC wallet, RGB assets, and Lightning channels
server.mjsNode.js HTTP (no framework)Proxy: CORS handling, binary file relay, composite operations
Vite + ReactReact 18 + Tailwind CSSWizard-style UI, one step per group of API calls

The proxy server (server.mjs) solves two fundamental browser limitations: rgbldkd does not set Access-Control-Allow-Origin headers, and binary consignment data cannot be streamed directly from the browser. The proxy handles both, presenting a clean REST surface to the frontend.


Step-by-Step Walkthrough ​

Steps 1–4: Foundation ​

Step 1 — Check Nodes queries both nodes for their node_id (secp256k1 public keys), which doubles as the Lightning Network P2P identity for the Noise Protocol handshake later.

Step 2 — Mine 101 Blocks creates the initial Bitcoin coinbase maturity. Bitcoin requires 100 confirmations before coinbase outputs can be spent; mining 101 blocks makes the first block's reward available. The Docker bitcoind-init container handles this automatically on startup.

Step 3 — Fund Alice BTC sends 1 BTC to Alice's rgbldkd wallet. The node uses BDK (Bitcoin Dev Kit) internally and syncs via Esplora — wallet/sync must be called explicitly since the node does not listen reactively.

Step 4 — Fund Alice RGB UTXO is a critical preparation step. RGB protocol uses Single-Use Seals: asset ownership is committed to a specific UTXO. When that UTXO is spent, the RGB state transition occurs. This requires RGB and BTC wallets to manage UTXOs independently, hence Alice needs a dedicated RGB address funded with 0.01 BTC before any token issuance.

Steps 5–7: RGB20 Token Issuance & Distribution ​

Step 5 — Import Issuer Template loads a .issuer binary file — compiled AluVM bytecode — into Alice's node. AluVM is RGB's virtual machine; contract logic executes locally on each recipient's machine, never on-chain. The RGB20-Simplest-v0 template is a stripped-down RGB20 implementation ideal for demos.

Step 6 — Issue RGB20 Contract creates 1,000,000 TTK tokens:

json
{
  "issuer_name": "RGB20-Simplest-v0-rLosfg",
  "contract_name": "TestToken",
  "ticker": "TTK",
  "precision": 0,
  "issued_supply": "1000000"
}

Internally, the node consumes the Step 4 UTXO as the Genesis Seal, constructs the Genesis State (Alice owns all 1M TTK), generates a globally unique contract_id, and embeds a cryptographic commitment into a Bitcoin transaction using Tapret or OP_RETURN. No new on-chain data structure broadcasts the token — only a commitment hash appears on-chain.

Step 7 — Share Contract → Bob distributes the contract to Bob's node entirely off-chain via the Consignment mechanism. The proxy server orchestrates three calls atomically:

  1. Alice exports a consignment_key
  2. The proxy downloads the binary consignment zip from Alice
  3. The proxy uploads it to Bob

Bob independently verifies the full contract history against the Bitcoin blockchain. If every anchored commitment is found in a confirmed transaction, Bob accepts the contract. Alice cannot forge this history — the verification is entirely on Bob's side.

Steps 8–11: Lightning Channel with RGB Assets ​

Step 8 — Fund Bob BTC gives Bob 0.05 BTC for later use in the atomic swap payment leg.

Step 9 — Connect Peers establishes the Lightning P2P TCP connection between Alice and Bob using the Noise Protocol (BOLT #8). The connection uses Docker internal IPs (172.18.0.5:9735) since P2P happens container-to-container.

Step 10 — Open RGB LN Channel is where RGB meets Lightning in a single Funding Transaction:

json
{
  "channel_amount_sats": "2000000",
  "push_to_counterparty_msat": "50000000",
  "rgb": {
    "asset_id": "<asset_id>",
    "asset_amount": "500000",
    "color_context_data": "http://172.18.0.4:8500/api/v1/rgb/consignments/{txid}?format=zip"
  }
}

The BTC side follows standard LDK: a 2-of-2 multisig Funding Output with 2M sats, pushing 50K sats to Bob for initial outbound liquidity. The RGB side binds 500,000 TTK ownership to the same Funding Transaction output. The color_context_data URL tells Bob where to download the RGB Consignment to verify Alice's token ownership independently before accepting the channel.

Step 11 — Confirm Channel mines enough blocks to reach the 6-confirmation threshold required by BOLT #2. The demo uses a dual-confirmation strategy: first it waits for a ChannelReady event from the node's event queue, and falls back to polling the /channels API if the event was already consumed (e.g., after a page refresh).

Step 12: RGB Lightning Payment ​

With the channel open, Alice pays Bob 100 TTK instantly with no block confirmation:

json
{
  "asset_id": "<asset_id>",
  "asset_amount": "100",
  "btc_carrier_amount_msat": "3000000"
}

The key design insight here: RGB tokens cannot be routed in the Lightning Network independently — they have no millisatoshi denomination. Every RGB LN payment must piggyback on a small BTC HTLC (the "carrier"). The 3,000 sat carrier activates the HTLC routing path; the RGB state update travels alongside it in the off-chain protocol layer. Payment completes in milliseconds with no on-chain footprint.

Step 13: Hold Invoice Atomic Swap ​

This is the demo's most sophisticated feature — a BTC ↔ RGB token swap implemented using Hold Invoices. The swap: Bob pays 10,000 sats, Alice delivers 200 TTK.

How it works ​

① Alice creates a Hold Invoice

Alice generates a random 32-byte preimage locally and computes paymentHash = SHA256(preimage). She creates a Hold Invoice for 10,000 sats locked to paymentHash, but does not reveal the preimage yet.

preimage = random_bytes(32)       // kept secret by Alice
paymentHash = SHA256(preimage)    // published in the invoice

② Bob pays the invoice (HTLC locked)

Bob sends the payment. His 10,000 sats are now locked in an HTLC:

HTLC condition: reveal x where SHA256(x) == paymentHash
→ funds locked, Bob cannot unilaterally cancel before expiry

This is the "commitment" phase. Bob's funds are locked but Alice hasn't received them yet.

③ Alice sends 200 TTK to Bob

Seeing the HTLC is locked, Alice now sends the RGB payment to Bob via the same Lightning channel. This is a standard RGB LN payment and settles immediately.

④ Alice claims BTC by revealing preimage

json
{
  "payment_hash": "<paymentHash>",
  "preimage": "<preimage>",
  "claimable_amount_msat": "10000000"
}

Alice calls claim_for_hash, broadcasts the preimage, and the HTLC settles — she receives 10,000 sats.

Final state:

Alice: +10,000 sats,  -200 TTK
Bob:   -10,000 sats,  +200 TTK

The production rgb-lightning-node protocol achieves true cryptographic atomicity without DLC or cross-chain coordination. It does so through a circular Lightning route: Maker is both the start and end of the route, Taker sits in the middle as the conversion point, and a single payment_hash locks both legs simultaneously. When Maker reveals the preimage at the destination, both the inbound and outbound legs settle at once — atomicity is guaranteed by the routing topology itself, not by any additional protocol layer.


Key Protocol Insights ​

RGB's Client-Side Validation Model ​

Traditional On-Chain TokensRGB
All transfers publicly visibleOnly counterparties know transfer history
Contract state lives on-chainContract state lives with the recipient
Validation by full nodesValidation by the recipient locally
All data permanently on-chainOnly commitments (hashes) on-chain

This design gives RGB three important properties: privacy (transfer history is not globally visible), scalability (no per-transfer on-chain data), and sovereignty (you validate your own funds independently).

Single-Use Seals ​

Every RGB state transition is bound to a UTXO spend. When the UTXO is spent, the seal is "broken" — the RGB transition is executed irreversibly. This prevents double-spending at the protocol level without requiring a global ledger of RGB transactions.

Consignments as Proof Packages ​

When you receive RGB assets, you receive a Consignment: a complete audit trail from the Genesis transaction to the current state. You verify this trail locally against Bitcoin transactions. The sender cannot manipulate this history — all RGB commitments are anchored in confirmed Bitcoin blocks that anyone can verify independently.

BTC as RGB's Lightning Carrier ​

RGB tokens cannot exist in Lightning's HTLC routing natively (no msat value). The btc_carrier_amount_msat field in every RGB LN payment is the minimum BTC required to "carry" the RGB state update through the channel. The BTC carrier and RGB payload travel together; if the BTC HTLC fails, the RGB transfer fails too.


Open Source Reference ​

The full verification demo code is available at rgbstashlabs/rgb-ldk-verify-demo. It includes the complete Docker infrastructure, proxy server, and React frontend. Developers can run the full 13-step flow locally on any macOS or Linux machine with Docker.

RGB assets on Lightning represent one of the most significant capability expansions for Bitcoin's programmability layer. The ability to issue, transfer, and swap custom assets inside off-chain channels — with Bitcoin's security guarantees and client-side privacy — opens the door to a new class of Lightning-native DeFi applications that don't require a separate blockchain.

Trade and secure your RGB assets | Unlock the full potential of the RGB protocol