Skip to content

Commit 8a1ad25

Browse files
authored
Merge pull request #3 from distributed-lab/feature/docs
Add docs
2 parents 3f0250a + abfd33e commit 8a1ad25

File tree

12 files changed

+604
-45
lines changed

12 files changed

+604
-45
lines changed

.github/actions/setup/action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ runs:
1414
- name: Install packages
1515
run: npm install
1616
shell: bash
17+
18+
- name: Compile
19+
run: npm run compile
20+
shell: bash

.github/workflows/docs.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: "docs"
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
# Add permissions for GitHub Pages deployment
9+
permissions:
10+
contents: read # Needed to checkout the repo
11+
pages: write # Needed to deploy to GitHub Pages
12+
id-token: write # Needed for OIDC token authentication
13+
14+
jobs:
15+
docs:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout the repository
19+
uses: actions/checkout@v4
20+
21+
- name: Setup
22+
uses: ./.github/actions/setup
23+
24+
- name: Generate Docs
25+
run: |
26+
npx hardhat markup --outdir docs/contracts
27+
cp README.md docs/
28+
npm install -g docsify-cli
29+
docsify generate docs
30+
31+
- name: Setup Pages
32+
uses: actions/configure-pages@v4
33+
34+
- name: Upload artifact
35+
uses: actions/upload-pages-artifact@v3
36+
with:
37+
path: 'docs'
38+
39+
deploy:
40+
permissions:
41+
pages: write
42+
id-token: write
43+
environment:
44+
name: github-pages
45+
url: ${{ steps.deployment.outputs.page_url }}
46+
runs-on: ubuntu-latest
47+
needs: docs
48+
steps:
49+
- name: Deploy to GitHub Pages
50+
id: deployment
51+
uses: actions/deploy-pages@v4

README.md

Lines changed: 200 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,213 @@
1-
# SPV Contracts
2-
3-
Smart contract for verifying Bitcoin block headers on EVM-compatible chains using the **Simple Payment Verification (SPV)** method.
4-
5-
This contract behaves like an SPV node: it builds a valid chain of Bitcoin block headers, verifies them according to Bitcoin consensus rules, and enables Merkle Proof-based verification of Bitcoin transactions.
6-
7-
## Features
8-
9-
- Stores and verifies Bitcoin block headers
10-
- Validates headers using:
11-
- Proof of Work (`bits``target`)
12-
- Median time rule
13-
- Chain continuity
14-
- Handles difficulty adjustment every 2016 blocks
15-
- Supports pending difficulty epochs before finalization
16-
- Stores historical targets and supports reorg handling
17-
18-
## Contract: `SPVContract.sol`
1+
# 🛡️ SPV Contract: Bitcoin Light Client on EVM
2+
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.
3+
4+
# ✨ Why this SPV Contract?
5+
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:
6+
- **Cross-chain bridges** for Bitcoin-backed assets
7+
- **Light clients** for dApps that need to confirm Bitcoin transaction finality
8+
- **Decentralized custodianship** solutions
9+
- **Oracle services** for Bitcoin data on EVM
10+
11+
# 🚀 Key Features
12+
- **Block Header Submission:** Efficiently add individual or batches of Bitcoin block headers to the contract.
13+
- **Mainchain Tracking:** Automatically identifies and updates the "main" Bitcoin chain based on accumulated work.
14+
- **Block Validation:** Verifies block headers against Bitcoin's consensus rules, including:
15+
- Proof-of-Work (target difficulty)
16+
- Block time validity (median time past)
17+
- Chain continuity (previous block hash)
18+
- **Block Information Retrieval:** Query detailed information about any stored block, such as:
19+
- Its Merkle root
20+
- Its height
21+
- Its inclusion status in the mainchain
22+
- Its cumulative work (difficulty)
23+
- Its confirmation count relative to the mainchain head
24+
- **Difficulty Adjustment:** Integrates Bitcoin's precise difficulty adjustment algorithm to accurately calculate current and future targets.
25+
26+
# ⚙️ How it Works (Under the Hood)
27+
The contract operates by receiving raw Bitcoin block headers, which are then parsed and validated against Bitcoin's strict consensus rules.
28+
29+
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.
30+
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.
31+
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).
32+
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.
33+
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.
34+
35+
# 📊 Flow Diagrams
36+
These diagrams outline the step-by-step process for adding block headers to the SPV Contract.
37+
38+
### `addBlockHeader(bytes calldata blockHeaderRaw_)` Sequence Diagram
39+
40+
```mermaid
41+
sequenceDiagram
42+
participant Caller
43+
participant SPVContract
44+
participant BlockHeaderLib
45+
participant TargetsHelperLib
46+
47+
Caller->>SPVContract: addBlockHeader(blockHeaderRaw)
48+
activate SPVContract
49+
50+
SPVContract->>BlockHeaderLib: 1. Parse blockHeaderRaw_ (parseBlockHeaderData)
51+
activate BlockHeaderLib
52+
BlockHeaderLib-->>SPVContract: 1.1. Check length (80 bytes) & LE to BE
53+
alt Length Invalid
54+
BlockHeaderLib--xSPVContract: Error: InvalidBlockHeaderDataLength
55+
SPVContract--xCaller: Revert
56+
end
57+
BlockHeaderLib-->>SPVContract: 1.2. Return BlockHeaderData & blockHash
58+
deactivate BlockHeaderLib
59+
60+
SPVContract->>SPVContract: 1.3. Check blockHash existence
61+
alt BlockHash Exists
62+
SPVContract--xCaller: Error: BlockAlreadyExists
63+
end
64+
65+
SPVContract->>SPVContract: 2. Check prevBlockHash existence
66+
alt Prev Block Missing
67+
SPVContract--xCaller: Error: PrevBlockDoesNotExist
68+
end
69+
70+
SPVContract->>SPVContract: 3. Calculate newBlockHeight = prevBlockHeight + 1
71+
72+
SPVContract->>SPVContract: 4. Get Current Target
73+
SPVContract->>SPVContract: 4.1. Get target from prevBlockBits
74+
SPVContract->>TargetsHelperLib: Check if newBlockHeight is Recalculation Block (isTargetAdjustmentBlock)
75+
activate TargetsHelperLib
76+
alt Recalculation Block
77+
SPVContract->>SPVContract: Recalculate target & Save lastEpochCumulativeWork
78+
TargetsHelperLib-->>SPVContract: Return newNetworkTarget
79+
else Not Recalculation Block
80+
TargetsHelperLib-->>SPVContract: Use prevBlockTarget as networkTarget
81+
end
82+
deactivate TargetsHelperLib
83+
84+
SPVContract->>SPVContract: 5. Check Block Rules
85+
SPVContract->>TargetsHelperLib: 5.1. Check Header Target == Contract Target
86+
activate TargetsHelperLib
87+
TargetsHelperLib-->>SPVContract: Result
88+
deactivate TargetsHelperLib
89+
alt Invalid Target
90+
SPVContract--xCaller: Error: InvalidTarget
91+
end
92+
93+
SPVContract->>SPVContract: 5.2. Check newBlockHash <= networkTarget (PoW)
94+
alt Invalid Block Hash
95+
SPVContract--xCaller: Error: InvalidBlockHash
96+
end
97+
98+
SPVContract->>SPVContract: 5.3. Check newBlockTime >= medianTime
99+
alt Invalid Block Time
100+
SPVContract--xCaller: Error: InvalidBlockTime
101+
end
102+
103+
SPVContract->>SPVContract: 6. Add Block To Chain
104+
SPVContract->>SPVContract: 6.1. Save newBlockHeader & newBlockHash to Storage
105+
106+
SPVContract->>SPVContract: 6.2. Update Mainchain
107+
alt 6.2.1. prevBlockHash == mainchainHead?
108+
SPVContract->>SPVContract: Move mainchainHead to newBlockHash
109+
else
110+
SPVContract->>SPVContract: 6.2.2. Calculate New Block & Current Head Cumulative Work
111+
SPVContract->>SPVContract: 6.2.3. newBlock Cumulative Work > Current Head?
112+
alt New Block Has Higher Work
113+
SPVContract->>SPVContract: Set New Block as mainchainHead
114+
SPVContract->>SPVContract: Recursively update mainchain path backwards (do-while loop)
115+
end
116+
end
117+
118+
SPVContract->>SPVContract: Emit BlockHeaderAdded
119+
SPVContract-->>Caller: Transaction Complete
120+
deactivate SPVContract
121+
```
19122

20-
### Key Functions
123+
### `addBlockHeaderBatch(bytes[] calldata blockHeaderRawArr_)` Sequence Diagram
124+
125+
This function processes multiple block headers in a single transaction, iterating through the array and validating each sequentially.
126+
127+
```mermaid
128+
sequenceDiagram
129+
participant Caller
130+
participant SPVContract
131+
participant BlockHeaderLib
132+
participant TargetsHelperLib
133+
134+
Caller->>SPVContract: addBlockHeaderBatch(blockHeaderRawArray_)
135+
activate SPVContract
136+
137+
SPVContract->>SPVContract: Check if Header Array is Empty
138+
alt Array Empty
139+
SPVContract--xCaller: Error: EmptyBlockHeaderArray
140+
end
141+
142+
SPVContract->>BlockHeaderLib: 1. Parse Block Headers Array (_parseBlockHeadersRaw)
143+
activate BlockHeaderLib
144+
BlockHeaderLib-->>SPVContract: Returns BlockHeaderData[] & bytes32[]
145+
deactivate BlockHeaderLib
146+
147+
loop For each blockHeader in parsed array (from i=0 to length-1)
148+
SPVContract->>SPVContract: 2. Check prevBlockHash for current block
149+
alt First block in batch
150+
SPVContract->>SPVContract: Check prevBlockHash existence (like addBlockHeader)
151+
alt Prev Block Missing
152+
SPVContract--xCaller: Error: PrevBlockDoesNotExist
153+
end
154+
else Subsequent blocks
155+
SPVContract->>SPVContract: Check prevBlockHash == blockHash of (i-1)th block
156+
alt Order Invalid
157+
SPVContract--xCaller: Error: InvalidBlockHeadersOrder
158+
end
159+
end
160+
161+
SPVContract->>SPVContract: 3. Calculate currentBlockHeight = prevBlockHeight + 1
162+
163+
SPVContract->>SPVContract: 4. Get Current Target (like addBlockHeader)
164+
SPVContract->>TargetsHelperLib: Check for Recalculation Block & Recalculate if needed
165+
activate TargetsHelperLib
166+
TargetsHelperLib-->>SPVContract: Return networkTarget
167+
deactivate TargetsHelperLib
168+
169+
SPVContract->>SPVContract: 5. Get Median Time
170+
alt 5.1. Num blocks added < 12
171+
SPVContract->>SPVContract: Use _getStorageMedianTime (like addBlockHeader)
172+
else 5.2. Num blocks added >= 12
173+
SPVContract->>SPVContract: Use _getMemoryMedianTime (from batch data)
174+
end
175+
176+
SPVContract->>SPVContract: 6. Validate Block Rules (_validateBlockRules)
177+
alt Validation Fails
178+
SPVContract--xCaller: Error: InvalidTarget / InvalidBlockHash / InvalidBlockTime
179+
end
180+
181+
SPVContract->>SPVContract: 7. Add Block To Chain (_addBlock)
182+
SPVContract->>SPVContract: Emit BlockHeaderAdded
183+
end
184+
185+
SPVContract-->>Caller: Transaction Complete
186+
deactivate SPVContract
187+
```
21188

22-
#### `addBlockHeader(bytes calldata blockHeaderRaw)`
23-
Adds and validates a new block header, updates internal state, and emits an event.
24189

25-
### Validation Rules
26-
- `prevBlockHash` must point to a known block
27-
- New `blockHash` must not exist
28-
- Header `bits` must match the expected network target
29-
- Header `time` must be > median of last 11 blocks
30-
- `blockHash` must be less than or equal to the target (valid PoW)
190+
# 📦 Contract Components
191+
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.
31192

32-
## Storage Structure
193+
## SPVContract
194+
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.
33195

34-
- `BlocksData` – stores block headers, timestamps, and chain height
35-
- `TargetsData` – handles target values and difficulty epochs
36-
- `pendingTargetHeightCount` – controls target finalization after N blocks
196+
## BlockHeader Library
197+
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.
37198

38-
## Dev Info
39-
### Compilation
199+
## TargetsHelper Library
200+
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.
40201

202+
# 💻 Dev Info
203+
## Compilation
41204
To compile the contracts, use the next script:
42205

43206
```bash
44207
npm run compile
45208
```
46209

47-
### Test
48-
210+
## Test
49211
To run the tests, execute the following command:
50212

51213
```bash
@@ -58,19 +220,17 @@ Or to see the coverage, run:
58220
npm run coverage
59221
```
60222

61-
### Local deployment
62-
223+
## Local deployment
63224
To deploy the contracts locally, run the following commands (in the different terminals):
64225

65226
```bash
66227
npm run private-network
67228
npm run deploy-localhost
68229
```
69230

70-
### Bindings
71-
231+
## Bindings
72232
The command to generate the bindings is as follows:
73233

74234
```bash
75235
npm run generate-types
76-
```
236+
```

0 commit comments

Comments
 (0)