Hash Time-Locked Contracts (HTLCs) are the foundation of ChainBridge's atomic swap mechanism. This document details the protocol for cross-chain atomic swaps, including the cryptographic primitives, verification mechanisms, and sequence flows.
An HTLC is a smart contract that:
- Locks funds with a cryptographic hash condition
- Enforces a timelock after which funds can be refunded
- Enables atomic execution through secret revelation
| Property | Description |
|---|---|
| Atomicity | Either both sides complete or both refund |
| Trustless | No trusted third party required |
| Timelocked | Funds recoverable after timeout |
| Hash-locked | Secret required to claim |
ChainBridge uses SHA-256 for hash locking:
H = SHA256(S)
Where:
S= Secret preimage (32 random bytes)H= Hash lock (32 bytes)
- Battle-tested cryptographic hash function
- 256-bit security level
- Native support in all supported blockchains
- Efficient computation on embedded devices
import os
import hashlib
def generate_secret() -> bytes:
"""Generate cryptographically secure 32-byte secret"""
secret = os.urandom(32)
return secret
def compute_hash(secret: bytes) -> bytes:
"""Compute SHA256 hash of secret"""
return hashlib.sha256(secret).digest()| Role | Description |
|---|---|
| Initiator | Creates the swap, locks funds on source chain |
| Responder | Locks funds on destination chain |
| Relayer (optional) | Monitors chains, submits proofs, earns fees |
┌─────────────────────────────────────────────────────────────────────────────┐
│ CROSS-CHAIN ATOMIC SWAP PROTOCOL │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Alice (Initiator) Bob (Responder) │
│ ───────────────── ───────────────── │
│ │ │ │
│ │ 1. Generate Secret S │ │
│ │ Compute H = SHA256(S) │ │
│ │ │ │
│ ├─────────────────────────────>│ │
│ │ 2. Propose Swap │ │
│ │ (H, amount, timeout) │ │
│ │ │ │
│ │ 3. Verify Proposal │ │
│ │ Check conditions │ │
│ │ │ │
│ │ 4. Create HTLC on │ │
│ │ Destination Chain │ │
│ │ (H, timeout_dest) │ │
│ │<─────────────────────────────┤ │
│ │ │ │
│ │ 5. Verify Destination │ │
│ │ HTLC Locked │ │
│ │ │ │
│ │ 6. Create HTLC on │ │
│ │ Source Chain │ │
│ │ (H, timeout_src) │ │
│ ├─────────────────────────────>│ │
│ │ │ │
│ │ 7. Verify Source │ │
│ │ HTLC Locked │ │
│ │ │ │
│ │ 8. Claim Destination │ │
│ │ HTLC with Secret S │ │
│ │<─────────────────────────────┤ │
│ │ │ │
│ │ 9. Secret Revealed! │ │
│ │ (via blockchain event) │ │
│ │ │ │
│ │ 10. Claim Source │ │
│ │ HTLC with Secret S │ │
│ ├─────────────────────────────>│ │
│ │ │ │
│ │ 11. Swap Complete! │ │
│ │ Both parties have │ │
│ │ received their funds │ │
│ │ │ │
└─────────────────────────────────────────────────────────────────────────────┘
- Initiator generates random secret
S(32 bytes) - Initiator computes hash
H = SHA256(S) - Initiator sends proposal to Responder containing:
- Hash
H - Amount and asset details
- Source and destination chains
- Proposed timeouts
- Hash
-
Responder creates HTLC on destination chain with:
- Hash lock:
H - Timelock:
T_dest(e.g., 12 hours) - Recipient: Initiator's address on destination chain
- Hash lock:
-
Initiator verifies HTLC was created on destination chain
-
Initiator creates HTLC on source chain with:
- Hash lock:
H - Timelock:
T_src(e.g., 24 hours, must be > T_dest) - Recipient: Responder's address on source chain
- Hash lock:
-
Responder verifies HTLC was created on source chain
-
Initiator (or helper) claims destination HTLC by revealing
S -
Responder detects secret revelation via blockchain event
-
Responder claims source HTLC using revealed secret
S
- Both HTLCs are now claimed - swap complete!
If swap is not completed before timeout:
- After
T_dest: Responder can refund destination HTLC - After
T_src: Initiator can refund source HTLC
Source Chain Timeout (T_src) > Destination Chain Timeout (T_dest)
This ensures:
- Initiator has time to claim after Responder claims
- No race condition where one party waits until last moment
- Fair gameplay for both participants
| Scenario | Source Chain | Destination Chain |
|---|---|---|
| Stellar → Bitcoin | 24 hours | 12 hours |
| Stellar → Ethereum | 24 hours | 12 hours |
| Stellar → Solana | 24 hours | 12 hours |
- Minimum recommended: 6 hours
- Allows time for:
- Block confirmations
- Relayer processing
- Manual intervention if needed
Prove that an HTLC was created on another chain with specific parameters.
Proof Components:
- Transaction hash (TxID)
- Block height
- Merkle proof of transaction inclusion
- Decoded HTLC parameters
Prove that an HTLC was claimed with a specific secret.
Proof Components:
- Transaction hash of claim
- Block height
- Merkle proof
- Revealed secret (optional, can be derived)
Different chains have different finality guarantees:
| Chain | Finality | Confirmations |
|---|---|---|
| Bitcoin | ~60 minutes | 6 blocks |
| Ethereum | ~12 minutes | 12-15 blocks |
| Solana | ~0.4 seconds | 1 block (local) |
| Stellar | ~5 seconds | 1 ledger |
struct BitcoinSPVProof {
tx_hash: [u8; 32],
block_header: BlockHeader,
merkle_proof: Vec<MerkleNode>,
tx_index: u32,
}
struct EthereumProof {
tx_receipt: Receipt,
block_header: Header,
merkle_proof: Vec<bytes>,
log_index: u32,
}
struct SolanaProof {
tx_signature: [u8; 64],
block_hash: Hash,
merkle_proof: Vec<bytes>,
confirmations: u64,
}
fn verify_chain_proof(env: &Env, proof: &ChainProof) -> Result<bool, Error> {
match proof.chain {
Chain::Bitcoin => verify_bitcoin_proof(proof),
Chain::Ethereum => verify_ethereum_proof(proof),
Chain::Solana => verify_solana_proof(proof),
_ => Err(Error::InvalidChain),
}
}┌─────────┐ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐
│ Alice │ │ Relayer│ │ Stellar │ │ Relayer │ │ Bitcoin │
│(Init) │ │ │ │ Contract │ │ (BTC) │ │ Network │
└────┬────┘ └────┬────┘ └────┬─────┘ └────┬────┘ └────┬─────┘
│ │ │ │ │
│ Generate S │ │ │ │
│ H = SHA256(S) │ │ │ │
│ │ │ │ │
│───────────────>│ │ │ │
│ Propose Swap │ │ │ │
│ │ │ │ │
│ │───────────────>│ │ │
│ │ Create Order │ │ │
│ │ │ │ │
│<───────────────│ │ │ │
│ Order ID │ │ │ │
│ │ │ │ │
│ │ │<──────┐ │ │
│ │ │ │ │ │
│ │ │ Create HTLC │ │
│ │ │ (Alice→Bob) │ │
│ │ │ │ │ │
│ │ │───────┘ │ │
│ │ │ HTLC Created │ │
│ │ │ │ │
│ │ │ Event: HTLC │ │
│ │ │───────>─────────>│ │
│ │ │ │ Monitor BTC │
│ │ │ │ │
│ │ │ │<───────────────┤
│ │ │ │ Create BTC │
│ │ │ │ HTLC │
│ │ │ │───────────────>│
│ │ │ │ │
│ │ │ │<───────────────┤
│ │ │ │ BTC HTLC Lock │
│ │ │ │ │
│ │<────────────────│<─────── Event ──│ │
│ Verify BTC HTLC│ │ │ │
│ Locked │ │ │ │
│ │ │ │ │
│ │ │<────── Invoke ──│ │
│ │ │ create_htlc() │ │
│ │ │───────>─────────>│ │
│ │ │ │ │
│ │ │ │───────────────>│
│ │ │ │ Claim BTC HTLC│
│ │ │ │ with Secret S │
│ │ │ │<───────────────│
│ │ │ │ │
│ │<────────────────│<─────── Event ──│ │
│ │ HTLC Claimed, │ │ │
│ │ Secret Revealed │ │ │
│ │ │ │ │
│<───────────────│<─────── Event ──│ │ │
│ Secret S │ │ │ │
│ Revealed! │ │ │ │
│ │ │ │ │
│ │ │<────── Invoke ──│ │
│ │ │ claim_htlc() │ │
│ │ │ with Secret S │ │
│ │ │ │ │
│ │ │───────>─────────>│ │
│ │ │ Success! │ │
│ │ │ │ │
│ Swap Complete! │ │ │ │
└────────────────┴─────────────────┴─────────────────┴────────────────┘
┌─────────┐ ┌──────────┐ ┌─────────┐
│ Alice │ │ Stellar │ │ Bitcoin │
│ │ │ Contract │ │ Network │
└────┬────┘ └────┬─────┘ └───┬─────┘
│ │ │
│ Create HTLC │ │
│ (24h timeout) │ │
│───────────────>│ │
│ │ │
│<───────────────│ │
│ HTLC Created │ │
│ │ │
│ ... (no claim) │ │
│ │ │
│ 24h passes... │ │
│ │ │
│ │<────── Invoke ──│
│ │ refund_htlc() │
│ │ │
│ │───────>─────────│
│ │ Refunded! │
│ │ │
│<───────────────│ │
│ Refund Success │ │
│ │ │
└────────────────┴─────────────────┘
The protocol guarantees atomicity through:
- Cryptographic Binding: Hash lock ties both HTLCs together
- Sequential Reveal: Secret revealed in one direction enables the other
- Timelock Safety: Refund capability prevents permanent lockup
Risk: Attacker sees initiator's claim transaction and front-runs with higher fee.
Mitigation: Initiator reveals secret on destination chain first. Since initiator controls the secret, they have priority on destination chain claim.
Risk: Responder waits until just before timeout to claim, giving initiator no time to claim on source chain.
Mitigation: T_src > T_dest ensures initiator has at least as much time as responder, usually more.
Risk: Relayer refuses to submit proof, blocking swap completion.
Mitigation: Anyone can submit proofs - relayers are optional. Users can run their own relayer.
Risk: Chain reorg invalidates proof submitted to Stellar.
Mitigation: Wait for sufficient confirmations before considering proof final. Recommended: 6 Bitcoin confirmations, 12 Ethereum blocks.
- Secret Visibility: Revealed on destination chain claim, visible to anyone watching
- Address Linkability: Both parties learn each other's addresses on respective chains
- Amount Visibility: All amounts are public on-chain
For enhanced privacy, consider:
- Using different addresses for each swap
- Mixing services (if available on destination chain)
- CoinJoin for Bitcoin
// HTLC creation
pub fn create_htlc(
env: Env,
sender: Address,
receiver: Address,
amount: i128,
hash_lock: Bytes,
time_lock: u64,
) -> Result<u64, Error> {
// Validate inputs
if amount <= 0 {
return Err(Error::InvalidAmount);
}
if hash_lock.len() != 32 {
return Err(Error::InvalidHashLength);
}
if time_lock <= env.ledger().timestamp() {
return Err(Error::InvalidTimelock);
}
// Create HTLC
let htlc_id = storage::increment_htlc_counter(&env);
let htlc = HTLC {
sender,
receiver,
amount,
hash_lock,
time_lock,
status: HTLCStatus::Active,
secret: None,
created_at: env.ledger().timestamp(),
};
storage::write_htlc(&env, htlc_id, &htlc);
Ok(htlc_id)
}
// HTLC claim
pub fn claim_htlc(
env: &Env,
htlc_id: u64,
secret: Bytes,
) -> Result<(), Error> {
let mut htlc = storage::read_htlc(env, htlc_id)?;
// Check status
if htlc.status != HTLCStatus::Active {
return Err(Error::AlreadyClaimed);
}
// Check timelock
if env.ledger().timestamp() >= htlc.time_lock {
return Err(Error::HTLCExpired);
}
// Verify secret
let computed_hash = env.crypto().sha256(&secret);
if computed_hash != htlc.hash_lock {
return Err(Error::InvalidSecret);
}
// Update status
htlc.status = HTLCStatus::Claimed;
htlc.secret = Some(secret);
storage::write_htlc(env, htlc_id, &htlc);
// Transfer funds (implementation depends on asset type)
// ...
Ok(())
}// P2SH HTLC script for Bitcoin
const HTLC_script = `
OP_IF
// Revealed secret case
OP_SHA256 <hash> OP_EQUALVERIFY <receiver_pubkey> OP_CHECKSIG
OP_ELSE
// Refund case
<expiry> OP_CHECKSEQUENCEVERIFY OP_DROP <sender_pubkey> OP_CHECKSIG
OP_ENDIF
`;// Simplified Ethereum HTLC
contract HTLC {
bytes32 public hashLock;
address public sender;
address public receiver;
uint256 public timelock;
bool public claimed;
bool public refunded;
constructor(
bytes32 _hashLock,
address _sender,
address _receiver,
uint256 _timelock
) {
hashLock = _hashLock;
sender = _sender;
receiver = _receiver;
timelock = _timelock;
}
function claim(bytes32 _secret) public {
require(!claimed && !refunded, "Already resolved");
require(msg.sender == receiver, "Not receiver");
require(
sha256(abi.encodePacked(_secret)) == hashLock,
"Invalid secret"
);
require(block.timestamp < timelock, "Timelock expired");
claimed = true;
// Transfer funds
payable(receiver).transfer(address(this).balance);
}
function refund() public {
require(!claimed && !refunded, "Already resolved");
require(msg.sender == sender, "Not sender");
require(block.timestamp >= timelock, "Timelock not expired");
refunded = true;
// Transfer funds back
payable(sender).transfer(address(this).balance);
}
}| Error | Cause | Resolution |
|---|---|---|
| InvalidSecret | Wrong secret provided | Verify correct secret |
| HTLCExpired | Timelock passed | Initiate refund |
| HTLCNotFound | HTLC doesn't exist | Check HTLC ID |
| AlreadyClaimed | HTLC already claimed | Check status |
- Expired Unclaimed HTLC: Call
refund_htlc()after timeout - Failed Claim: Verify secret is correct, retry with correct secret
- Proof Rejected: Regenerate proof with more confirmations
- Verify Timeouts: Ensure sufficient time for completion
- Monitor Progress: Watch both chains for events
- Keep Secrets Safe: Lose secret = lose funds (if unclaimed)
- Test First: Use testnet before mainnet
- Monitor Reliability: Ensure 24/7 monitoring
- Quick Response: Submit proofs promptly
- Fee Management: Set appropriate fees
- Proof Storage: Keep proof data for disputes
- Finality Confirmation: Wait for sufficient confirmations
- Event Monitoring: Watch for all HTLC events
- Error Handling: Implement retry logic
- Testing: Thoroughly test with testnet