commit 8af91e37d272af25626b1f02b2cdf55456d4db36
parent 98e8f9cc3d5c4883e91eedeae380654d6a53b8f3
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 25 Jan 2026 10:52:43 +0400
Milestone 0: Add module stubs and core Types
Create project structure with module stubs:
- Lightning.Protocol.BOLT3.Types: core newtypes and ADTs
- Lightning.Protocol.BOLT3.Keys: key derivation (stub)
- Lightning.Protocol.BOLT3.Scripts: script templates (stub)
- Lightning.Protocol.BOLT3.Tx: transaction assembly (stub)
- Lightning.Protocol.BOLT3.Encode: serialization (stub)
- Lightning.Protocol.BOLT3.Decode: parsing (stub)
- Lightning.Protocol.BOLT3.Validate: validation (stub)
Types module includes:
- Monetary types (Satoshi, MilliSatoshi)
- Key types (Pubkey, Seckey, Point, various basepoints)
- Transaction primitives (TxId, Outpoint, Sequence, Locktime)
- Channel parameters (CommitmentNumber, ToSelfDelay, etc.)
- HTLC types with BOLT #3 ordering
- Script/Witness newtypes
- Weight and dust constants from spec
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat:
10 files changed, 714 insertions(+), 1 deletion(-)
diff --git a/flake.lock b/flake.lock
@@ -0,0 +1,88 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1766840161,
+ "narHash": "sha256-Ss/LHpJJsng8vz1Pe33RSGIWUOcqM1fjrehjUkdrWio=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "3edc4a30ed3903fdf6f90c837f961fa6b49582d1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "ppad-nixpkgs": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1766932084,
+ "narHash": "sha256-GvVsbTfW+B7IQ9K/QP2xcXJAm1lhBin1jYZWNjOzT+o=",
+ "ref": "master",
+ "rev": "353e61763b959b960a55321a85423501e3e9ed7a",
+ "revCount": 2,
+ "type": "git",
+ "url": "git://git.ppad.tech/nixpkgs.git"
+ },
+ "original": {
+ "ref": "master",
+ "type": "git",
+ "url": "git://git.ppad.tech/nixpkgs.git"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": [
+ "ppad-nixpkgs",
+ "flake-utils"
+ ],
+ "nixpkgs": [
+ "ppad-nixpkgs",
+ "nixpkgs"
+ ],
+ "ppad-nixpkgs": "ppad-nixpkgs"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/lib/Lightning/Protocol/BOLT3.hs b/lib/Lightning/Protocol/BOLT3.hs
@@ -1,5 +1,4 @@
{-# OPTIONS_HADDOCK prune #-}
-{-# LANGUAGE BangPatterns #-}
-- |
-- Module: Lightning.Protocol.BOLT3
@@ -9,7 +8,20 @@
--
-- Bitcoin transaction formats for the Lightning Network, per
-- [BOLT #3](https://github.com/lightning/bolts/blob/master/03-transactions.md).
+--
+-- This module re-exports the public API from submodules:
+--
+-- * "Lightning.Protocol.BOLT3.Types" - Core types
+-- * "Lightning.Protocol.BOLT3.Keys" - Key derivation
+-- * "Lightning.Protocol.BOLT3.Scripts" - Script templates
+-- * "Lightning.Protocol.BOLT3.Tx" - Transaction assembly
+-- * "Lightning.Protocol.BOLT3.Encode" - Serialization
+-- * "Lightning.Protocol.BOLT3.Decode" - Parsing
+-- * "Lightning.Protocol.BOLT3.Validate" - Validation
module Lightning.Protocol.BOLT3 (
+ -- * Re-exports
+ module Lightning.Protocol.BOLT3.Types
) where
+import Lightning.Protocol.BOLT3.Types
diff --git a/lib/Lightning/Protocol/BOLT3/Decode.hs b/lib/Lightning/Protocol/BOLT3/Decode.hs
@@ -0,0 +1,26 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Decode
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Parsing for BOLT #3 transactions and scripts.
+
+module Lightning.Protocol.BOLT3.Decode (
+ -- * Transaction parsing
+ -- decode_tx
+
+ -- * Script parsing
+ -- , decode_script
+ -- , decode_witness
+
+ -- * Primitive decoding
+ -- , decode_varint
+ -- , decode_le32
+ -- , decode_le64
+ ) where
+
+import Lightning.Protocol.BOLT3.Types
diff --git a/lib/Lightning/Protocol/BOLT3/Encode.hs b/lib/Lightning/Protocol/BOLT3/Encode.hs
@@ -0,0 +1,27 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Encode
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Serialization for BOLT #3 transactions and scripts.
+
+module Lightning.Protocol.BOLT3.Encode (
+ -- * Transaction serialization
+ -- encode_tx
+ -- , encode_tx_for_signing
+
+ -- * Script serialization
+ -- , encode_script
+ -- , encode_witness
+
+ -- * Primitive encoding
+ -- , encode_varint
+ -- , encode_le32
+ -- , encode_le64
+ ) where
+
+import Lightning.Protocol.BOLT3.Types
diff --git a/lib/Lightning/Protocol/BOLT3/Keys.hs b/lib/Lightning/Protocol/BOLT3/Keys.hs
@@ -0,0 +1,51 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Keys
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Per-commitment key derivation per BOLT #3.
+--
+-- Implements key derivation formulas:
+--
+-- @
+-- pubkey = basepoint + SHA256(per_commitment_point || basepoint) * G
+-- revocationpubkey = revocation_basepoint * SHA256(revocation_basepoint || per_commitment_point)
+-- + per_commitment_point * SHA256(per_commitment_point || revocation_basepoint)
+-- @
+
+module Lightning.Protocol.BOLT3.Keys (
+ -- * Per-commitment point derivation
+ -- derive_per_commitment_point
+
+ -- * Key derivation
+ -- , derive_pubkey
+ -- , derive_localpubkey
+ -- , derive_local_htlcpubkey
+ -- , derive_remote_htlcpubkey
+ -- , derive_local_delayedpubkey
+ -- , derive_remote_delayedpubkey
+
+ -- * Revocation key derivation
+ -- , derive_revocationpubkey
+ -- , derive_revocationprivkey
+
+ -- * Per-commitment secret generation
+ -- , generate_from_seed
+ -- , derive_secret
+
+ -- * Per-commitment secret storage
+ -- , SecretStore
+ -- , empty_store
+ -- , insert_secret
+ -- , derive_old_secret
+
+ -- * Commitment number obscuring
+ -- , obscured_commitment_number
+ -- , commitment_number_from_locktime_sequence
+ ) where
+
+import Lightning.Protocol.BOLT3.Types
diff --git a/lib/Lightning/Protocol/BOLT3/Scripts.hs b/lib/Lightning/Protocol/BOLT3/Scripts.hs
@@ -0,0 +1,61 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Scripts
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Script templates for BOLT #3 transaction outputs.
+--
+-- Includes witness scripts for:
+--
+-- * Funding output (2-of-2 multisig)
+-- * to_local output (revocable with CSV delay)
+-- * to_remote output (P2WPKH or anchored)
+-- * Anchor outputs
+-- * Offered HTLC outputs
+-- * Received HTLC outputs
+-- * HTLC-timeout/success output (revocable with delay)
+
+module Lightning.Protocol.BOLT3.Scripts (
+ -- * Funding output
+ -- funding_script
+ -- , funding_witness
+
+ -- * to_local output
+ -- , to_local_script
+ -- , to_local_witness_spend
+ -- , to_local_witness_revoke
+
+ -- * to_remote output
+ -- , to_remote_script
+ -- , to_remote_witness
+
+ -- * Anchor outputs
+ -- , anchor_script
+ -- , anchor_witness_owner
+ -- , anchor_witness_anyone
+
+ -- * Offered HTLC output
+ -- , offered_htlc_script
+ -- , offered_htlc_witness_preimage
+ -- , offered_htlc_witness_revoke
+
+ -- * Received HTLC output
+ -- , received_htlc_script
+ -- , received_htlc_witness_timeout
+ -- , received_htlc_witness_revoke
+
+ -- * HTLC-timeout/success output (same as to_local)
+ -- , htlc_output_script
+ -- , htlc_output_witness_spend
+ -- , htlc_output_witness_revoke
+
+ -- * P2WSH helpers
+ -- , to_p2wsh
+ -- , witness_script_hash
+ ) where
+
+import Lightning.Protocol.BOLT3.Types
diff --git a/lib/Lightning/Protocol/BOLT3/Tx.hs b/lib/Lightning/Protocol/BOLT3/Tx.hs
@@ -0,0 +1,48 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Tx
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Transaction assembly for BOLT #3.
+--
+-- Constructs:
+--
+-- * Commitment transactions
+-- * HTLC-timeout transactions
+-- * HTLC-success transactions
+-- * Closing transactions
+
+module Lightning.Protocol.BOLT3.Tx (
+ -- * Commitment transaction
+ -- CommitmentTx(..)
+ -- , build_commitment_tx
+
+ -- * HTLC transactions
+ -- , HTLCTx(..)
+ -- , build_htlc_timeout_tx
+ -- , build_htlc_success_tx
+
+ -- * Closing transaction
+ -- , ClosingTx(..)
+ -- , build_closing_tx
+ -- , build_legacy_closing_tx
+
+ -- * Fee calculation
+ -- , commitment_fee
+ -- , htlc_timeout_fee
+ -- , htlc_success_fee
+
+ -- * Trimming
+ -- , is_trimmed
+ -- , trimmed_htlcs
+ -- , untrimmed_htlcs
+
+ -- * Output ordering
+ -- , sort_outputs
+ ) where
+
+import Lightning.Protocol.BOLT3.Types
diff --git a/lib/Lightning/Protocol/BOLT3/Types.hs b/lib/Lightning/Protocol/BOLT3/Types.hs
@@ -0,0 +1,363 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Types
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Core types for BOLT #3 transaction and script formats.
+
+module Lightning.Protocol.BOLT3.Types (
+ -- * Monetary amounts
+ Satoshi(..)
+ , MilliSatoshi(..)
+ , msat_to_sat
+ , sat_to_msat
+
+ -- * Keys and points
+ , Pubkey(..)
+ , Seckey(..)
+ , Point(..)
+
+ -- * Hashes
+ , PaymentHash(..)
+ , PaymentPreimage(..)
+
+ -- * Transaction primitives
+ , TxId(..)
+ , Outpoint(..)
+ , Sequence(..)
+ , Locktime(..)
+
+ -- * Channel parameters
+ , CommitmentNumber(..)
+ , ToSelfDelay(..)
+ , CltvExpiry(..)
+ , DustLimit(..)
+ , FeeratePerKw(..)
+
+ -- * HTLC types
+ , HTLC(..)
+ , HTLCDirection(..)
+
+ -- * Basepoints
+ , Basepoints(..)
+ , PerCommitmentPoint(..)
+ , PerCommitmentSecret(..)
+ , RevocationBasepoint(..)
+ , PaymentBasepoint(..)
+ , DelayedPaymentBasepoint(..)
+ , HtlcBasepoint(..)
+
+ -- * Derived keys
+ , LocalPubkey(..)
+ , RemotePubkey(..)
+ , LocalDelayedPubkey(..)
+ , RemoteDelayedPubkey(..)
+ , LocalHtlcPubkey(..)
+ , RemoteHtlcPubkey(..)
+ , RevocationPubkey(..)
+ , FundingPubkey(..)
+
+ -- * Script and witness
+ , Script(..)
+ , Witness(..)
+
+ -- * Channel options
+ , ChannelFeatures(..)
+ , has_anchors
+
+ -- * Transaction weights (constants)
+ , commitment_weight_no_anchors
+ , commitment_weight_anchors
+ , htlc_timeout_weight_no_anchors
+ , htlc_timeout_weight_anchors
+ , htlc_success_weight_no_anchors
+ , htlc_success_weight_anchors
+ , htlc_output_weight
+
+ -- * Dust thresholds (constants)
+ , dust_p2pkh
+ , dust_p2sh
+ , dust_p2wpkh
+ , dust_p2wsh
+ , anchor_output_value
+ ) where
+
+import Data.Bits ((.&.))
+import Data.Word (Word16, Word32, Word64)
+import qualified Data.ByteString as BS
+import GHC.Generics (Generic)
+
+-- monetary amounts ------------------------------------------------------------
+
+-- | Amount in satoshis.
+newtype Satoshi = Satoshi { unSatoshi :: Word64 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- | Amount in millisatoshis.
+newtype MilliSatoshi = MilliSatoshi { unMilliSatoshi :: Word64 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- | Convert millisatoshis to satoshis (rounds down).
+msat_to_sat :: MilliSatoshi -> Satoshi
+msat_to_sat (MilliSatoshi m) = Satoshi (m `div` 1000)
+{-# INLINE msat_to_sat #-}
+
+-- | Convert satoshis to millisatoshis.
+sat_to_msat :: Satoshi -> MilliSatoshi
+sat_to_msat (Satoshi s) = MilliSatoshi (s * 1000)
+{-# INLINE sat_to_msat #-}
+
+-- keys and points -------------------------------------------------------------
+
+-- | Compressed public key (33 bytes).
+newtype Pubkey = Pubkey { unPubkey :: BS.ByteString }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Secret key (32 bytes).
+newtype Seckey = Seckey { unSeckey :: BS.ByteString }
+ deriving (Eq, Generic)
+
+-- Don't show secret keys
+instance Show Seckey where
+ show _ = "Seckey <redacted>"
+
+-- | Elliptic curve point (33-byte compressed form).
+newtype Point = Point { unPoint :: BS.ByteString }
+ deriving (Eq, Ord, Show, Generic)
+
+-- hashes ----------------------------------------------------------------------
+
+-- | Payment hash (32 bytes, SHA256 of preimage).
+newtype PaymentHash = PaymentHash { unPaymentHash :: BS.ByteString }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Payment preimage (32 bytes).
+newtype PaymentPreimage = PaymentPreimage { unPaymentPreimage :: BS.ByteString }
+ deriving (Eq, Generic)
+
+instance Show PaymentPreimage where
+ show _ = "PaymentPreimage <redacted>"
+
+-- transaction primitives ------------------------------------------------------
+
+-- | Transaction ID (32 bytes, little-endian hash).
+newtype TxId = TxId { unTxId :: BS.ByteString }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Transaction outpoint (txid + output index).
+data Outpoint = Outpoint
+ { outpoint_txid :: {-# UNPACK #-} !TxId
+ , outpoint_index :: {-# UNPACK #-} !Word32
+ } deriving (Eq, Ord, Show, Generic)
+
+-- | Transaction input sequence number.
+newtype Sequence = Sequence { unSequence :: Word32 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- | Transaction locktime.
+newtype Locktime = Locktime { unLocktime :: Word32 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- channel parameters ----------------------------------------------------------
+
+-- | 48-bit commitment number.
+newtype CommitmentNumber = CommitmentNumber { unCommitmentNumber :: Word64 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- | CSV delay for to_local outputs.
+newtype ToSelfDelay = ToSelfDelay { unToSelfDelay :: Word16 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- | CLTV expiry for HTLCs.
+newtype CltvExpiry = CltvExpiry { unCltvExpiry :: Word32 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- | Dust limit threshold.
+newtype DustLimit = DustLimit { unDustLimit :: Satoshi }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Fee rate in satoshis per 1000 weight units.
+newtype FeeratePerKw = FeeratePerKw { unFeeratePerKw :: Word32 }
+ deriving (Eq, Ord, Show, Generic, Num)
+
+-- HTLC types ------------------------------------------------------------------
+
+-- | Direction of an HTLC from the commitment tx owner's perspective.
+data HTLCDirection
+ = HTLCOffered -- ^ We offered this HTLC (outgoing)
+ | HTLCReceived -- ^ We received this HTLC (incoming)
+ deriving (Eq, Ord, Show, Generic)
+
+-- | HTLC output details.
+data HTLC = HTLC
+ { htlc_direction :: !HTLCDirection
+ , htlc_amount_msat :: {-# UNPACK #-} !MilliSatoshi
+ , htlc_payment_hash :: {-# UNPACK #-} !PaymentHash
+ , htlc_cltv_expiry :: {-# UNPACK #-} !CltvExpiry
+ } deriving (Eq, Show, Generic)
+
+-- Define ordering per BOLT #3: by amount (sat), then scriptpubkey, then expiry
+instance Ord HTLC where
+ compare h1 h2 =
+ let sat1 = msat_to_sat (htlc_amount_msat h1)
+ sat2 = msat_to_sat (htlc_amount_msat h2)
+ in case compare sat1 sat2 of
+ EQ -> compare (htlc_cltv_expiry h1) (htlc_cltv_expiry h2)
+ other -> other
+
+-- basepoints ------------------------------------------------------------------
+
+-- | Per-commitment point (used to derive keys).
+newtype PerCommitmentPoint = PerCommitmentPoint { unPerCommitmentPoint :: Point }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Per-commitment secret (32 bytes).
+newtype PerCommitmentSecret = PerCommitmentSecret
+ { unPerCommitmentSecret :: BS.ByteString }
+ deriving (Eq, Generic)
+
+instance Show PerCommitmentSecret where
+ show _ = "PerCommitmentSecret <redacted>"
+
+-- | Revocation basepoint.
+newtype RevocationBasepoint = RevocationBasepoint
+ { unRevocationBasepoint :: Point }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Payment basepoint.
+newtype PaymentBasepoint = PaymentBasepoint
+ { unPaymentBasepoint :: Point }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Delayed payment basepoint.
+newtype DelayedPaymentBasepoint = DelayedPaymentBasepoint
+ { unDelayedPaymentBasepoint :: Point }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | HTLC basepoint.
+newtype HtlcBasepoint = HtlcBasepoint { unHtlcBasepoint :: Point }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Collection of all basepoints for one party.
+data Basepoints = Basepoints
+ { bp_revocation :: !RevocationBasepoint
+ , bp_payment :: !PaymentBasepoint
+ , bp_delayed_payment :: !DelayedPaymentBasepoint
+ , bp_htlc :: !HtlcBasepoint
+ } deriving (Eq, Show, Generic)
+
+-- derived keys ----------------------------------------------------------------
+
+-- | Local pubkey (derived from payment_basepoint + per_commitment_point).
+newtype LocalPubkey = LocalPubkey { unLocalPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Remote pubkey (simply the remote's payment_basepoint).
+newtype RemotePubkey = RemotePubkey { unRemotePubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Local delayed pubkey.
+newtype LocalDelayedPubkey = LocalDelayedPubkey
+ { unLocalDelayedPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Remote delayed pubkey.
+newtype RemoteDelayedPubkey = RemoteDelayedPubkey
+ { unRemoteDelayedPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Local HTLC pubkey.
+newtype LocalHtlcPubkey = LocalHtlcPubkey { unLocalHtlcPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Remote HTLC pubkey.
+newtype RemoteHtlcPubkey = RemoteHtlcPubkey { unRemoteHtlcPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Revocation pubkey (derived from revocation_basepoint + per_commitment).
+newtype RevocationPubkey = RevocationPubkey { unRevocationPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Funding pubkey (used in 2-of-2 multisig).
+newtype FundingPubkey = FundingPubkey { unFundingPubkey :: Pubkey }
+ deriving (Eq, Ord, Show, Generic)
+
+-- script and witness ----------------------------------------------------------
+
+-- | Bitcoin script (serialized).
+newtype Script = Script { unScript :: BS.ByteString }
+ deriving (Eq, Ord, Show, Generic)
+
+-- | Transaction witness stack.
+newtype Witness = Witness { unWitness :: [BS.ByteString] }
+ deriving (Eq, Ord, Show, Generic)
+
+-- channel options -------------------------------------------------------------
+
+-- | Channel feature flags relevant to BOLT #3.
+data ChannelFeatures = ChannelFeatures
+ { cf_option_anchors :: !Bool
+ } deriving (Eq, Show, Generic)
+
+-- | Check if option_anchors is enabled.
+has_anchors :: ChannelFeatures -> Bool
+has_anchors = cf_option_anchors
+{-# INLINE has_anchors #-}
+
+-- transaction weights (constants from spec) -----------------------------------
+
+-- | Base commitment tx weight without option_anchors.
+commitment_weight_no_anchors :: Word64
+commitment_weight_no_anchors = 724
+
+-- | Base commitment tx weight with option_anchors.
+commitment_weight_anchors :: Word64
+commitment_weight_anchors = 1124
+
+-- | HTLC-timeout tx weight without option_anchors.
+htlc_timeout_weight_no_anchors :: Word64
+htlc_timeout_weight_no_anchors = 663
+
+-- | HTLC-timeout tx weight with option_anchors.
+htlc_timeout_weight_anchors :: Word64
+htlc_timeout_weight_anchors = 666
+
+-- | HTLC-success tx weight without option_anchors.
+htlc_success_weight_no_anchors :: Word64
+htlc_success_weight_no_anchors = 703
+
+-- | HTLC-success tx weight with option_anchors.
+htlc_success_weight_anchors :: Word64
+htlc_success_weight_anchors = 706
+
+-- | Weight added per HTLC output in commitment tx.
+htlc_output_weight :: Word64
+htlc_output_weight = 172
+
+-- dust thresholds (constants from Bitcoin Core) -------------------------------
+
+-- | P2PKH dust threshold (546 satoshis).
+dust_p2pkh :: Satoshi
+dust_p2pkh = Satoshi 546
+
+-- | P2SH dust threshold (540 satoshis).
+dust_p2sh :: Satoshi
+dust_p2sh = Satoshi 540
+
+-- | P2WPKH dust threshold (294 satoshis).
+dust_p2wpkh :: Satoshi
+dust_p2wpkh = Satoshi 294
+
+-- | P2WSH dust threshold (330 satoshis).
+dust_p2wsh :: Satoshi
+dust_p2wsh = Satoshi 330
+
+-- | Fixed anchor output value (330 satoshis).
+anchor_output_value :: Satoshi
+anchor_output_value = Satoshi 330
diff --git a/lib/Lightning/Protocol/BOLT3/Validate.hs b/lib/Lightning/Protocol/BOLT3/Validate.hs
@@ -0,0 +1,30 @@
+{-# OPTIONS_HADDOCK prune #-}
+{-# LANGUAGE BangPatterns #-}
+
+-- |
+-- Module: Lightning.Protocol.BOLT3.Validate
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Stateless validation for BOLT #3 transactions.
+
+module Lightning.Protocol.BOLT3.Validate (
+ -- * Validation errors
+ -- ValidationError(..)
+
+ -- * Commitment transaction validation
+ -- , validate_commitment_tx
+
+ -- * HTLC transaction validation
+ -- , validate_htlc_tx
+
+ -- * Closing transaction validation
+ -- , validate_closing_tx
+
+ -- * Output validation
+ -- , validate_output_ordering
+ -- , validate_dust_limits
+ ) where
+
+import Lightning.Protocol.BOLT3.Types
diff --git a/ppad-bolt3.cabal b/ppad-bolt3.cabal
@@ -25,6 +25,13 @@ library
-Wall
exposed-modules:
Lightning.Protocol.BOLT3
+ Lightning.Protocol.BOLT3.Decode
+ Lightning.Protocol.BOLT3.Encode
+ Lightning.Protocol.BOLT3.Keys
+ Lightning.Protocol.BOLT3.Scripts
+ Lightning.Protocol.BOLT3.Tx
+ Lightning.Protocol.BOLT3.Types
+ Lightning.Protocol.BOLT3.Validate
build-depends:
base >= 4.9 && < 5
, bytestring >= 0.9 && < 0.13