diff --git a/Clarinet.toml b/Clarinet.toml index 4d223e1..e97c367 100644 --- a/Clarinet.toml +++ b/Clarinet.toml @@ -1,20 +1,22 @@ - [project] name = "btc-bridge" authors = [] +description = "" telemetry = true +requirements = [] +[contracts.btc-bridge] +path = "contracts/btc-bridge.clar" +depends_on = [] + +[repl] +costs_version = 2 +parser_version = 2 + [repl.analysis] passes = ["check_checker"] + [repl.analysis.check_checker] -# If true, inputs are trusted after tx_sender has been checked. +strict = false trusted_sender = false -# If true, inputs are trusted after contract-caller has been checked. trusted_caller = false -# If true, untrusted data may be passed into a private function without a -# warning, if it gets checked inside. This check will also propagate up to the -# caller. callee_filter = false - -# [contracts.counter] -# path = "contracts/counter.clar" -# depends_on = [] diff --git a/README.md b/README.md new file mode 100644 index 0000000..f789344 --- /dev/null +++ b/README.md @@ -0,0 +1,185 @@ +# Bitcoin-Stacks Bridge Smart Contract + +A secure and robust cross-chain bridge enabling asset transfers between Bitcoin and Stacks blockchains. + +## Overview + +The BTC-Stacks Bridge facilitates trustless asset transfers between Bitcoin and Stacks networks through a validator-based system with multiple security checks and balances. + +## Features + +- **Secure Cross-Chain Transfers**: Enable safe asset movement between Bitcoin and Stacks +- **Validator Management**: Multi-validator system for enhanced security +- **Deposit Management**: Structured handling of deposits with confirmations +- **Emergency Controls**: Safety mechanisms including pause functionality and emergency withdrawals +- **Balance Tracking**: Accurate tracking of user balances and total bridged amounts + +## Technical Architecture + +### Core Components + +1. **Token Interface** + +```clarity +(define-trait bridgeable-token-trait + ((transfer (uint principal principal) (response bool uint)) + (get-balance (principal) (response uint uint))) +) +``` + +2. **Data Storage** + +- Deposits tracking +- Validator registry +- Bridge balances +- Validator signatures + +### Security Parameters + +- Minimum deposit: 100,000 units +- Maximum deposit: 1,000,000,000 units +- Required confirmations: 6 + +## Functions + +### Administrative Functions + +1. **initialize-bridge** + + - Initializes the bridge in active state + - Restricted to contract deployer + +2. **pause-bridge / resume-bridge** + + - Emergency controls for bridge operations + - Restricted to contract deployer + +3. **add-validator / remove-validator** + - Validator management functions + - Restricted to contract deployer + +### Core Operations + +1. **initiate-deposit** + + ```clarity + (tx-hash (buff 32)) + (amount uint) + (recipient principal) + (btc-sender (buff 33)) + ``` + + - Initiates cross-chain deposit + - Requires validator authorization + - Validates transaction parameters + +2. **confirm-deposit** + + ```clarity + (tx-hash (buff 32)) + (signature (buff 65)) + ``` + + - Confirms deposit with validator signature + - Updates user balances + - Requires minimum confirmations + +3. **withdraw** + ```clarity + (amount uint) + (btc-recipient (buff 34)) + ``` + - Processes withdrawals to Bitcoin + - Validates balance and amount + - Updates bridge state + +### Query Functions + +1. **get-deposit**: Retrieves deposit details +2. **get-bridge-status**: Checks bridge operational status +3. **get-validator-status**: Verifies validator status +4. **get-bridge-balance**: Checks user balance + +### Validation Functions + +1. **is-valid-principal**: Validates Stacks addresses +2. **is-valid-btc-address**: Validates Bitcoin addresses +3. **is-valid-tx-hash**: Validates transaction hashes +4. **is-valid-signature**: Validates validator signatures +5. **validate-deposit-amount**: Validates deposit amounts + +## Error Handling + +The contract includes comprehensive error handling with specific error codes: + +| Code | Description | +| ---- | ------------------------ | +| 1000 | Unauthorized access | +| 1001 | Invalid amount | +| 1002 | Insufficient balance | +| 1003 | Invalid bridge status | +| 1004 | Invalid signature | +| 1005 | Already processed | +| 1006 | Bridge paused | +| 1007 | Invalid validator | +| 1008 | Invalid recipient | +| 1009 | Invalid BTC address | +| 1010 | Invalid transaction hash | +| 1011 | Invalid signature format | + +## Security Considerations + +1. **Access Control** + + - Strict validator management + - Administrative function restrictions + - Multi-signature requirements + +2. **Transaction Validation** + + - Comprehensive parameter validation + - Double-processing prevention + - Balance checks + +3. **Emergency Measures** + - Bridge pause mechanism + - Emergency withdrawal function + - Invalid transaction protection + +## Usage Examples + +### Initiating a Deposit + +```clarity +(contract-call? .btc-bridge initiate-deposit + 0x1234567890abcdef... ;; tx-hash + u500000 ;; amount + 'ST1234567890ABCDEF ;; recipient + 0x9876543210... ;; btc-sender +) +``` + +### Processing a Withdrawal + +```clarity +(contract-call? .btc-bridge withdraw + u500000 ;; amount + 0x9876543210... ;; btc-recipient +) +``` + +## Best Practices + +1. Always verify transaction status before confirmation +2. Maintain secure validator key management +3. Monitor bridge status before operations +4. Verify recipient addresses carefully +5. Check balance availability before withdrawals + +## Error Resolution + +1. **ERR-UNAUTHORIZED**: Verify caller has appropriate permissions +2. **ERR-INVALID-AMOUNT**: Ensure amount within valid range +3. **ERR-BRIDGE-PAUSED**: Check bridge operational status +4. **ERR-ALREADY-PROCESSED**: Verify transaction uniqueness +5. **ERR-INSUFFICIENT-BALANCE**: Confirm adequate balance diff --git a/contracts/btc-bridge.clar b/contracts/btc-bridge.clar new file mode 100644 index 0000000..95d08cc --- /dev/null +++ b/contracts/btc-bridge.clar @@ -0,0 +1,307 @@ +;; title: btc-bridge-contract +;; summary: Enables secure cross-chain transfers between Bitcoin and Stacks networks +;; description: Facilitates secure asset transfers between Bitcoin and Stacks blockchains, +;; handling deposits, withdrawals, and validator management with robust security checks. + + +;; traits +(define-trait bridgeable-token-trait + ( + (transfer (uint principal principal) (response bool uint)) + (get-balance (principal) (response uint uint)) + ) +) + + +;; constants +;; Error codes +(define-constant ERROR-NOT-AUTHORIZED u1000) +(define-constant ERROR-INVALID-AMOUNT u1001) +(define-constant ERROR-INSUFFICIENT-BALANCE u1002) +(define-constant ERROR-INVALID-BRIDGE-STATUS u1003) +(define-constant ERROR-INVALID-SIGNATURE u1004) +(define-constant ERROR-ALREADY-PROCESSED u1005) +(define-constant ERROR-BRIDGE-PAUSED u1006) +(define-constant ERROR-INVALID-VALIDATOR-ADDRESS u1007) +(define-constant ERROR-INVALID-RECIPIENT-ADDRESS u1008) +(define-constant ERROR-INVALID-BTC-ADDRESS u1009) +(define-constant ERROR-INVALID-TX-HASH u1010) +(define-constant ERROR-INVALID-SIGNATURE-FORMAT u1011) + +;; Constants +(define-constant CONTRACT-DEPLOYER tx-sender) +(define-constant MIN-DEPOSIT-AMOUNT u100000) +(define-constant MAX-DEPOSIT-AMOUNT u1000000000) +(define-constant REQUIRED-CONFIRMATIONS u6) + +;; data vars +(define-data-var bridge-paused bool false) +(define-data-var total-bridged-amount uint u0) +(define-data-var last-processed-height uint u0) + +;; data maps +(define-map deposits + { tx-hash: (buff 32) } + { + amount: uint, + recipient: principal, + processed: bool, + confirmations: uint, + timestamp: uint, + btc-sender: (buff 33) + } +) + +(define-map validators principal bool) +(define-map validator-signatures + { tx-hash: (buff 32), validator: principal } + { signature: (buff 65), timestamp: uint } +) + +(define-map bridge-balances principal uint) + +;; public functions +;; Initializes the bridge by setting the paused state to false. Only the contract deployer can call this function. +(define-public (initialize-bridge) + (begin + (asserts! (is-eq tx-sender CONTRACT-DEPLOYER) (err ERROR-NOT-AUTHORIZED)) + (var-set bridge-paused false) + (ok true) + ) +) + +;; Pauses the bridge. Only the contract deployer can call this function. +(define-public (pause-bridge) + (begin + (asserts! (is-eq tx-sender CONTRACT-DEPLOYER) (err ERROR-NOT-AUTHORIZED)) + (var-set bridge-paused true) + (ok true) + ) +) + +;; Resumes the bridge if it is paused. Only the contract deployer can call this function. +(define-public (resume-bridge) + (begin + (asserts! (is-eq tx-sender CONTRACT-DEPLOYER) (err ERROR-NOT-AUTHORIZED)) + (asserts! (var-get bridge-paused) (err ERROR-INVALID-BRIDGE-STATUS)) + (var-set bridge-paused false) + (ok true) + ) +) + +;; Adds a validator to the bridge. Only the contract deployer can call this function. +(define-public (add-validator (validator principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-DEPLOYER) (err ERROR-NOT-AUTHORIZED)) + (asserts! (is-valid-principal validator) (err ERROR-INVALID-VALIDATOR-ADDRESS)) + (map-set validators validator true) + (ok true) + ) +) + +;; Removes a validator from the bridge. Only the contract deployer can call this function. +(define-public (remove-validator (validator principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-DEPLOYER) (err ERROR-NOT-AUTHORIZED)) + (asserts! (is-valid-principal validator) (err ERROR-INVALID-VALIDATOR-ADDRESS)) + (map-set validators validator false) + (ok true) + ) +) + +;; Initiates a deposit into the bridge. Validators must call this function. +(define-public (initiate-deposit + (tx-hash (buff 32)) + (amount uint) + (recipient principal) + (btc-sender (buff 33)) +) + (begin + (asserts! (not (var-get bridge-paused)) (err ERROR-BRIDGE-PAUSED)) + (asserts! (validate-deposit-amount amount) (err ERROR-INVALID-AMOUNT)) + (asserts! (get-validator-status tx-sender) (err ERROR-NOT-AUTHORIZED)) + (asserts! (is-valid-tx-hash tx-hash) (err ERROR-INVALID-TX-HASH)) + (asserts! (is-none (map-get? deposits {tx-hash: tx-hash})) (err ERROR-ALREADY-PROCESSED)) + (asserts! (is-valid-principal recipient) (err ERROR-INVALID-RECIPIENT-ADDRESS)) + (asserts! (is-valid-btc-address btc-sender) (err ERROR-INVALID-BTC-ADDRESS)) + + (let + ((validated-deposit { + amount: amount, + recipient: recipient, + processed: false, + confirmations: u0, + timestamp: block-height, + btc-sender: btc-sender + })) + + (map-set deposits + {tx-hash: tx-hash} + validated-deposit + ) + (ok true) + ) + ) +) + +;; Confirms a deposit into the bridge. Validators must call this function. +(define-public (confirm-deposit + (tx-hash (buff 32)) + (signature (buff 65)) +) + (let ( + (deposit (unwrap! (map-get? deposits {tx-hash: tx-hash}) (err ERROR-INVALID-BRIDGE-STATUS))) + (is-validator (get-validator-status tx-sender)) + ) + (asserts! (not (var-get bridge-paused)) (err ERROR-BRIDGE-PAUSED)) + (asserts! (is-valid-tx-hash tx-hash) (err ERROR-INVALID-TX-HASH)) + (asserts! (is-valid-signature signature) (err ERROR-INVALID-SIGNATURE-FORMAT)) + (asserts! (not (get processed deposit)) (err ERROR-ALREADY-PROCESSED)) + (asserts! (>= (get confirmations deposit) REQUIRED-CONFIRMATIONS) (err ERROR-INVALID-BRIDGE-STATUS)) + + (asserts! + (is-none (map-get? validator-signatures {tx-hash: tx-hash, validator: tx-sender})) + (err ERROR-ALREADY-PROCESSED) + ) + + (let + ((validated-signature { + signature: signature, + timestamp: block-height + })) + + (map-set validator-signatures + {tx-hash: tx-hash, validator: tx-sender} + validated-signature + ) + + (map-set deposits + {tx-hash: tx-hash} + (merge deposit {processed: true}) + ) + + (map-set bridge-balances + (get recipient deposit) + (+ (default-to u0 (map-get? bridge-balances (get recipient deposit))) + (get amount deposit)) + ) + + (var-set total-bridged-amount + (+ (var-get total-bridged-amount) (get amount deposit)) + ) + (ok true) + ) + ) +) + +;; Withdraws an amount from the bridge to a Bitcoin recipient address. +(define-public (withdraw + (amount uint) + (btc-recipient (buff 34)) +) + (let ( + (current-balance (get-bridge-balance tx-sender)) + ) + (asserts! (not (var-get bridge-paused)) (err ERROR-BRIDGE-PAUSED)) + (asserts! (>= current-balance amount) (err ERROR-INSUFFICIENT-BALANCE)) + (asserts! (validate-deposit-amount amount) (err ERROR-INVALID-AMOUNT)) + + (map-set bridge-balances + tx-sender + (- current-balance amount) + ) + + (print { + type: "withdraw", + sender: tx-sender, + amount: amount, + btc-recipient: btc-recipient, + timestamp: block-height + }) + + (var-set total-bridged-amount (- (var-get total-bridged-amount) amount)) + (ok true) + ) +) + +;; Allows the contract deployer to perform an emergency withdrawal. +(define-public (emergency-withdraw (amount uint) (recipient principal)) + (begin + (asserts! (is-eq tx-sender CONTRACT-DEPLOYER) (err ERROR-NOT-AUTHORIZED)) + (asserts! (>= (var-get total-bridged-amount) amount) (err ERROR-INSUFFICIENT-BALANCE)) + (asserts! (is-valid-principal recipient) (err ERROR-INVALID-RECIPIENT-ADDRESS)) + + (let ( + (current-balance (default-to u0 (map-get? bridge-balances recipient))) + (new-balance (+ current-balance amount)) + ) + (asserts! (> new-balance current-balance) (err ERROR-INVALID-AMOUNT)) + (map-set bridge-balances recipient new-balance) + (ok true) + ) + ) +) + +;; read only functions +;; Retrieves the details of a deposit using the transaction hash. +(define-read-only (get-deposit (tx-hash (buff 32))) + (map-get? deposits {tx-hash: tx-hash}) +) + +;; Retrieves the current status of the bridge (paused or not). +(define-read-only (get-bridge-status) + (var-get bridge-paused) +) + +;; Checks if a given principal is a validator. +(define-read-only (get-validator-status (validator principal)) + (default-to false (map-get? validators validator)) +) + +;; Retrieves the bridge balance of a user. +(define-read-only (get-bridge-balance (user principal)) + (default-to u0 (map-get? bridge-balances user)) +) + +;; Validates if a given principal address is valid. +(define-read-only (is-valid-principal (address principal)) + (and + (not (is-eq address CONTRACT-DEPLOYER)) + (not (is-eq address (as-contract tx-sender))) + ) +) + +;; Validates if a given Bitcoin address is valid. +(define-read-only (is-valid-btc-address (btc-addr (buff 33))) + (and + (is-eq (len btc-addr) u33) + (not (is-eq btc-addr 0x000000000000000000000000000000000000000000000000000000000000000000)) + true + ) +) + +;; Validates if a given transaction hash is valid. +(define-read-only (is-valid-tx-hash (tx-hash (buff 32))) + (and + (is-eq (len tx-hash) u32) + (not (is-eq tx-hash 0x0000000000000000000000000000000000000000000000000000000000000000)) + true + ) +) + +;; Validates if a given signature is valid. +(define-read-only (is-valid-signature (signature (buff 65))) + (and + (is-eq (len signature) u65) + (not (is-eq signature 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)) + true + ) +) + +;; Validates if a given deposit amount is within the allowed range. +(define-read-only (validate-deposit-amount (amount uint)) + (and + (>= amount MIN-DEPOSIT-AMOUNT) + (<= amount MAX-DEPOSIT-AMOUNT) + ) +) \ No newline at end of file diff --git a/tests/btc-bridge_test.ts b/tests/btc-bridge_test.ts new file mode 100644 index 0000000..9a18ae0 --- /dev/null +++ b/tests/btc-bridge_test.ts @@ -0,0 +1,26 @@ + +import { Clarinet, Tx, Chain, Account, types } from 'https://deno.land/x/clarinet@v0.14.0/index.ts'; +import { assertEquals } from 'https://deno.land/std@0.90.0/testing/asserts.ts'; + +Clarinet.test({ + name: "Ensure that <...>", + async fn(chain: Chain, accounts: Map) { + let block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 2); + + block = chain.mineBlock([ + /* + * Add transactions with: + * Tx.contractCall(...) + */ + ]); + assertEquals(block.receipts.length, 0); + assertEquals(block.height, 3); + }, +});