Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 71 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 🛡️ SPV Contract: Bitcoin Light Client on EVM
Welcome to the **SPV Contract**, a robust and efficient Solidity implementation for verifying Bitcoin block headers directly on an EVM-compatible blockchain. This contract empowers dApps to act as a **Simplified Payment Verification (SPV)** client, allowing them to validate the existence and inclusion of Bitcoin transactions without needing to run a full Bitcoin node.
# 🛡️ SPV Gateway: Bitcoin Light Client on EVM
Welcome to the **SPV Gateway**, a robust and efficient Solidity implementation for verifying Bitcoin block headers directly on an EVM-compatible blockchain. This contract empowers dApps to act as a **Simplified Payment Verification (SPV)** client, allowing them to validate the existence and inclusion of Bitcoin transactions without needing to run a full Bitcoin node.

# ✨ Why this SPV Contract?
In the decentralized world, connecting different blockchain ecosystems securely is paramount. This SPV contract provides a trust-minimized bridge, enabling smart contracts on EVM chains to cryptographically verify the state of the Bitcoin blockchain. This opens doors for exciting use cases like:
# ✨ Why this SPV Gateway?
In the decentralized world, connecting different blockchain ecosystems securely is paramount. This SPV Gateway provides a trust-minimized bridge, enabling smart contracts on EVM chains to cryptographically verify the state of the Bitcoin blockchain. This opens doors for exciting use cases like:
- **Cross-chain bridges** for Bitcoin-backed assets
- **Light clients** for dApps that need to confirm Bitcoin transaction finality
- **Decentralized custodianship** solutions
Expand All @@ -26,98 +26,98 @@ In the decentralized world, connecting different blockchain ecosystems securely
# ⚙️ How it Works (Under the Hood)
The contract operates by receiving raw Bitcoin block headers, which are then parsed and validated against Bitcoin's strict consensus rules.

1. **Header Parsing:** Raw 80-byte Bitcoin block headers are parsed into a structured *BlockHeaderData* format. This involves handling Bitcoin's unique little-endian byte ordering.
1. **Header Parsing:** Raw 80-byte Bitcoin block headers are parsed into a structured *BlockHeader.HeaderData* format. This involves handling Bitcoin's unique little-endian byte ordering.
2. **Double SHA256 Hashing:** Each block header is double SHA256 hashed to derive its unique block hash, which is then byte-reversed for standard representation.
3. **Proof-of-Work Verification:** The calculated block hash is checked against the current network difficulty target (derived from the *bits* field in the header).
4. **Chain Extension & Reorganization:** New blocks are added to a data structure that allows for tracking multiple chains. When a new block extends a chain with higher cumulative work, the *mainchainHead* is updated, reflecting potential chain reorganizations.
5. **Difficulty Adjustment:** Every 2016 blocks, the contract calculates a new difficulty target based on the time taken to mine the preceding epoch. This ensures the 10-minute average block time is maintained.

# 📊 Flow Diagrams
These diagrams outline the step-by-step process for adding block headers to the SPV Contract.
These diagrams outline the step-by-step process for adding block headers to the SPV Gateway.

### `addBlockHeader(bytes calldata blockHeaderRaw_)` Sequence Diagram

```mermaid
sequenceDiagram
participant Caller
participant SPVContract
participant SPVGateway
participant BlockHeaderLib
participant TargetsHelperLib

Caller->>SPVContract: addBlockHeader(blockHeaderRaw)
activate SPVContract
Caller->>SPVGateway: addBlockHeader(blockHeaderRaw)
activate SPVGateway

SPVContract->>BlockHeaderLib: 1. Parse blockHeaderRaw_ (parseBlockHeaderData)
SPVGateway->>BlockHeaderLib: 1. Parse blockHeaderRaw_ (parseBlockHeaderData)
activate BlockHeaderLib
BlockHeaderLib-->>SPVContract: 1.1. Check length (80 bytes) & LE to BE
BlockHeaderLib-->>SPVGateway: 1.1. Check length (80 bytes) & LE to BE
alt Length Invalid
BlockHeaderLib--xSPVContract: Error: InvalidBlockHeaderDataLength
SPVContract--xCaller: Revert
BlockHeaderLib--xSPVGateway: Error: InvalidBlockHeaderDataLength
SPVGateway--xCaller: Revert
end
BlockHeaderLib-->>SPVContract: 1.2. Return BlockHeaderData & blockHash
BlockHeaderLib-->>SPVGateway: 1.2. Return BlockHeaderData & blockHash
deactivate BlockHeaderLib

SPVContract->>SPVContract: 1.3. Check blockHash existence
SPVGateway->>SPVGateway: 1.3. Check blockHash existence
alt BlockHash Exists
SPVContract--xCaller: Error: BlockAlreadyExists
SPVGateway--xCaller: Error: BlockAlreadyExists
end

SPVContract->>SPVContract: 2. Check prevBlockHash existence
SPVGateway->>SPVGateway: 2. Check prevBlockHash existence
alt Prev Block Missing
SPVContract--xCaller: Error: PrevBlockDoesNotExist
SPVGateway--xCaller: Error: PrevBlockDoesNotExist
end

SPVContract->>SPVContract: 3. Calculate newBlockHeight = prevBlockHeight + 1
SPVGateway->>SPVGateway: 3. Calculate newBlockHeight = prevBlockHeight + 1

SPVContract->>SPVContract: 4. Get Current Target
SPVContract->>SPVContract: 4.1. Get target from prevBlockBits
SPVContract->>TargetsHelperLib: Check if newBlockHeight is Recalculation Block (isTargetAdjustmentBlock)
SPVGateway->>SPVGateway: 4. Get Current Target
SPVGateway->>SPVGateway: 4.1. Get target from prevBlockBits
SPVGateway->>TargetsHelperLib: Check if newBlockHeight is Recalculation Block (isTargetAdjustmentBlock)
activate TargetsHelperLib
alt Recalculation Block
SPVContract->>SPVContract: Recalculate target & Save lastEpochCumulativeWork
TargetsHelperLib-->>SPVContract: Return newNetworkTarget
SPVGateway->>SPVGateway: Recalculate target & Save lastEpochCumulativeWork
TargetsHelperLib-->>SPVGateway: Return newNetworkTarget
else Not Recalculation Block
TargetsHelperLib-->>SPVContract: Use prevBlockTarget as networkTarget
TargetsHelperLib-->>SPVGateway: Use prevBlockTarget as networkTarget
end
deactivate TargetsHelperLib

SPVContract->>SPVContract: 5. Check Block Rules
SPVContract->>TargetsHelperLib: 5.1. Check Header Target == Contract Target
SPVGateway->>SPVGateway: 5. Check Block Rules
SPVGateway->>TargetsHelperLib: 5.1. Check Header Target == Contract Target
activate TargetsHelperLib
TargetsHelperLib-->>SPVContract: Result
TargetsHelperLib-->>SPVGateway: Result
deactivate TargetsHelperLib
alt Invalid Target
SPVContract--xCaller: Error: InvalidTarget
SPVGateway--xCaller: Error: InvalidTarget
end

SPVContract->>SPVContract: 5.2. Check newBlockHash <= networkTarget (PoW)
SPVGateway->>SPVGateway: 5.2. Check newBlockHash <= networkTarget (PoW)
alt Invalid Block Hash
SPVContract--xCaller: Error: InvalidBlockHash
SPVGateway--xCaller: Error: InvalidBlockHash
end

SPVContract->>SPVContract: 5.3. Check newBlockTime >= medianTime
SPVGateway->>SPVGateway: 5.3. Check newBlockTime >= medianTime
alt Invalid Block Time
SPVContract--xCaller: Error: InvalidBlockTime
SPVGateway--xCaller: Error: InvalidBlockTime
end

SPVContract->>SPVContract: 6. Add Block To Chain
SPVContract->>SPVContract: 6.1. Save newBlockHeader & newBlockHash to Storage
SPVGateway->>SPVGateway: 6. Add Block To Chain
SPVGateway->>SPVGateway: 6.1. Save newBlockHeader & newBlockHash to Storage

SPVContract->>SPVContract: 6.2. Update Mainchain
SPVGateway->>SPVGateway: 6.2. Update Mainchain
alt 6.2.1. prevBlockHash == mainchainHead?
SPVContract->>SPVContract: Move mainchainHead to newBlockHash
SPVGateway->>SPVGateway: Move mainchainHead to newBlockHash
else
SPVContract->>SPVContract: 6.2.2. Calculate New Block & Current Head Cumulative Work
SPVContract->>SPVContract: 6.2.3. newBlock Cumulative Work > Current Head?
SPVGateway->>SPVGateway: 6.2.2. Calculate New Block & Current Head Cumulative Work
SPVGateway->>SPVGateway: 6.2.3. newBlock Cumulative Work > Current Head?
alt New Block Has Higher Work
SPVContract->>SPVContract: Set New Block as mainchainHead
SPVContract->>SPVContract: Recursively update mainchain path backwards (do-while loop)
SPVGateway->>SPVGateway: Set New Block as mainchainHead
SPVGateway->>SPVGateway: Recursively update mainchain path backwards (do-while loop)
end
end

SPVContract->>SPVContract: Emit BlockHeaderAdded
SPVContract-->>Caller: Transaction Complete
deactivate SPVContract
SPVGateway->>SPVGateway: Emit BlockHeaderAdded
SPVGateway-->>Caller: Transaction Complete
deactivate SPVGateway
```

### `addBlockHeaderBatch(bytes[] calldata blockHeaderRawArr_)` Sequence Diagram
Expand All @@ -127,75 +127,72 @@ This function processes multiple block headers in a single transaction, iteratin
```mermaid
sequenceDiagram
participant Caller
participant SPVContract
participant SPVGateway
participant BlockHeaderLib
participant TargetsHelperLib

Caller->>SPVContract: addBlockHeaderBatch(blockHeaderRawArray_)
activate SPVContract
Caller->>SPVGateway: addBlockHeaderBatch(blockHeaderRawArray_)
activate SPVGateway

SPVContract->>SPVContract: Check if Header Array is Empty
SPVGateway->>SPVGateway: Check if Header Array is Empty
alt Array Empty
SPVContract--xCaller: Error: EmptyBlockHeaderArray
SPVGateway--xCaller: Error: EmptyBlockHeaderArray
end

SPVContract->>BlockHeaderLib: 1. Parse Block Headers Array (_parseBlockHeadersRaw)
SPVGateway->>BlockHeaderLib: 1. Parse Block Headers Array (_parseBlockHeadersRaw)
activate BlockHeaderLib
BlockHeaderLib-->>SPVContract: Returns BlockHeaderData[] & bytes32[]
BlockHeaderLib-->>SPVGateway: Returns BlockHeaderData[] & bytes32[]
deactivate BlockHeaderLib

loop For each blockHeader in parsed array (from i=0 to length-1)
SPVContract->>SPVContract: 2. Check prevBlockHash for current block
SPVGateway->>SPVGateway: 2. Check prevBlockHash for current block
alt First block in batch
SPVContract->>SPVContract: Check prevBlockHash existence (like addBlockHeader)
SPVGateway->>SPVGateway: Check prevBlockHash existence (like addBlockHeader)
alt Prev Block Missing
SPVContract--xCaller: Error: PrevBlockDoesNotExist
SPVGateway--xCaller: Error: PrevBlockDoesNotExist
end
else Subsequent blocks
SPVContract->>SPVContract: Check prevBlockHash == blockHash of (i-1)th block
SPVGateway->>SPVGateway: Check prevBlockHash == blockHash of (i-1)th block
alt Order Invalid
SPVContract--xCaller: Error: InvalidBlockHeadersOrder
SPVGateway--xCaller: Error: InvalidBlockHeadersOrder
end
end

SPVContract->>SPVContract: 3. Calculate currentBlockHeight = prevBlockHeight + 1
SPVGateway->>SPVGateway: 3. Calculate currentBlockHeight = prevBlockHeight + 1

SPVContract->>SPVContract: 4. Get Current Target (like addBlockHeader)
SPVContract->>TargetsHelperLib: Check for Recalculation Block & Recalculate if needed
SPVGateway->>SPVGateway: 4. Get Current Target (like addBlockHeader)
SPVGateway->>TargetsHelperLib: Check for Recalculation Block & Recalculate if needed
activate TargetsHelperLib
TargetsHelperLib-->>SPVContract: Return networkTarget
TargetsHelperLib-->>SPVGateway: Return networkTarget
deactivate TargetsHelperLib

SPVContract->>SPVContract: 5. Get Median Time
SPVGateway->>SPVGateway: 5. Get Median Time
alt 5.1. Num blocks added < 12
SPVContract->>SPVContract: Use _getStorageMedianTime (like addBlockHeader)
SPVGateway->>SPVGateway: Use _getStorageMedianTime (like addBlockHeader)
else 5.2. Num blocks added >= 12
SPVContract->>SPVContract: Use _getMemoryMedianTime (from batch data)
SPVGateway->>SPVGateway: Use _getMemoryMedianTime (from batch data)
end

SPVContract->>SPVContract: 6. Validate Block Rules (_validateBlockRules)
SPVGateway->>SPVGateway: 6. Validate Block Rules (_validateBlockRules)
alt Validation Fails
SPVContract--xCaller: Error: InvalidTarget / InvalidBlockHash / InvalidBlockTime
SPVGateway--xCaller: Error: InvalidTarget / InvalidBlockHash / InvalidBlockTime
end

SPVContract->>SPVContract: 7. Add Block To Chain (_addBlock)
SPVContract->>SPVContract: Emit BlockHeaderAdded
SPVGateway->>SPVGateway: 7. Add Block To Chain (_addBlock)
SPVGateway->>SPVGateway: Emit BlockHeaderAdded
end

SPVContract-->>Caller: Transaction Complete
deactivate SPVContract
SPVGateway-->>Caller: Transaction Complete
deactivate SPVGateway
```


# 📦 Contract Components
The solution is primarily composed of the main SPV contract and two essential helper libraries that manage the intricacies of Bitcoin's block structure and difficulty adjustments.
The solution primarily consists of the main SPV Gateway contract and the TargetsHelper library, which manages difficulty adjustments.

## SPVContract
## SPVGateway
This is the central contract that users will interact with. It serves as the primary interface for managing Bitcoin block headers on the EVM. It handles the core logic for adding and validating blocks, tracking the main Bitcoin chain, and providing querying functionalities. All custom errors and events related to the SPV operations are defined here, ensuring clear feedback and transparency during contract execution.

## BlockHeader Library
This is a pure utility library specifically designed to handle the low-level details of Bitcoin block headers. It's responsible for the precise parsing of raw 80-byte Bitcoin block header data into a structured format that Solidity can easily work with. Crucially, it manages the byte order conversions, translating Bitcoin's little-endian format to Solidity's big-endian, and vice-versa. It also provides the essential function for calculating the double SHA256 hash of a block header, which is fundamental for verifying Proof-of-Work.

## TargetsHelper Library
This library encapsulates all the complex mathematical and logical operations related to Bitcoin's difficulty targets. It provides functions to accurately calculate new difficulty targets based on elapsed time between blocks, ensuring the contract adheres to Bitcoin's dynamic difficulty adjustment rules. Additionally, it offers utilities for converting between the compact "bits" format (as found in Bitcoin block headers) and the full 256-bit target value, and it calculates the cumulative work associated with a given block or epoch, which is vital for determining the most valid chain.

Expand Down
Loading