Cross-Chain Interoperability and Bridges
Building cross-chain bridges and implementing interoperability protocols between different blockchains.
Cross-Chain Interoperability and Bridges
Cross-chain solutions enable communication and value transfer between different blockchains.
The Interoperability Problem
Blockchains are isolated - they can't natively communicate with each other. This creates:
- Liquidity Fragmentation: Assets locked on different chains
- User Friction: Multiple wallets, different experiences
- Limited Composability: Can't combine features across chains
Bridge Types
1. Lock and Mint
- Lock assets on source chain
- Mint wrapped tokens on destination chain
- Burn wrapped tokens to unlock originals
2. Burn and Mint
- Burn tokens on source chain
- Mint equivalent on destination chain
- Reverse process for return
3. Atomic Swaps
- Direct peer-to-peer swaps
- No intermediary needed
- Uses hash time-locked contracts (HTLC)
4. Relays
- Validators monitor both chains
- Relay state and messages
- More complex but more flexible
Bridge Architecture
RUSTstruct Bridge { source_chain: String, target_chain: String, validators: Vec<String>, deposits: HashMap<String, DepositInfo>, withdrawals: HashMap<String, WithdrawalInfo>, } struct DepositInfo { amount: u64, recipient: String, source_tx_hash: String, status: BridgeStatus, } enum BridgeStatus { Pending, Confirmed, Processed, Failed, } impl Bridge { fn deposit(&mut self, amount: u64, recipient: &str, tx_hash: &str) -> String { let deposit_id = generate_id(); self.deposits.insert(deposit_id.clone(), DepositInfo { amount, recipient: recipient.to_string(), source_tx_hash: tx_hash.to_string(), status: BridgeStatus::Pending, }); deposit_id } fn confirm_deposit(&mut self, deposit_id: &str, validator: &str) -> Result<(), String> { if !self.validators.contains(&validator.to_string()) { return Err(String::from("Invalid validator")); } let deposit = self.deposits.get_mut(deposit_id) .ok_or(String::from("Deposit not found"))?; // In real implementation: verify validator signatures deposit.status = BridgeStatus::Confirmed; Ok(()) } fn process_withdrawal(&mut self, deposit_id: &str) -> Result<WithdrawalInfo, String> { let deposit = self.deposits.get(deposit_id) .ok_or(String::from("Deposit not found"))?; if deposit.status != BridgeStatus::Confirmed { return Err(String::from("Deposit not confirmed")); } // Check validator consensus (simplified) // In real implementation: require 2/3+ validator signatures let withdrawal = WithdrawalInfo { amount: deposit.amount, recipient: deposit.recipient.clone(), target_tx_hash: String::new(), // Will be set when processed }; self.withdrawals.insert(deposit_id.to_string(), withdrawal.clone()); Ok(withdrawal) } }
Hash Time-Locked Contracts (HTLC)
HTLCs enable atomic swaps without trusted intermediaries.
RUSTstruct HTLC { hash_lock: Vec<u8>, // Hash of secret time_lock: u64, // Expiration time sender: String, receiver: String, amount: u64, secret: Option<Vec<u8>>, // Revealed when claimed } impl HTLC { fn new(hash_lock: Vec<u8>, time_lock: u64, sender: &str, receiver: &str, amount: u64) -> Self { HTLC { hash_lock, time_lock, sender: sender.to_string(), receiver: receiver.to_string(), amount, secret: None, } } fn claim(&mut self, secret: Vec<u8>) -> Result<(), String> { // Verify secret matches hash if hash(&secret) != self.hash_lock { return Err(String::from("Invalid secret")); } self.secret = Some(secret); Ok(()) } fn refund(&self, current_time: u64) -> Result<(), String> { if current_time < self.time_lock { return Err(String::from("Time lock not expired")); } if self.secret.is_some() { return Err(String::from("Already claimed")); } Ok(()) } }
Security Considerations
Validator Risks
- Collusion: Validators could steal funds
- Single Point of Failure: If validators go offline
- Solution: Use many validators, require high threshold
Replay Attacks
- Problem: Same transaction executed on both chains
- Solution: Nonces, chain-specific identifiers
Double Spending
- Problem: Spending same asset on both chains
- Solution: Lock mechanism, proper state tracking
Real-World Examples
- Polygon Bridge: Lock and mint between Ethereum and Polygon
- Wormhole: Multi-chain bridge with validators
- Chainlink CCIP: Cross-chain messaging protocol
- Cosmos IBC: Inter-Blockchain Communication protocol
Best Practices
- Multi-Signature: Require multiple validator signatures
- Time Locks: Allow users to challenge suspicious transactions
- Rate Limiting: Prevent large-scale attacks
- Monitoring: Alert on unusual activity
- Upgradability: Ability to fix bugs and add features
Code Examples
Cross-Chain Bridge
Basic bridge implementation for asset transfer
use std::collections::HashMap;
enum BridgeStatus {
Pending,
Confirmed,
Processed,
}
struct Deposit {
amount: u64,
recipient: String,
source_tx: String,
status: BridgeStatus,
confirmations: usize,
}
struct Bridge {
deposits: HashMap<String, Deposit>,
validators: Vec<String>,
required_confirmations: usize,
}
impl Bridge {
fn new(validators: Vec<String>) -> Self {
let required = (validators.len() * 2) / 3 + 1; // 2/3+1
Bridge {
deposits: HashMap::new(),
validators,
required_confirmations: required,
}
}
fn deposit(&mut self, deposit_id: String, amount: u64, recipient: String, source_tx: String) {
self.deposits.insert(deposit_id, Deposit {
amount,
recipient,
source_tx,
status: BridgeStatus::Pending,
confirmations: 0,
});
}
fn confirm(&mut self, deposit_id: &str, validator: &str) -> Result<bool, String> {
if !self.validators.contains(&validator.to_string()) {
return Err(String::from("Invalid validator"));
}
let deposit = self.deposits.get_mut(deposit_id)
.ok_or(String::from("Deposit not found"))?;
deposit.confirmations += 1;
if deposit.confirmations >= self.required_confirmations {
deposit.status = BridgeStatus::Confirmed;
Ok(true) // Ready to process
} else {
Ok(false) // Need more confirmations
}
}
fn process_withdrawal(&mut self, deposit_id: &str) -> Result<u64, String> {
let deposit = self.deposits.get_mut(deposit_id)
.ok_or(String::from("Deposit not found"))?;
match deposit.status {
BridgeStatus::Confirmed => {
deposit.status = BridgeStatus::Processed;
Ok(deposit.amount)
}
_ => Err(String::from("Deposit not confirmed")),
}
}
}
fn main() {
let validators = vec![
String::from("validator1"),
String::from("validator2"),
String::from("validator3"),
String::from("validator4"),
];
let mut bridge = Bridge::new(validators);
// User deposits on source chain
bridge.deposit(
String::from("deposit1"),
1000,
String::from("0xAlice"),
String::from("0xtx123"),
);
// Validators confirm
bridge.confirm("deposit1", "validator1").unwrap();
bridge.confirm("deposit1", "validator2").unwrap();
bridge.confirm("deposit1", "validator3").unwrap();
// Process withdrawal on target chain
match bridge.process_withdrawal("deposit1") {
Ok(amount) => {
println!("Withdrawal processed: {} tokens to 0xAlice", amount);
}
Err(e) => println!("Error: {}", e),
}
}Explanation:
Bridges lock assets on the source chain and mint equivalent assets on the target chain. Validators monitor both chains and confirm deposits. Once enough validators confirm (2/3+), the withdrawal can be processed. This enables cross-chain asset transfers.
HTLC Atomic Swap
Hash Time-Locked Contract for atomic swaps
use std::collections::HashMap;
fn hash(data: &[u8]) -> Vec<u8> {
// Simplified hash function
// In real implementation: use SHA256 or similar
data.iter().map(|b| b.wrapping_add(1)).collect()
}
struct HTLC {
hash_lock: Vec<u8>,
time_lock: u64,
sender: String,
receiver: String,
amount: u64,
secret: Option<Vec<u8>>,
claimed: bool,
}
impl HTLC {
fn new(
secret_preimage: Vec<u8>,
time_lock: u64,
sender: &str,
receiver: &str,
amount: u64,
) -> Self {
let hash_lock = hash(&secret_preimage);
HTLC {
hash_lock,
time_lock,
sender: sender.to_string(),
receiver: receiver.to_string(),
amount,
secret: None,
claimed: false,
}
}
fn claim(&mut self, secret: Vec<u8>, current_time: u64) -> Result<(), String> {
if self.claimed {
return Err(String::from("Already claimed"));
}
if current_time >= self.time_lock {
return Err(String::from("Time lock expired"));
}
if hash(&secret) != self.hash_lock {
return Err(String::from("Invalid secret"));
}
self.secret = Some(secret);
self.claimed = true;
Ok(())
}
fn refund(&self, current_time: u64) -> Result<(), String> {
if self.claimed {
return Err(String::from("Already claimed"));
}
if current_time < self.time_lock {
return Err(String::from("Time lock not expired"));
}
Ok(())
}
}
fn main() {
// Alice wants to swap with Bob
let secret = vec![1, 2, 3, 4, 5];
// Alice creates HTLC on chain A
let mut htlc_a = HTLC::new(
secret.clone(),
1000, // Expires at block 1000
"alice",
"bob",
100,
);
println!("HTLC created on chain A");
// Bob sees the hash, creates HTLC on chain B
// Bob reveals secret to claim on chain A
match htlc_a.claim(secret, 500) {
Ok(_) => {
println!("Bob claimed HTLC on chain A");
println!("Secret revealed: {:?}", htlc_a.secret);
println!("Alice can now use secret to claim on chain B");
}
Err(e) => println!("Claim failed: {}", e),
}
}Explanation:
HTLCs enable atomic swaps: Alice locks funds with a hash, Bob locks funds on another chain, Bob claims Alice's funds revealing the secret, then Alice uses the same secret to claim Bob's funds. If either party doesn't cooperate, funds can be refunded after the time lock expires.
Exercises
Simple Bridge
Create a simple bridge deposit and withdrawal system!
Starter Code:
use std::collections::HashMap;
struct Bridge {
deposits: HashMap<String, u64>,
}
fn main() {
// Create bridge
// Deposit assets
// Process withdrawal
}