BTC On-Chain Verification

This document explains how xBTC verifies Bitcoin block hashes on smart contract blockchains (Ethereum, Solana, and other EVM-compatible chains) in a trustless manner, without relying on oracles or centralized validators.

1. Overview

1.1 The Problem

Cross-chain applications need to verify Bitcoin transactions on other blockchains. Traditional approaches rely on:

All these approaches introduce trust assumptions and potential failure points.

1.2 Our Solution: Optimistic Verification with Challenge

We use an optimistic verification model that leverages Bitcoin's own Proof-of-Work security:

1

Submit Block Hash

User submits a Bitcoin block hash with a deposit, claiming it exists on mainnet.

2

Challenge Period

Anyone can challenge by submitting competing block headers with valid Proof-of-Work.

3

Chain Work Comparison

The system tracks cumulative work for each chain branch. Highest work wins.

4

Resolution

After timeout, the winning chain determines validity. Winners get rewards; losers lose deposits.

1.3 Why This Works

The Attacker's Dilemma

To submit a fake block hash successfully, an attacker must produce more Proof-of-Work than the entire Bitcoin network. With Bitcoin's massive global hash rate (hundreds of EH/s and growing), the cost of attack far exceeds any potential gain.

The security comes from a simple insight:

Key Properties

Trustless: No oracles or centralized validators needed.

Permissionless: Anyone can submit blocks or challenge fraudulent claims.

Economically Secure: Attack cost scales with Bitcoin's total hash rate.


2. Implementation Details

Technical Content

This section contains detailed technical implementation information intended for developers and technical users. If you're not interested in the technical details, you can skip this section.

Our implementation is based on the SelfValidator smart contract. This section focuses on how block headers form a tree structure during the challenge period.

2.1 Core Concept: Block Tree

During the challenge period, submitted block headers form a tree structure with two types of branches:

Branch Type Condition Description
Supporting Branch firstBlockHash == validation.blockHash Blocks that support the validity of the submitted block hash. These blocks use the validation target as their root.
Opposing Branch firstBlockHash != validation.blockHash Blocks that contest the validity. These blocks start from a different root, claiming an alternative chain has more work.

2.2 Tree Structure Visualization

// Initial State: User submits blockHash H for validation validation.blockHash = H winnerBlockHash = H // Initially, H is the winner // During Challenge Period, a tree forms: H (validation target) │ ├── [Supporting Branch] firstBlockHash == H │ │ │ └── H (block header of H itself) │ │ chainWork: d₁ │ │ blockAmount: 1 │ │ │ └── H+1 (next block, prevHash = H) │ │ chainWork: d₁ + d₂ │ │ blockAmount: 2 │ │ │ └── H+2 (prevHash = H+1) │ chainWork: d₁ + d₂ + d₃ │ blockAmount: 3 │ ├── [Opposing Branch 1] firstBlockHash == A │ │ │ └── A (alternative root) │ │ chainWork: dₐ │ │ blockAmount: 1 │ │ │ └── A+1 │ chainWork: dₐ + dₐ₊₁ │ blockAmount: 2 │ └── [Opposing Branch 2] firstBlockHash == B │ └── B (another alternative root) chainWork: dᵦ blockAmount: 1

2.3 State Transitions in submitBlockHeader

When a user calls submitBlockHeader(id, blockHeaderBytes), the contract determines which state transition applies based on three key conditions:

State Variables

Variable Meaning
hashCurrentBlock == validation.blockHash Is the submitted block the validation target?
prevBlockInfo.firstBlockHash != bytes32(0) Has the previous block been added?
currentBlockInfo.firstBlockHash != bytes32(0) Has the current block been added?
currentBlockInfo.addTime > firstBlockInfo.addTime Can the block be overwritten? (addTime condition)

All Possible State Combinations

# Current == Validation Prev Exists Current Exists addTime Condition Result
1 Yes - No - Add as Supporting Tree Root
2 Yes - Yes - Reject "dup"
3 No No No - Add as New Tree Root (Opposing)
4 No No Yes - Reject "dup"
5 No Yes No - Normal Add to prev's tree
6 No Yes Yes Met Overwrite (refund original)
7 No Yes Yes Not Met Reject "NO"

Case 1: Supporting Tree Root

// hashCurrentBlock == validation.blockHash && not exists
if (hashCurrentBlock == validation.blockHash) {
    require(currentBlockInfo.firstBlockHash == bytes32(0), "dup");
    addNewBlock(id, hashCurrentBlock, bytes32(0), ...);
}

The validation target becomes the supporting tree root. Its firstBlockHash = self and addTime = 0 (highest priority).

Case 2: Duplicate Validation Target

If the validation target has already been submitted, the transaction reverts with "dup".

Case 3: New Opposing Tree Root

// hashCurrentBlock != validation.blockHash && prevBlock not exists && current not exists
if (prevBlockInfo.firstBlockHash == bytes32(0)) {
    require(currentBlockInfo.firstBlockHash == bytes32(0), "dup");
    addNewBlock(id, hashCurrentBlock, bytes32(0), ...);
}

When the previous block doesn't exist, this block becomes a new opposing tree root. Its firstBlockHash = self and addTime = block.timestamp.

Case 4: Duplicate Without Valid Path

If the previous block doesn't exist but current block already exists, the transaction reverts with "dup". Cannot overwrite without a valid prev path.

Case 5: Normal Extension

// prevBlock exists && current not exists
addNewBlock(id, hashCurrentBlock, hashPrevBlock, ...);

The block is added to the same tree as its previous block. It inherits firstBlockHash and addTime from the tree root.

Case 6: Block Overwrite

// prevBlock exists && current exists && addTime condition met
if (currentBlockInfo.addTime > firstBlockInfo.addTime) {
    player.transfer(refundValue);  // Refund original submitter
    addNewBlock(id, hashCurrentBlock, hashPrevBlock, ..., refundValue, oldFirstBlockHash);
}

Overwrite Mechanism

If a block exists in Tree A but Tree B (created earlier) wants to claim it, and block.addTime > TreeB.root.addTime, the block is reassigned to Tree B.

The original submitter is refunded, and valueYes/valueNo is correctly adjusted between the two camps.

Case 7: Overwrite Rejected

If the addTime condition is not met (currentBlockInfo.addTime ≤ firstBlockInfo.addTime), the original tree has higher priority. Transaction reverts with "NO".

2.4 Chain Work and Winner Selection

After each block submission, the contract compares chain work:

// Update winner if this chain has more cumulative work
if (currentBlockInfo.chainWork > winnerBlockInfo.chainWork) {
    validation.winnerBlockHash = hashCurrentBlock;
}

The winnerBlockHash always points to the block with the highest chainWork.

Chain Work Calculation

if (prevBlockHash == bytes32(0)) {
    // First block in branch
    currentBlock.chainWork = difficulty;
    currentBlock.blockAmount = 1;
} else {
    // Extending existing branch
    currentBlock.chainWork = prevBlock.chainWork + difficulty;
    currentBlock.blockAmount = prevBlock.blockAmount + 1;
}

2.5 Stake Tracking

The contract tracks total stakes on each side:

Variable Condition Meaning
valueYes firstBlockHash == validation.blockHash Total deposits supporting validity
valueNo firstBlockHash != validation.blockHash Total deposits opposing validity
// Step 1: Set addTime based on tree position
if (prevBlockHash == bytes32(0)) {
    currentBlock.addTime = block.timestamp;  // Tree root
} else {
    currentBlock.addTime = prevBlock.addTime;  // Inherit from tree root
}

// Step 2: Track stakes and override addTime for supporting branch
if (currentBlock.firstBlockHash == validation.blockHash) {
    validation.valueYes += value;
    currentBlock.addTime = 0;  // Supporting branch has highest priority
} else {
    validation.valueNo += value;
}

2.6 Final Resolution

After the deadline passes, setBlockHashStatus determines the outcome:

function setBlockHashStatus(uint256 id) public nonReentrant {
    require(block.timestamp > validation.deadline);

    bytes32 winnerBlockHash = validation.winnerBlockHash;
    BlockHeaderInfo storage winnerBlockInfo = blockHeaderInfo[id][winnerBlockHash];

    // Check if winner belongs to supporting branch (or no challenge occurred)
    if (winnerBlockInfo.firstBlockHash == validation.blockHash ||
        winnerBlockInfo.firstBlockHash == bytes32(0)) {
        // Supporting branch wins → blockHash is VALID
        blockMapping[validation.blockHash] = true;
        payable(validation.player).transfer(validation.value);  // Refund only
    } else {
        // Opposing branch wins → blockHash is INVALID
        blockMapping[validation.blockHash] = false;
    }
}

Outcome Summary

Scenario Winner Result Reward Pool
Supporting branch has more work Supporters Block hash marked valid valueNo distributed to winners
Opposing branch has more work Challengers Block hash marked invalid valueYes distributed to winners

2.7 Deadline Extension Strategy

Each block submission resets the challenge deadline:

validation.deadline = block.timestamp + TIMEOUT;

This creates an important defense mechanism for honest participants:

Delay Strategy

When facing a malicious challenge but the next real Bitcoin block hasn't been mined yet, an honest submitter can submit any valid Bitcoin block header (even older blocks with lower difficulty) to extend the deadline.

The block only needs to satisfy hash <= target. The submitter's deposit will be refunded after winning, so the maximum cost is just gas fees.

How It Works

// Scenario: Alice submitted valid block H, Bob maliciously challenges // TIMEOUT = 15 minutes Time 00:00 Alice submits H (valid Bitcoin block) deadline = 00:00 + 15min = 00:15 Time 00:05 Bob submits fake_block as opposing branch deadline = 00:05 + 15min = 00:20 Time 00:18 // Deadline approaching! Next real Bitcoin block not yet mined! // Alice submits any valid block header to buy time Alice submits H_delay (any valid block, satisfies hash <= target) deadline = 00:18 + 15min = 00:33 Time 00:25 Real Bitcoin block H+1 is mined on mainnet Alice submits H+1 with full difficulty deadline = 00:25 + 15min = 00:40 Time 00:41 Deadline passes, no more submissions Alice's branch wins (real blocks have overwhelming chainWork) // Result: Alice receives: original deposit (refund) + H_delay deposit (refund) + Bob's stake (reward) Net cost to Alice: only gas fees for H_delay submission

Key Points

Note

This strategy ensures honest participants are never at a disadvantage due to Bitcoin's ~10 minute block time. They can always extend the deadline until real blocks with overwhelming chainWork arrive.

2.8 Configuration Parameters

Parameter Value Description
TIMEOUT 15 minutes Challenge period duration (extended with each submission)
PAY_MIN_VALUE 0.1 ETH Minimum deposit required
PAY_VALUE Configurable Current deposit requirement (>= PAY_MIN_VALUE)

Deadline Extension

Each block submission resets the deadline: deadline = block.timestamp + TIMEOUT

This prevents premature finalization and ensures all parties have time to respond.