← Back to Docs

Swap Tutorial: Alice & Bob

A complete regtest walkthrough demonstrating an L2 → L1 swap from start to finish.

End Goal: Alice ends up with parentchain coins (received from Bob). Bob ends up with sidechain coins (claimed from Alice's swap offer).

What You're Setting Up

0. Environment Variables

Paste these once per terminal session:

export BITCOIN_DIR="/path/to/bitcoin-patched/build/bin"
export BITCOIND="$BITCOIN_DIR/bitcoind"
export BITCOIN_CLI="$BITCOIN_DIR/bitcoin-cli"
export ENFORCER="/path/to/bip300301_enforcer/target/debug/bip300301_enforcer"
export RPC_USER="user"
export RPC_PASSWORD="passwordDC"

# Mainchain regtest (sidechain activation)
export MAINCHAIN_RPC_PORT="18443"
export MAINCHAIN_P2P_PORT="38333"
export MAINCHAIN_DATADIR="$HOME/coinshift-mainchain-data"
export MAINCHAIN_WALLET="mainchainwallet"

# Parentchain regtest (swap transactions)
export PARENTCHAIN_RPC_PORT="18444"
export PARENTCHAIN_P2P_PORT="38334"
export PARENTCHAIN_DATADIR="$HOME/coinshift-parentchain-data"
export PARENTCHAIN_WALLET="parentchainwallet"

# Enforcer
export ENFORCER_GRPC_PORT="50051"
export ENFORCER_GRPC_ADDR="127.0.0.1:$ENFORCER_GRPC_PORT"

# ZMQ
export ZMQ_SEQUENCE="tcp://127.0.0.1:29000"
export ZMQ_HASHBLOCK="tcp://127.0.0.1:29001"
export ZMQ_HASHTX="tcp://127.0.0.1:29002"
export ZMQ_RAWBLOCK="tcp://127.0.0.1:29003"
export ZMQ_RAWTX="tcp://127.0.0.1:29004"

# Coinshift RPC
export COINSHIFT_RPC_URL="http://127.0.0.1:6255"

mkdir -p "$MAINCHAIN_DATADIR" "$PARENTCHAIN_DATADIR"

1. Start Mainchain

"$BITCOIND" -regtest \
  -fallbackfee=0.0002 \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" \
  -server -txindex -rest \
  -zmqpubsequence="$ZMQ_SEQUENCE" \
  -zmqpubhashblock="$ZMQ_HASHBLOCK" \
  -zmqpubhashtx="$ZMQ_HASHTX" \
  -zmqpubrawblock="$ZMQ_RAWBLOCK" \
  -zmqpubrawtx="$ZMQ_RAWTX" \
  -listen -port="$MAINCHAIN_P2P_PORT" \
  -datadir="$MAINCHAIN_DATADIR" -daemon

Wait for RPC, then create wallet and mine 101 blocks:

"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" \
  createwallet "$MAINCHAIN_WALLET" || true

ADDR=$("$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" \
  -rpcwallet="$MAINCHAIN_WALLET" getnewaddress)

"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" \
  generatetoaddress 101 "$ADDR"

2. Start Parentchain

"$BITCOIND" -regtest \
  -fallbackfee=0.0002 \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$PARENTCHAIN_RPC_PORT" \
  -server -txindex -rest \
  -listen -port="$PARENTCHAIN_P2P_PORT" \
  -datadir="$PARENTCHAIN_DATADIR" -daemon

Create wallet and mine 101 blocks on the parentchain too.

3. Start the Enforcer

"$ENFORCER" \
  --node-rpc-addr=127.0.0.1:"$MAINCHAIN_RPC_PORT" \
  --node-rpc-user="$RPC_USER" \
  --node-rpc-pass="$RPC_PASSWORD" \
  --node-zmq-addr-sequence="$ZMQ_SEQUENCE" \
  --serve-grpc-addr "$ENFORCER_GRPC_ADDR" \
  --enable-wallet \
  --wallet-sync-source=disabled

4. Create Sidechain Proposal

grpcurl -plaintext -d @ "$ENFORCER_GRPC_ADDR" \
  cusf.mainchain.v1.WalletService/CreateSidechainProposal \
  < docs/create_sidechain_proposal.json

Mine additional blocks to activate the sidechain.

5. Configure Parentchain RPC in Coinshift

The coinshift app needs to connect to the parentchain to monitor swap transactions. Configure via the GUI (L1 Config) or config file:

Then start the coinshift sidechain node:

./target/release/coinshift --network regtest

6. Alice Deposits (Gets L2 Coins)

Alice needs L2 coins before she can create a swap. Get her deposit address and fund it:

# Get deposit address from coinshift
DEPOSIT_ADDRESS=$(curl -s -X POST "$COINSHIFT_RPC_URL" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":10,"method":"format_deposit_address","params":[]}' \
  | jq -r '.result')

# Send coins to deposit address on mainchain
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" \
  -rpcwallet="$MAINCHAIN_WALLET" \
  sendtoaddress "$DEPOSIT_ADDRESS" 1.0

# Mine blocks to process the deposit (BIP-300)
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" \
  generatetoaddress 6 "$DEPOSIT_ADDRESS"

Check Alice's L2 balance:

curl -s -X POST "$COINSHIFT_RPC_URL" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":12,"method":"get_balance","params":[]}'

7. Alice Creates a Swap Offer

Alice offers her L2 coins in exchange for parentchain coins:

# Get Alice's parentchain address
ALICE_PARENTCHAIN_ADDR=$("$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$PARENTCHAIN_RPC_PORT" -datadir="$PARENTCHAIN_DATADIR" \
  -rpcwallet="$PARENTCHAIN_WALLET" getnewaddress)

# Create the swap
curl -s -X POST "$COINSHIFT_RPC_URL" \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\":\"2.0\",\"id\":1,
    \"method\":\"create_swap\",
    \"params\":{
      \"parent_chain\":\"Regtest\",
      \"l1_recipient_address\":\"$ALICE_PARENTCHAIN_ADDR\",
      \"l1_amount_sats\":5000000,
      \"l2_recipient\":null,
      \"l2_amount_sats\":10000000,
      \"required_confirmations\":1,
      \"fee_sats\":1000
    }
  }"

Save the swap_id from the response. Mine a mainchain block to include it:

SWAP_ID="swap_id_from_response"  # Replace with actual value

8. Bob Sends L1 Payment

Bob sends the exact l1_amount to Alice's parentchain address:

"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$PARENTCHAIN_RPC_PORT" -datadir="$PARENTCHAIN_DATADIR" \
  -rpcwallet="$PARENTCHAIN_WALLET" \
  sendtoaddress "$ALICE_PARENTCHAIN_ADDR" 0.05

# Mine a block to confirm Bob's payment
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$PARENTCHAIN_RPC_PORT" -datadir="$PARENTCHAIN_DATADIR" \
  generatetoaddress 1 "$BOB_PARENTCHAIN_ADDR"

9. Trigger Detection & Check Status

Mine a mainchain block to trigger 2WPD processing, which detects Bob's parentchain transaction:

# Trigger 2WPD
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" \
  generatetoaddress 1 "$ADDR"

sleep 2

# Check swap status (should be ReadyToClaim)
curl -s -X POST "$COINSHIFT_RPC_URL" \
  -H "Content-Type: application/json" \
  -d "{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"get_swap_status\",\"params\":{\"swap_id\":\"$SWAP_ID\"}}"

10. Bob Claims L2 Coins

curl -s -X POST "$COINSHIFT_RPC_URL" \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\":\"2.0\",\"id\":5,
    \"method\":\"claim_swap\",
    \"params\":{
      \"swap_id\":\"$SWAP_ID\",
      \"l2_claimer_address\":\"$BOB_L2_ADDRESS\"
    }
  }"

Mine another mainchain block and verify the swap is Completed.

11. Final Validation

# Alice's parentchain balance (should have received Bob's payment)
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$PARENTCHAIN_RPC_PORT" -datadir="$PARENTCHAIN_DATADIR" \
  -rpcwallet="$PARENTCHAIN_WALLET" \
  getreceivedbyaddress "$ALICE_PARENTCHAIN_ADDR"

# List all swaps
curl -s -X POST "$COINSHIFT_RPC_URL" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":9,"method":"list_swaps","params":[]}'

Expected final state: Alice has parentchain coins (from Bob). Bob has sidechain coins (claimed from swap). Swap is marked Completed. Locked L2 outputs are released.

Cleanup

# Stop mainchain
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$MAINCHAIN_RPC_PORT" -datadir="$MAINCHAIN_DATADIR" stop || true

# Stop parentchain
"$BITCOIN_CLI" -regtest -rpcwait \
  -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASSWORD" \
  -rpcport="$PARENTCHAIN_RPC_PORT" -datadir="$PARENTCHAIN_DATADIR" stop || true

# Stop enforcer
pkill -f "bip300301_enforcer" || true