Bithoven Developer Guide

Learn to build safe, expressive smart contracts on Bitcoin with readable, type-safe code.

Version 0.1.0 Beta Legacy / SegWit / Taproot Formally Specified
πŸ†• v0.1.0: a major revision of the analyzer. The compiler now proves that every spending path needs a valid signature, rejects contracts no witness can satisfy, and enforces stricter well-formedness rules. See what changed β†’

1. Introduction

Bithoven is a high-level, imperative programming language designed for Bitcoin smart contracts. It bridges the gap between complex contract logic and the low-level stack machine of the Bitcoin Virtual Machine (VM).

Unlike writing raw Bitcoin Scriptβ€”which is notoriously difficult and error-proneβ€”Bithoven allows you to write readable, auditable code with modern control flow (if/else), named variables, and built-in safety checks. It compiles down to highly optimized native Bitcoin Script.

Why Bithoven?

  • Imperative Syntax: Use familiar control flow instead of mental stack juggling.
  • Formal Safety: A type checker, a liveness analyzer, a consensus-literal checker and a path-sensitive semantic analyzer reject anyone-can-spend paths, signature misuse, type errors and unspendable contracts before deployment.
  • Proven, then Tested: Type soundness and a refinement theorem connect the source to the emitted script, and the compiler is differentially tested against Bitcoin Core.
  • Low Overhead: On standard Miniscript policies, emitted scripts are within a median of one byte of rust-miniscript.

πŸ“Š Bithoven vs. Raw Bitcoin Script

❌ Bitcoin Script (Raw)
OP_IF
  <1000> OP_CSV OP_DROP
  <alice_pk> OP_CHECKSIG
OP_ELSE
  OP_HASH256
  <hash> OP_EQUALVERIFY
  <bob_pk> OP_CHECKSIG
OP_ENDIF

Stack-based, hard to read, error-prone

βœ… Bithoven (High-Level)
if condition {
  older 1000;
  return checksig(sig_alice, "<alice_pk>");
} else {
  verify sha256(sha256(preimage)) == "<hash>";
  return checksig(sig_bob, "<bob_pk>");
}

Readable, type-safe, checked before deployment

πŸ†• What's New in v0.1.0

v0.1.0 is a major revision of the analyzer and back end, accompanying the Bithoven paper.

Upgrading from 0.0.x?

Contracts that relied on the looser rules may stop compiling. Give every if an else (NoReturn), write exactly one declaration per spending path (DeclarationPathMismatch), and use 32-byte x-only keys under taproot or 33-byte compressed keys under legacy/segwit (MalformedPubkey).

2. Getting Started

Installation

You can install the Bithoven compiler via the command line or package managers.

Terminal
# For Rust Users
cargo add bithoven

# For Node.js Users
npm install bithoven

# CLI Tool
cargo install bithoven

Online IDE

Try Bithoven instantly in your browser without installation:

Go to Bithoven Online IDE

3. Structure of a Contract

A Bithoven contract (file extension .bithoven) consists of three main parts:

  1. Pragmas: Compiler directives.
  2. Stack Definitions: The expected input variables (Witness data), one declaration per spending path.
  3. Logic Block: The executable code, in which every path ends in a return.

Example: Single Signature

// 1. Pragmas
pragma bithoven version 0.0.1;
pragma bithoven target segwit;

// 2. Stack Input Definitions (The "Witness")
(sig_alice: signature)

// 3. Logic Block
{
    return checksig(sig_alice, "0245a6b3f8eeab8e88501a9a25391318dce9bf35e24c377ee82799543606bf5212");
}

4. Language Basics

Pragmas

Pragmas define the compiler version and the Bitcoin protocol target. The target controls opcode generation.

A key literal in the wrong encoding for the target, or one that is not a point on secp256k1, is rejected with MalformedPubkey.

Types

Type Description Example
bool Boolean values true
number Integer values (32-bit signed, Β±2,147,483,647) 1000
string Hex or ASCII string data (at most 520 bytes) "secret"
signature ECDSA (legacy/segwit) or Schnorr (taproot) signatures, supplied by the witness sig_alice

Public keys are not a declarable type. A key must be a string literal such as "0245...", so a spender can never substitute a key of their own; a key taken from the witness is rejected with TypeMismatch.

Operators

Operands are type-checked at compile time; a mismatch is rejected with InvalidOperation.

Operators Operands Result
+ - max(a, b) min(a, b) ++ -- negate abs number number
< <= > >= number bool
== != Two operands of the same type: number, string or bool bool
&& || ! bool bool
sha256 ripemd160 number, string or bool (not signature) string
len string number
checksig signature and key literal pairs bool

Stack Input Definitions

Bithoven allows you to define multiple valid "spending paths" at the top of your file. The compiler verifies that every path is secure.

(condition: bool, sig_alice: signature) // Path A: the if side
(condition: bool, preimage: string, sig_bob: signature) // Path B: the else side

The compiler enforces four rules on declarations:

πŸ’‘ A witness serializes the stack bottom-to-top, so when spending, the first declared item is the last one pushed.

5. Control Flow

Conditionals

Use standard if/else logic. Every if needs an else.

if condition {
    // Path A
    return checksig(sig_alice, "<pk_alice>");
} else {
    // Path B
    return checksig(sig_bob, "<pk_bob>");
}

Assertions

Halts execution if false.

// Fails transaction if false
verify sha256(preimage) == "53de...4e45f";

Structural Rules

Bithoven has no loops, recursion or calls, so a contract is a tree of if/else branches whose leaves are returns. The compiler requires that:

6. Bitcoin Primitives

Time Locks

The compiler range-checks every locktime literal and rejects one out of range with IntegerOverflow.

Cryptography

Hashing: sha256(data), ripemd160(data). Nested hashes compile to single opcodes: sha256(sha256(x)) becomes OP_HASH256 and ripemd160(sha256(x)) becomes OP_HASH160.

Length: len(s) returns the byte length of a string.

Signature Checks: Supports single and multisig via checksig. Keys must be literals in the target's encoding. Multisig compiles to OP_CHECKMULTISIG under legacy/segwit and to a chain of OP_CHECKSIGADD under taproot.

// Single Signature
checksig(sig_var, "pubkey_hex")

// 2-of-2 Multisig
checksig [2,
    (sig_alice, "pk_alice_hex"),
    (sig_bob, "pk_bob_hex")
]

7. Security Features

Bithoven is "Correct-by-Construction." A contract reaches the code generator only after passing all four analysis stages below, and every rejection comes with a diagnostic pointing at the offending line and column.

Analysis Pipeline

1. Type Checker

Rejects arithmetic on strings or on checksig results and comparisons across types. Keys must be on-curve literals in the target's encoding, so a spender cannot supply their own key (uncertain-sig) and no key is impossible to sign for (impossible-key).

2. Liveness & Scope Analyzer

Ensures every declared witness item (especially signatures) is consumed exactly once, in stack order, on every path, and that the declarations match the paths. Prevents signature reuse and leftover stack items.

3. Consensus-Literal Checker

Checks literals against Bitcoin's consensus limits: numbers fit the 32-bit script range, byte strings fit the 520-byte push limit, and locktimes fit their BIP-65 and BIP-68 fields.

4. Semantic & Control-Flow Analyzer

Enumerates every terminating path and asks whether it can succeed with every checksig forced to false, whether it can succeed at all, and whether it ends in an explicit return. Prevents anyone-can-spend bugs and unspendable outputs.

Signature Necessity

The central security property: no accepted spending path can succeed unless a checksig on that path returns true. Rather than matching syntactic patterns, the analyzer turns each path's success condition into a boolean formula, pins every signature check to false, and asks whether the formula can still be satisfied. Anything it cannot model exactly is treated as attacker-controlled, so it errs toward rejecting a contract, never toward accepting one.

This contract looks signature-gated, but its only path succeeds exactly when the signature check fails:

pragma bithoven version 0.0.1;
pragma bithoven target segwit;

(sig_alice: signature)
{
    return checksig(sig_alice, "0245a6b3f8eeab8e88501a9a25391318dce9bf35e24c377ee82799543606bf5212") == false;
}
Error at line 6:5: UselessSig("A checksig is present on the top-level path but it does not gate the spend: the return at line 6:5 can succeed without a valid signature.")

Because the check reasons about what each path computes rather than how it is spelled, equivalent rewrites get the same verdict (.. elides the signature and key):

Contract body Verdict
return !checksig(..); ❌ UselessSig
return checksig(..) == false; ❌ UselessSig
return checksig(..) || flag; ❌ UselessSig
if checksig(..) { return false; } else { return true; } ❌ UselessSig
if flag { return true; } else { return checksig(..); } ❌ NoSigRequired
verify false; return checksig(..); ❌ DeadPath
return checksig(..) == true; βœ… Compiles
return !!checksig(..); βœ… Compiles

The analysis is bounded at 20 attacker-controlled inputs per path; a path above the bound is rejected rather than trusted. Keyless spending paths, such as a bare hash lock or a bare timelock, are outside the guarantee by design and rejected with NoSigRequired.

Compiler Diagnostics

Every diagnostic is reported as Error at line L:C: Kind("message"). Names in italics are the defect classes of BSHunter (Zheng et al., ICSE 2023), a study of defective scripts deployed on Bitcoin.

Diagnostic Stage Rejected when
ParseError Parser The source does not match the grammar
InvalidOperation Type checker An operand has the wrong type: arithmetic on a string or a checksig result, comparing a number to a string, a non-bool condition
TypeMismatch Type checker A public key comes from the witness instead of a literal (uncertain-sig), or a signature is not a declared witness item
MalformedPubkey Type checker A key literal is not on secp256k1 or not in the target's encoding (impossible-key)
IntegerOverflow Consensus literals A number outside Β±2,147,483,647, a byte string over 520 bytes, or a locktime out of range
UndefinedVariable
DuplicateVariable
Liveness & scope A name is not declared on the path, or is declared twice in one declaration
VariableConsumed
UnusedVariable
Liveness & scope A witness item is used twice, or never used
InvalidConsumptionOrder Liveness & scope An item is used while one declared before it is still unused
DeclarationPathMismatch Liveness & scope The declarations do not match the spending paths in number or in their shared prefix
NoReturn
UnreachableCode
Control flow A path has no return or an if has no else; or a statement follows a return or an if/else
NoSigRequired Semantic security A path with no signature check can succeed (unbound-txid), or a path exceeds the analysis bound
UselessSig Semantic security A path has a checksig but can succeed without a valid signature (useless-sig)
DeadPath Semantic security Every path is provably unsatisfiable (never-true)

Formal Guarantees & Evaluation

The Bithoven paper gives the language a formal syntax, big-step operational semantics and type system, and states two guarantees separately:

The refinement theorem rests on an explicit trusted base, chiefly that Bitcoin Core implements the modeled fragment of Script. That assumption is tested rather than taken on faith:

0
of 32,560 executions accepted by Bitcoin Core but rejected by the semantics
11 / 11
injected compiler faults detected by the harness
5 / 6
BSHunter defect classes prevented at compile time
17 / 17
standard signature-gated policies accepted, 0 false positives
+1 byte
median script size vs. rust-miniscript
0.147 ms
median compile time per accepted contract (Apple M3 Pro)

The sixth BSHunter class, simple-key (a weak private key), is invisible to any analysis of the contract. Every figure can be reproduced with cargo test and cargo bench; see tests/README.md.

Scope

Script-level resource limits (201 opcodes, 10,000-byte scripts, 1,000 stack items, 20 keys per multisig) are not enforced, so a very large contract can compile to an unspendable script. Taproot output is checked against expected opcodes but not executed against consensus, since libbitcoinconsensus has no taproot verification flag.

8. Examples Gallery

Explore real-world Bitcoin smart contract patterns implemented in Bithoven. Each example demonstrates key concepts and can be tried live in the Web IDE.

πŸŽ“ Suggested Learning Path

1 Beginner β†’ Basic signatures & locks
2 Intermediate β†’ Combine concepts
3 Advanced β†’ Multi-party patterns
4 Expert β†’ Complex systems

BEGINNER Getting Started

✍️ Single Signature (P2PK) Try in IDE β†’

The most basic Bitcoin transaction - requires only a signature to spend.

πŸ’‘ Learns: Basic syntax, checksig operation

Use case: Simple wallet, standard payments

πŸ”’ Hash Lock Try in IDE β†’

Spend coins by revealing a secret that matches a double-SHA256 hash, together with a signature.

πŸ’‘ Learns: Hash verification, secret reveals, why every path needs a signature

Use case: Atomic swaps, payment channels

⏰ Time Lock (CLTV) Try in IDE β†’

Coins locked until a specific block height using absolute timelocks, then spendable with a signature.

πŸ’‘ Learns: Absolute timelocks (after), CLTV

Use case: Vesting schedules, delayed payments

INTERMEDIATE Combining Concepts

⚑ HTLC (Hash Time-Locked Contract) Try in IDE β†’

Lightning Network standard combining hash locks and timelocks with if/else branches.

πŸ’‘ Learns: Conditional paths, combining hash+time locks, relative timelocks (older)

Use case: Lightning Network, payment channels

πŸ”„ Atomic Swap Try in IDE β†’

Trustless cross-chain trading with hash locks and timeout refunds.

πŸ’‘ Learns: Cross-chain patterns, double SHA256, refund mechanisms

Use case: Decentralized exchanges, cross-chain swaps

πŸ‘₯ Multisig (2-of-2) Try in IDE β†’

Taproot multi-signature authorization requiring both parties to sign.

πŸ’‘ Learns: Multisig syntax, Taproot vs SegWit differences

Use case: Joint accounts, shared custody

ADVANCED Multi-Party Patterns

🀝 Escrow (2-of-3 Multisig) Try in IDE β†’

Marketplace contract with buyer, seller, and arbitrator. Multiple spending paths for different scenarios.

πŸ’‘ Learns: 3-party coordination, nested conditions, refund timeouts

Use case: P2P marketplaces, freelance platforms, real estate

🏦 Vault (Time-Delayed Withdrawal) Try in IDE β†’

Security wallet with delayed withdrawals and immediate cold storage recovery.

πŸ’‘ Learns: Hot/cold key hierarchy, security delays, emergency recovery

Use case: Corporate treasuries, high-value custody, exchange cold storage

πŸ—³οΈ Multisig Voting (2-of-3 Threshold) Try in IDE β†’

Democratic governance requiring 2-of-3 approval with security timelock, or 3-of-3 emergency override.

πŸ’‘ Learns: Threshold signatures, governance delays, emergency overrides

Use case: DAO treasuries, board approvals, multi-stakeholder governance

πŸ‘¨β€πŸ‘©β€πŸ‘§ Inheritance (Tiered Access) Try in IDE β†’

Three-tier access: owner (anytime), heir (after 1000 blocks + secret), lawyers (after 10,000 blocks).

πŸ’‘ Learns: Cascading timelocks, secret-based access, fallback multisig

Use case: Estate planning, inheritance vaults, tiered recovery

EXPERT Complex Systems

🎲 Prediction Market (Oracle Integration) Try in IDE β†’

Decentralized betting with cryptographic oracle commitment, multi-outcome resolution, and refund mechanism.

πŸ’‘ Learns: Oracle commitment schemes, multi-outcome branching, complex refund logic

Use case: Sports betting, price predictions, decentralized insurance

πŸ“š Quick Reference

By Difficulty:

  • Beginner singlesig, hashlock, timelock
  • Intermediate htlc, atomic_swap, multisig
  • Advanced escrow, vault, voting, inheritance
  • Expert prediction_market

By Use Case:

  • Payments: Lightning (htlc), atomic_swap
  • Security: vault, inheritance
  • Governance: multisig_voting, escrow
  • DeFi: prediction_market, atomic_swap

9. Frequently Asked Questions

Q: What is Bithoven?

A: Bithoven is a high-level, type-safe programming language for writing Bitcoin smart contracts. It provides imperative syntax (if/else, return) instead of low-level stack operations, and compiles to native Bitcoin Script.

Q: Do I need to know Bitcoin Script to use Bithoven?

A: No! That's the point. Bithoven abstracts away the complexity of Bitcoin Script's stack-based operations. You write readable code with familiar programming constructs, and the compiler handles the low-level details.

Q: How does Bithoven compare to Miniscript?

A: Miniscript (BIP-379) is a structured, composable subset of Bitcoin Script, best understood as an analyzable assembly language and compiler target. Its grammar has no named witness bindings, arithmetic or imperative control flow, so it obtains safety by excluding them. Bithoven is an imperative source language that admits those constructs and obtains comparable guarantees by analysis instead. On 17 standard Miniscript policies, its output is within a median of one byte of rust-miniscript's. A few Miniscript constructs have no Bithoven counterpart: pk_h, because keys must be literals; or_d, whose OP_IFDUP idiom conflicts with linear consumption; and thresh over arbitrary sub-policies, which Bithoven expresses only over keys (multisig) or as a conjunction.

Q: Why is my hash lock or timelock rejected with NoSigRequired?

A: By design, every spending path must require a valid signature. A bare hash lock lets anyone who sees the preimage, for example in the mempool, claim the coins, and a bare timelock lets anyone spend once it expires. Add a signature check to the path: verify sha256(preimage) == "<hash>"; return checksig(sig, "<pubkey>");

Q: My contract compiled with 0.0.x but fails with v0.1.0. Why?

A: v0.1.0 enforces rules that 0.0.x did not. The most common fixes are giving every if an else, writing exactly one stack declaration per spending path, and using the key encoding the target requires. See What's New in v0.1.0 for the full list.

Q: What's the difference between older and after?

A: older N is a relative timelock (OP_CHECKSEQUENCEVERIFY) - locks coins for N blocks after the UTXO is created. after N is an absolute timelock (OP_CHECKLOCKTIMEVERIFY) - locks coins until block height N. Use older for "wait X blocks from now", and after for "unlock at specific time".

Q: Can Bithoven create multiple outputs or check UTXO values?

A: No. Bitcoin Script (and therefore Bithoven) has amount blindness - scripts cannot read UTXO values or control output amounts. These require pre-signed transactions or off-chain coordination. Bithoven focuses on spending conditions, not transaction construction.

Q: Is Bithoven production-ready?

A: Bithoven v0.1.0 is in beta. The paper proves type safety and refinement for the language, and the compiler is differentially tested against Bitcoin Core, but it has not been independently audited. It also does not enforce Bitcoin's script-level resource limits, and its taproot output has not been executed against consensus (see Scope). Use it for research, prototyping, and learning. For production deployments, test on regtest or signet and conduct a security review.

Q: How do I test my Bithoven contracts?

A: Use the Web IDE for quick experimentation. For thorough testing, compile your contract and use Bitcoin testing frameworks like bitcoin-core regtest or btcd.

Q: Where can I get help?

A: Check the GitHub repository for documentation, or open an issue if you encounter problems. For research questions, refer to the Bithoven paper.

10. Glossary

Bitcoin Script
A stack-based programming language used to define spending conditions for Bitcoin transactions.
UTXO
Unspent Transaction Output - a chunk of bitcoin that can be spent by satisfying its script conditions.
Witness
The data provided to satisfy a script's spending conditions (signatures, preimages, etc.).
SegWit
Segregated Witness - a Bitcoin upgrade separating witness data from transaction data for efficiency.
Taproot
Bitcoin's 2021 upgrade enabling more efficient and private smart contracts via Schnorr signatures and MAST.
HTLC
Hash Time-Locked Contract - combines hash locks and timelocks for conditional payments (used in Lightning).
Multisig
Multi-signature - requires multiple signatures to spend (e.g., 2-of-3 means any 2 of 3 parties can spend).
CLTV / CSV
CheckLockTimeVerify (absolute) / CheckSequenceVerify (relative) - opcodes for timelocks.
Atomic Swap
Trustless cross-chain exchange using matching hash locks on different blockchains.
Opcode
Operation code - a single instruction in Bitcoin Script (e.g., OP_CHECKSIG, OP_IF).
Preimage
The secret data that, when hashed, produces a specific hash digest used in hash locks.
Spending Path
A distinct execution branch in a contract with its own witness requirements.
Stack Declaration
The typed list of witness items one spending path consumes, written top of stack first.
Signature Necessity
Bithoven's core security property: no spending path can succeed unless a signature check on it returns true.