1. Local RGB20 Issuance and Transfer Troubleshooting Record (bitlight-local-env-public)
1. Background & Objectives
Based on bitlightlabs/bitlight-local-env-public (opens in a new tab), set up a regtest local chain and complete the entire process of RGB20 asset issuance โ transfer โ validation.
Key Point: The tutorial corresponds to rgb CLI v0.11.0-beta.9 (old version). New CLI commands are incompatible, must use the version bundled in the project container or compile the same version yourself.
2. Environment Setup
Start Local Chain
git clone https://github.com/bitlightlabs/bitlight-local-env-public
cd bitlight-local-env-public
make startEnter Wallet Container (will print XPRV / XPUB / addresses)
make alice-cli # Enter alice wallet REPL
# Note down Alice's Fixed XPUB (for later descriptor creation)
# Note down Alice's Bitcoin address (for funding later)
make bob-cli # Enter bob wallet REPL
# Similarly note down Bob's Fixed XPUB and addressPrepare Bitcoin Test Coins
make core-cli
mint 1 # Simple method, mine one block to the mining wallet, can later send to Alice/Bob addresses per tutorial3. Issuance & Transfer (Reproducible Steps)
All commands below are run on the
host machine
3.1 Create RGB Wallet Using XPUB
Fill the Fixed XPUB (containing <0;1;9;10>/*) seen in the container into environment variables:
ALICE_DESC="[5183a8d8/86'/1'/0']tpubDDtdVYn7LWnWNUXADgoLGu48aLH4dZ17hYfRfV9rjB7QQK3BrphnrSV6pGAeyfyiAM7DmXPJgRzGoBdwWvRoFdJoMVpWfmM9FCk8ojVhbMS/<0;1;9;10>/*"
BOB_DESC="[3abb3cbb/86'/1'/0']tpubDDeBXqUqyVcbe75SbHAPNXMojntFu5C8KTUEhnyQc74Bty6h8XxqmCavPBMd1fqQQAAYDdp6MAAcN4e2eJQFH3v4txc89s8wvHg98QSUrdL/<0;1;9;10>/*"Create wallets (old version CLI has no init subcommand):
rgb -d .alice -n regtest create default --tapret-key-only "$ALICE_DESC"
rgb -d .bob -n regtest create default --tapret-key-only "$BOB_DESC"Check UTXOs (confirm both sides have funds):
rgb -d .alice -n regtest utxos
rgb -d .bob -n regtest utxos3.2 Import Contract (issuance already completed in contract file)
First generate rgb20-simplest.rgb in your contract project (e.g., bitlight-rgb20-contract), then import on host machine:
CONTRACT_FILE="/absolute/path/bitlight-rgb20-contract/test/rgb20-simplest.rgb"
rgb -d .alice -n regtest import "$CONTRACT_FILE" --esplora=http://localhost:3002
rgb -d .bob -n regtest import "$CONTRACT_FILE" --esplora=http://localhost:3002Check contract list and note down the contract ID (contains special characters like !, $, use single quotes):
rgb -d .alice -n regtest contracts
# Example: BppYGUUL-Qboz3UD-czwAaVV-!!Jkr1a-SE1!m1f-Cz$b0xs
RGB20_CONTRACT='BppYGUUL-Qboz3UD-czwAaVV-!!Jkr1a-SE1!m1f-Cz$b0xs'3.3 Bob Generates Payment Invoice
Old version invoice has no amount parameter, need to manually insert amount field:
INV_NOAMT=$(rgb -d .bob -n regtest invoice "$RGB20_CONTRACT")
# Replace "/RGB20Fixed/" with "/RGB20Fixed/TadF+"
INVOICE=$(printf '%s' "$INV_NOAMT" | sed 's#RGB20Fixed/#RGB20Fixed/TadF+#')
echo "$INVOICE"3.4 Alice Creates Transfer (generates consignment + PSBT)
rgb -d .alice -n regtest transfer "$INVOICE" transfer.consignment alice.psbt3.5 Bob Validates and Accepts Transfer
rgb -d .bob -n regtest validate transfer.consignment
rgb -d .bob -n regtest accept -f transfer.consignment3.6 Alice Signs PSBT (using bdk-cli)
Convert alice.psbt to base64, paste into bdk-cli REPL's wallet sign:
# On host machine
PB64=$(base64 < alice.psbt | tr -d '\r\n')
echo "$PB64" # Copy entire string
# In wallet container (make alice-cli) execute:
wallet sign --psbt "<entire base64 string from above>"Take the signed base64 from "psbt" field in the returned JSON from REPL, save and decode:
cat > alice.psbt.signed.b64 <<'EOF'
<paste complete base64 from "psbt" field in JSON here>
EOF
base64 -D -i alice.psbt.signed.b64 > alice.psbt.signed3.7 Finalize, Generate Raw Transaction and Broadcast
rgb -d .alice -n regtest finalize alice.psbt.signed alice.final.tx
# Broadcast using Esplora API
HEX=$(xxd -p -c 999 alice.final.tx)
TXID=$(curl -s -X POST http://localhost:3002/tx -d "$HEX")
echo "TXID=$TXID"3.8 Mine Block for Confirmation & Sync State
make core-cli
mint 1
# Sync and check RGB state (check both sides once each)
rgb -d .alice -n regtest state "$RGB20_CONTRACT" RGB20Fixed --sync --esplora=http://localhost:3002
rgb -d .bob -n regtest state "$RGB20_CONTRACT" RGB20Fixed --sync --esplora=http://localhost:3002
# History
rgb -d .alice -n regtest history "$RGB20_CONTRACT"
rgb -d .bob -n regtest history "$RGB20_CONTRACT"4. Pitfalls & Key Points
- Version Alignment: Tutorial targets rgb-wallet v0.11.0-beta.9. New version command sets are different, must use same version CLI from container or self-compiled.
- Data Directory: Old version places data in .alice/regtest, .bob/regtest. Don't mix with new version's bitcoin.testnet/bitcoin.regtest.
- Special Characters: Contract ID and invoice contain !, $, etc., zsh must use single quotes '...', otherwise will be consumed by history expansion.
- On-chain Parsing: Commands involving chain interaction (import, state --sync, etc.) add --esplora=http://localhost:3002 (opens in a new tab), otherwise will report no resolver specified.
- Invoice Amount: invoice has no amount, need to manually replace RGB20Fixed/ with RGB20Fixed/TadF+<amount>.
- Signing Tool: bdk-cli new version parameters are wallet --database-type sqlite --ext-descriptor --int-descriptor, in REPL use wallet sign --psbt "<base64>" most stable.
- Broadcast Method: Host's bitcoin-cli not connected to container data directory, directly use Esplora /tx endpoint for broadcast most convenient.
- Sync Refresh: After completing broadcast use state ... --sync --esplora=... to force refresh witness and balance; mint 1 to mine block for confirmation if necessary.
5. Reference Repositories
- Local chain and wallet container: https://github.com/bitlightlabs/bitlight-local-env-public (opens in a new tab)
- RGB v0.11 source code (can compile rgb CLI same version locally): https://github.com/RGB-WG/rgb (opens in a new tab)
- RGB20 contract example: https://github.com/bitlightlabs/bitlight-rgb20-contract (opens in a new tab)
Flow Diagram:
โโโโโโโโโโโโ
โ Alice โ
โ(Issuer) โ
โโโโโโโฌโโโโโ
โ
โ 1. Issue RGB20 contract in REPL
โ - Specify Ticker / Name / Precision / Amount
โ - Select a local UTXO as anchor point during issuance
โผ
โโโโโโโโโโโโ
โ Bitcoin โ
โBlockchain โ
โโโโโโโฌโโโโโ
โ
โ 2. There exists an "anchor transaction" carrying RGB contract
โ (Anchor UTXO: an output in Alice's wallet)
โผ
โโโโโโโโโโโโ
โ Bob โ
โ(Receiver) โ
โโโโโโโฌโโโโโ
โ
โ 3. Bob generates invoice
โ - Contains Bob's receiving seal + asset ID
โผ
โโโโโโโโโโโโ
โ Alice โ
โ(Issuer) โ
โโโโโโโฌโโโโโ
โ
โ 4. Alice generates transfer consignment based on invoice
โ 5. Alice signs transaction with her private key (PSBT)
โ 6. Broadcast to Bitcoin chain
โผ
โโโโโโโโโโโโ
โ Bitcoin โ
โBlockchain โ
โโโโโโโฌโโโโโ
โ
โ 7. After transaction confirmation, both sides sync state
โผ
โโโโโโโโโโโโ
โ Alice โ
โ Bob โ
โโโโโโโโโโโโ# Local RGB20 Issuance & Transfer โ Simplified Command Table
1๏ธโฃ Start Environment
--------------------------------
git clone https://github.com/bitlightlabs/bitlight-local-env-public
cd bitlight-local-env-public
make start
2๏ธโฃ Enter Alice Wallet Container (Issuer)
--------------------------------
make alice-cli
# Select corresponding descriptor to enter REPL
3๏ธโฃ Issue RGB20 Contract
--------------------------------
rgb -d .alice -n regtest issue \
--ticker TEST --name "Test asset" \
--precision 2 --amount 100000000000 \
--iface RGB20Fixed
# Record the output CONTRACT_ID
4๏ธโฃ Enter Bob Wallet Container (Receiver)
--------------------------------
make bob-cli
# Generate invoice
rgb -d .bob -n regtest invoice "$CONTRACT_ID" RGB20Fixed 2000 > bob.invoice
5๏ธโฃ Alice Creates Transfer Based on Invoice
--------------------------------
# Execute on host machine
rgb -d .alice -n regtest transfer bob.invoice > alice.psbt
6๏ธโฃ Sign Transaction (Inside Alice Container)
--------------------------------
PB64=$(base64 < alice.psbt | tr -d '\r\n')
wallet sign --psbt "$PB64" > signed.json
# Extract psbt field from signed.json, save as alice.psbt.signed
7๏ธโฃ Broadcast Transaction (Host Machine)
--------------------------------
xxd -p -c 999 alice.final.tx > hex.txt
HEX=$(cat hex.txt)
docker compose -p bitlight-local-env exec -T core-cli \
bitcoin-cli -regtest sendrawtransaction "$HEX"
8๏ธโฃ Mine for Confirmation
--------------------------------
make core-cli
mint 1
9๏ธโฃ Sync and Check State
--------------------------------
rgb -d .alice -n regtest state "$CONTRACT_ID" RGB20Fixed --sync --esplora=http://localhost:3002
rgb -d .bob -n regtest state "$CONTRACT_ID" RGB20Fixed --sync --esplora=http://localhost:3002Complete: RGB Asset Transfer On-chain Structure & CLI Operation Reference
**STEP 1: Alice Issues Asset (Anchor Commitment)**
-------------------------------------------
On-chain Structure:
[ Alice Internal PubKey ]
|
+-- commitment_hash = H( RGB_contract_state )
|
+-- tweak = H( internal_pubkey || commitment_hash )
|
+-- tweaked_pubkey = internal_pubkey + tweak*G
|
--> On-chain Taproot Output:
scriptPubKey = OP_1 <tweaked_pubkey>
(UTXO_A as anchor for RGB asset)
Bitlight CLI:
# Enter Alice REPL
make alice-cli
# Select RGB Descriptor
# Issue RGB20 asset
rgb -d .alice -n regtest issue --ticker TEST --name "Test asset" --amount 100000000000 RGB20Fixed
**STEP 2: Bob Invoice (Invoice / Seal)**
-----------------------------------
On-chain Structure:
[ Bob Internal PubKey ] --or--> [ Bob blind seal hash ]
|
+-- (Optional) blinding factor r
|
--> Invoice sent off-chain to Alice:
"Please lock N units of Asset_X to this seal"
Bitlight CLI:
# Enter Bob REPL
make bob-cli
# Generate invoice (seal information here)
rgb -d .bob -n regtest invoice "$RGB20_CONTRACT" 2000
# Get an rgb:... invoice string, send to Alice
**STEP 3: Alice Transfer (Commit to Bob's Seal)**
-------------------------------------------
On-chain Structure:
[ UTXO_A: Alice's asset anchor ]
|
--> Construct transaction:
txin: spend UTXO_A
txout0: Bob's tweaked pubkey (contains new RGB commitment)
txout1: Alice change
|
--> New commitment_hash describes:
"Alice: -N units, Bob: +N units"
Bitlight CLI:
# Alice generates transfer consignment using Bob's invoice
rgb -d .alice -n regtest transfer "$RGB20_CONTRACT" <invoice string>
# Get PSBT, Alice signs in host environment using bdk-cli
# Convert signed PSBT to final transaction hex
# Broadcast transaction (docker internal core-cli or REST API)
**STEP 4: Bob Validate (Reveal & Accept)**
-------------------------------------
On-chain Structure:
Bob checks txout0:
pubkey' ?= Bob_internal_pubkey + H( Bob_internal_pubkey || commitment_hash )*G
If matches:
- Validate consignment proof
- Update local RGB state database:
UTXO_B now anchors Bob's +N units of Asset_X
Bitlight CLI:
# Bob syncs state
rgb -d .bob -n regtest state "$RGB20_CONTRACT" RGB20Fixed --sync --esplora=http://localhost:3002
# Bob checks history
rgb -d .bob -n regtest history "$RGB20_CONTRACT"