bolt3

Lightning transaction and script formats, per BOLT #3 (docs.ppad.tech/bolt3).
git clone git://git.ppad.tech/bolt3.git
Log | Files | Refs | README | LICENSE

commit 7686c616c81bcb0a8747ce4c49c3d00e478a51e6
parent 4bbc2f83e4556a9583bccdd90147daabd14fdd56
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 18 Apr 2026 20:17:56 +0800

lib: use ppad-tx types for TxId, OutPoint, Witness

Replace bolt3-local TxId, Outpoint, and Witness definitions
with re-exports from Bitcoin.Prim.Tx (ppad-tx). Update all
internal modules for renamed types and field accessors:

- Outpoint -> OutPoint
- outpoint_txid -> op_txid
- outpoint_index -> op_vout
- txid (smart ctor) -> mkTxId
- unTxId field access -> TxId pattern match
- Witness no longer has unWitness accessor (unused)

Diffstat:
Mlib/Lightning/Protocol/BOLT3.hs | 4++--
Mlib/Lightning/Protocol/BOLT3/Decode.hs | 14+++++++-------
Mlib/Lightning/Protocol/BOLT3/Encode.hs | 9+++++----
Mlib/Lightning/Protocol/BOLT3/Tx.hs | 12++++++------
Mlib/Lightning/Protocol/BOLT3/Types.hs | 34+++++++---------------------------
5 files changed, 27 insertions(+), 46 deletions(-)

diff --git a/lib/Lightning/Protocol/BOLT3.hs b/lib/Lightning/Protocol/BOLT3.hs @@ -77,8 +77,8 @@ module Lightning.Protocol.BOLT3 ( -- ** Transaction primitives , TxId(..) - , txid - , Outpoint(..) + , mkTxId + , OutPoint(..) , Sequence(..) , Locktime(..) diff --git a/lib/Lightning/Protocol/BOLT3/Decode.hs b/lib/Lightning/Protocol/BOLT3/Decode.hs @@ -61,7 +61,7 @@ data DecodeError -- | A raw transaction input as parsed from bytes. data RawInput = RawInput - { ri_outpoint :: !Outpoint + { ri_outpoint :: !OutPoint , ri_script_sig :: !BS.ByteString , ri_sequence :: !Sequence } deriving (Eq, Show, Generic) @@ -184,22 +184,22 @@ decode_varint_32 !bs -- -- Format: 32 bytes txid (little-endian) + 4 bytes index (little-endian) -- --- >>> let txid = BS.replicate 32 0 +-- >>> let tid = BS.replicate 32 0 -- >>> let idx = BS.pack [0x01, 0x00, 0x00, 0x00] --- >>> decode_outpoint (txid <> idx) --- Right (Outpoint {outpoint_txid = ..., outpoint_index = 1}, "") +-- >>> decode_outpoint (tid <> idx) +-- Right (OutPoint {op_txid = ..., op_vout = 1}, "") decode_outpoint :: BS.ByteString - -> Either DecodeError (Outpoint, BS.ByteString) + -> Either DecodeError (OutPoint, BS.ByteString) decode_outpoint !bs | BS.length bs < 36 = Left (InsufficientBytes 36 (BS.length bs)) | otherwise = - let !txid = TxId (BS.take 32 bs) + let !tid = TxId (BS.take 32 bs) !rest1 = BS.drop 32 bs in case decode_le32 rest1 of Left err -> Left err Right (!idx, !rest2) -> - let !outpoint = Outpoint txid idx + let !outpoint = OutPoint tid idx in Right (outpoint, rest2) {-# INLINE decode_outpoint #-} diff --git a/lib/Lightning/Protocol/BOLT3/Encode.hs b/lib/Lightning/Protocol/BOLT3/Encode.hs @@ -95,12 +95,13 @@ encode_varint !n -- -- Format: 32 bytes txid (already LE in TxId) + 4 bytes output index LE -- --- >>> encode_outpoint (Outpoint txid 0) +-- >>> encode_outpoint (OutPoint txid 0) -- <32-byte txid><4-byte index> -encode_outpoint :: Outpoint -> BS.ByteString +encode_outpoint :: OutPoint -> BS.ByteString encode_outpoint !op = BSL.toStrict $ BSB.toLazyByteString $ - BSB.byteString (unTxId $ outpoint_txid op) <> - BSB.word32LE (outpoint_index op) + let !(TxId bs) = op_txid op + in BSB.byteString bs <> + BSB.word32LE (op_vout op) {-# INLINE encode_outpoint #-} -- | Encode a transaction output. diff --git a/lib/Lightning/Protocol/BOLT3/Tx.hs b/lib/Lightning/Protocol/BOLT3/Tx.hs @@ -99,7 +99,7 @@ data CommitmentKeys = CommitmentKeys -- | Context for building a commitment transaction. data CommitmentContext = CommitmentContext - { cc_funding_outpoint :: !Outpoint + { cc_funding_outpoint :: !OutPoint , cc_commitment_number :: !CommitmentNumber , cc_local_payment_bp :: !PaymentBasepoint , cc_remote_payment_bp :: !PaymentBasepoint @@ -118,7 +118,7 @@ data CommitmentContext = CommitmentContext data CommitmentTx = CommitmentTx { ctx_version :: {-# UNPACK #-} !Word32 , ctx_locktime :: !Locktime - , ctx_input_outpoint :: !Outpoint + , ctx_input_outpoint :: !OutPoint , ctx_input_sequence :: !Sequence , ctx_outputs :: ![TxOutput] , ctx_funding_script :: !Script @@ -305,7 +305,7 @@ data HTLCContext = HTLCContext data HTLCTx = HTLCTx { htx_version :: {-# UNPACK #-} !Word32 , htx_locktime :: !Locktime - , htx_input_outpoint :: !Outpoint + , htx_input_outpoint :: !OutPoint , htx_input_sequence :: !Sequence , htx_output_value :: !Satoshi , htx_output_script :: !Script @@ -328,7 +328,7 @@ build_htlc_tx_common ctx locktime fee = !inputSeq = if has_anchors (hc_features ctx) then Sequence 1 else Sequence 0 - !outpoint = Outpoint (hc_commitment_txid ctx) (hc_output_index ctx) + !outpoint = OutPoint (hc_commitment_txid ctx) (hc_output_index ctx) !outputScript = to_p2wsh $ htlc_output_script (hc_revocation_pubkey ctx) (hc_to_self_delay ctx) @@ -370,7 +370,7 @@ build_htlc_success_tx ctx = -- | Context for building closing transactions. data ClosingContext = ClosingContext - { clc_funding_outpoint :: !Outpoint + { clc_funding_outpoint :: !OutPoint , clc_local_amount :: !Satoshi , clc_remote_amount :: !Satoshi , clc_local_script :: !Script @@ -387,7 +387,7 @@ data ClosingContext = ClosingContext data ClosingTx = ClosingTx { cltx_version :: {-# UNPACK #-} !Word32 , cltx_locktime :: !Locktime - , cltx_input_outpoint :: !Outpoint + , cltx_input_outpoint :: !OutPoint , cltx_input_sequence :: !Sequence , cltx_outputs :: ![TxOutput] , cltx_funding_script :: !Script diff --git a/lib/Lightning/Protocol/BOLT3/Types.hs b/lib/Lightning/Protocol/BOLT3/Types.hs @@ -34,8 +34,8 @@ module Lightning.Protocol.BOLT3.Types ( -- * Transaction primitives , TxId(..) - , txid - , Outpoint(..) + , mkTxId + , OutPoint(..) , Sequence(..) , Locktime(..) @@ -71,8 +71,10 @@ module Lightning.Protocol.BOLT3.Types ( , RevocationPubkey(..) , FundingPubkey(..) - -- * Script and witness + -- * Script , Script(..) + + -- * Witness (re-exported from ppad-tx) , Witness(..) -- * Channel options @@ -96,6 +98,7 @@ module Lightning.Protocol.BOLT3.Types ( , anchor_output_value ) where +import Bitcoin.Prim.Tx (TxId(..), mkTxId, OutPoint(..), Witness(..)) import Data.Word (Word16, Word32, Word64) import qualified Data.ByteString as BS import GHC.Generics (Generic) @@ -203,25 +206,6 @@ payment_preimage bs -- transaction primitives ------------------------------------------------------ --- | Transaction ID (32 bytes, little-endian hash). -newtype TxId = TxId { unTxId :: BS.ByteString } - deriving (Eq, Ord, Show, Generic) - --- | Parse a 32-byte transaction ID. --- --- Returns Nothing if the input is not exactly 32 bytes. -txid :: BS.ByteString -> Maybe TxId -txid bs - | BS.length bs == 32 = Just (TxId bs) - | otherwise = Nothing -{-# INLINE txid #-} - --- | 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) @@ -368,16 +352,12 @@ newtype RevocationPubkey = RevocationPubkey { unRevocationPubkey :: Pubkey } newtype FundingPubkey = FundingPubkey { unFundingPubkey :: Pubkey } deriving (Eq, Ord, Show, Generic) --- script and witness ---------------------------------------------------------- +-- script ---------------------------------------------------------------------- -- | 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.