{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}

-- |
-- 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.
--
-- Decodes SegWit Bitcoin transactions from raw bytes.

module Lightning.Protocol.BOLT3.Decode (
    -- * Error types
    DecodeError(..)

    -- * Raw transaction type
  , RawTx(..)
  , RawInput(..)
  , RawOutput(..)

    -- * Transaction parsing
  , decode_tx

    -- * Witness parsing
  , decode_witness

    -- * Primitive decoding
  , decode_varint
  , decode_le32
  , decode_le64
  , decode_outpoint
  , decode_output
  ) where

import Data.Bits ((.|.), shiftL)
import Data.Word (Word8, Word32, Word64)
import qualified Data.ByteString as BS
import GHC.Generics (Generic)
import Lightning.Protocol.BOLT3.Types

-- error types -----------------------------------------------------------------

-- | Errors that can occur during transaction decoding.
data DecodeError
  = InsufficientBytes !Int !Int
    -- ^ Expected bytes, actual bytes available
  | InvalidMarker !Word8
    -- ^ Invalid SegWit marker byte (expected 0x00)
  | InvalidFlag !Word8
    -- ^ Invalid SegWit flag byte (expected 0x01)
  | InvalidVarint
    -- ^ Malformed varint encoding
  | EmptyInput
    -- ^ No bytes to decode
  deriving (DecodeError -> DecodeError -> Bool
(DecodeError -> DecodeError -> Bool)
-> (DecodeError -> DecodeError -> Bool) -> Eq DecodeError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DecodeError -> DecodeError -> Bool
== :: DecodeError -> DecodeError -> Bool
$c/= :: DecodeError -> DecodeError -> Bool
/= :: DecodeError -> DecodeError -> Bool
Eq, Int -> DecodeError -> ShowS
[DecodeError] -> ShowS
DecodeError -> String
(Int -> DecodeError -> ShowS)
-> (DecodeError -> String)
-> ([DecodeError] -> ShowS)
-> Show DecodeError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DecodeError -> ShowS
showsPrec :: Int -> DecodeError -> ShowS
$cshow :: DecodeError -> String
show :: DecodeError -> String
$cshowList :: [DecodeError] -> ShowS
showList :: [DecodeError] -> ShowS
Show, (forall x. DecodeError -> Rep DecodeError x)
-> (forall x. Rep DecodeError x -> DecodeError)
-> Generic DecodeError
forall x. Rep DecodeError x -> DecodeError
forall x. DecodeError -> Rep DecodeError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. DecodeError -> Rep DecodeError x
from :: forall x. DecodeError -> Rep DecodeError x
$cto :: forall x. Rep DecodeError x -> DecodeError
to :: forall x. Rep DecodeError x -> DecodeError
Generic)

-- raw transaction types -------------------------------------------------------

-- | A raw transaction input as parsed from bytes.
data RawInput = RawInput
  { RawInput -> Outpoint
ri_outpoint   :: !Outpoint
  , RawInput -> ByteString
ri_script_sig :: !BS.ByteString
  , RawInput -> Sequence
ri_sequence   :: !Sequence
  } deriving (RawInput -> RawInput -> Bool
(RawInput -> RawInput -> Bool)
-> (RawInput -> RawInput -> Bool) -> Eq RawInput
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RawInput -> RawInput -> Bool
== :: RawInput -> RawInput -> Bool
$c/= :: RawInput -> RawInput -> Bool
/= :: RawInput -> RawInput -> Bool
Eq, Int -> RawInput -> ShowS
[RawInput] -> ShowS
RawInput -> String
(Int -> RawInput -> ShowS)
-> (RawInput -> String) -> ([RawInput] -> ShowS) -> Show RawInput
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RawInput -> ShowS
showsPrec :: Int -> RawInput -> ShowS
$cshow :: RawInput -> String
show :: RawInput -> String
$cshowList :: [RawInput] -> ShowS
showList :: [RawInput] -> ShowS
Show, (forall x. RawInput -> Rep RawInput x)
-> (forall x. Rep RawInput x -> RawInput) -> Generic RawInput
forall x. Rep RawInput x -> RawInput
forall x. RawInput -> Rep RawInput x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RawInput -> Rep RawInput x
from :: forall x. RawInput -> Rep RawInput x
$cto :: forall x. Rep RawInput x -> RawInput
to :: forall x. Rep RawInput x -> RawInput
Generic)

-- | A raw transaction output as parsed from bytes.
data RawOutput = RawOutput
  { RawOutput -> Satoshi
ro_value  :: !Satoshi
  , RawOutput -> Script
ro_script :: !Script
  } deriving (RawOutput -> RawOutput -> Bool
(RawOutput -> RawOutput -> Bool)
-> (RawOutput -> RawOutput -> Bool) -> Eq RawOutput
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RawOutput -> RawOutput -> Bool
== :: RawOutput -> RawOutput -> Bool
$c/= :: RawOutput -> RawOutput -> Bool
/= :: RawOutput -> RawOutput -> Bool
Eq, Int -> RawOutput -> ShowS
[RawOutput] -> ShowS
RawOutput -> String
(Int -> RawOutput -> ShowS)
-> (RawOutput -> String)
-> ([RawOutput] -> ShowS)
-> Show RawOutput
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RawOutput -> ShowS
showsPrec :: Int -> RawOutput -> ShowS
$cshow :: RawOutput -> String
show :: RawOutput -> String
$cshowList :: [RawOutput] -> ShowS
showList :: [RawOutput] -> ShowS
Show, (forall x. RawOutput -> Rep RawOutput x)
-> (forall x. Rep RawOutput x -> RawOutput) -> Generic RawOutput
forall x. Rep RawOutput x -> RawOutput
forall x. RawOutput -> Rep RawOutput x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RawOutput -> Rep RawOutput x
from :: forall x. RawOutput -> Rep RawOutput x
$cto :: forall x. Rep RawOutput x -> RawOutput
to :: forall x. Rep RawOutput x -> RawOutput
Generic)

-- | A raw transaction as parsed from bytes.
--
-- Supports both legacy and SegWit transaction formats.
data RawTx = RawTx
  { RawTx -> Word32
rtx_version  :: {-# UNPACK #-} !Word32
  , RawTx -> [RawInput]
rtx_inputs   :: ![RawInput]
  , RawTx -> [RawOutput]
rtx_outputs  :: ![RawOutput]
  , RawTx -> [[ByteString]]
rtx_witness  :: ![[BS.ByteString]]
    -- ^ Witness stack for each input (empty list for legacy tx)
  , RawTx -> Locktime
rtx_locktime :: !Locktime
  } deriving (RawTx -> RawTx -> Bool
(RawTx -> RawTx -> Bool) -> (RawTx -> RawTx -> Bool) -> Eq RawTx
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RawTx -> RawTx -> Bool
== :: RawTx -> RawTx -> Bool
$c/= :: RawTx -> RawTx -> Bool
/= :: RawTx -> RawTx -> Bool
Eq, Int -> RawTx -> ShowS
[RawTx] -> ShowS
RawTx -> String
(Int -> RawTx -> ShowS)
-> (RawTx -> String) -> ([RawTx] -> ShowS) -> Show RawTx
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RawTx -> ShowS
showsPrec :: Int -> RawTx -> ShowS
$cshow :: RawTx -> String
show :: RawTx -> String
$cshowList :: [RawTx] -> ShowS
showList :: [RawTx] -> ShowS
Show, (forall x. RawTx -> Rep RawTx x)
-> (forall x. Rep RawTx x -> RawTx) -> Generic RawTx
forall x. Rep RawTx x -> RawTx
forall x. RawTx -> Rep RawTx x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RawTx -> Rep RawTx x
from :: forall x. RawTx -> Rep RawTx x
$cto :: forall x. Rep RawTx x -> RawTx
to :: forall x. Rep RawTx x -> RawTx
Generic)

-- primitive decoding ----------------------------------------------------------

-- | Decode a little-endian 32-bit integer.
--
-- >>> decode_le32 (BS.pack [0x01, 0x00, 0x00, 0x00])
-- Right (1, "")
decode_le32 :: BS.ByteString -> Either DecodeError (Word32, BS.ByteString)
decode_le32 :: ByteString -> Either DecodeError (Word32, ByteString)
decode_le32 !ByteString
bs
  | ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
4 = DecodeError -> Either DecodeError (Word32, ByteString)
forall a b. a -> Either a b
Left (Int -> Int -> DecodeError
InsufficientBytes Int
4 (ByteString -> Int
BS.length ByteString
bs))
  | Bool
otherwise =
      let !b0 :: Word32
b0 = Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
0)
          !b1 :: Word32
b1 = Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
1)
          !b2 :: Word32
b2 = Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
2)
          !b3 :: Word32
b3 = Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
3)
          !val :: Word32
val = Word32
b0 Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word32
b1 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
8) Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word32
b2 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
16)
                     Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word32
b3 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
24)
          !rest :: ByteString
rest = Int -> ByteString -> ByteString
BS.drop Int
4 ByteString
bs
      in (Word32, ByteString) -> Either DecodeError (Word32, ByteString)
forall a b. b -> Either a b
Right (Word32
val, ByteString
rest)
{-# INLINE decode_le32 #-}

-- | Decode a little-endian 64-bit integer.
--
-- >>> decode_le64 (BS.pack [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
-- Right (1, "")
decode_le64 :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)
decode_le64 :: ByteString -> Either DecodeError (Word64, ByteString)
decode_le64 !ByteString
bs
  | ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
8 = DecodeError -> Either DecodeError (Word64, ByteString)
forall a b. a -> Either a b
Left (Int -> Int -> DecodeError
InsufficientBytes Int
8 (ByteString -> Int
BS.length ByteString
bs))
  | Bool
otherwise =
      let !b0 :: Word64
b0 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
0)
          !b1 :: Word64
b1 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
1)
          !b2 :: Word64
b2 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
2)
          !b3 :: Word64
b3 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
3)
          !b4 :: Word64
b4 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
4)
          !b5 :: Word64
b5 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
5)
          !b6 :: Word64
b6 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
6)
          !b7 :: Word64
b7 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
7)
          !val :: Word64
val = Word64
b0 Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b1 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
8) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b2 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
16)
                    Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b3 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
24) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b4 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
32)
                    Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b5 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
40) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b6 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
48)
                    Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b7 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
56)
          !rest :: ByteString
rest = Int -> ByteString -> ByteString
BS.drop Int
8 ByteString
bs
      in (Word64, ByteString) -> Either DecodeError (Word64, ByteString)
forall a b. b -> Either a b
Right (Word64
val, ByteString
rest)
{-# INLINE decode_le64 #-}

-- | Decode a Bitcoin varint (CompactSize).
--
-- Encoding:
-- * 0x00-0xFC: 1 byte
-- * 0xFD: 2 bytes little-endian follow
-- * 0xFE: 4 bytes little-endian follow
-- * 0xFF: 8 bytes little-endian follow
--
-- >>> decode_varint (BS.pack [0x01])
-- Right (1, "")
-- >>> decode_varint (BS.pack [0xfd, 0x00, 0x01])
-- Right (256, "")
decode_varint :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)
decode_varint :: ByteString -> Either DecodeError (Word64, ByteString)
decode_varint !ByteString
bs
  | ByteString -> Bool
BS.null ByteString
bs = DecodeError -> Either DecodeError (Word64, ByteString)
forall a b. a -> Either a b
Left DecodeError
EmptyInput
  | Bool
otherwise =
      let !first :: Word8
first = HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
0
          !rest :: ByteString
rest = Int -> ByteString -> ByteString
BS.drop Int
1 ByteString
bs
      in case Word8
first of
           Word8
0xFD -> ByteString -> Either DecodeError (Word64, ByteString)
decode_varint_16 ByteString
rest
           Word8
0xFE -> ByteString -> Either DecodeError (Word64, ByteString)
decode_varint_32 ByteString
rest
           Word8
0xFF -> ByteString -> Either DecodeError (Word64, ByteString)
decode_le64 ByteString
rest
           Word8
_    -> (Word64, ByteString) -> Either DecodeError (Word64, ByteString)
forall a b. b -> Either a b
Right (Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
first, ByteString
rest)
{-# INLINE decode_varint #-}

-- | Decode a 16-bit varint payload.
decode_varint_16 :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)
decode_varint_16 :: ByteString -> Either DecodeError (Word64, ByteString)
decode_varint_16 !ByteString
bs
  | ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2 = DecodeError -> Either DecodeError (Word64, ByteString)
forall a b. a -> Either a b
Left (Int -> Int -> DecodeError
InsufficientBytes Int
2 (ByteString -> Int
BS.length ByteString
bs))
  | Bool
otherwise =
      let !b0 :: Word64
b0 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
0) :: Word64
          !b1 :: Word64
b1 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
1) :: Word64
          !val :: Word64
val = Word64
b0 Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b1 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
8)
          !rest :: ByteString
rest = Int -> ByteString -> ByteString
BS.drop Int
2 ByteString
bs
      in (Word64, ByteString) -> Either DecodeError (Word64, ByteString)
forall a b. b -> Either a b
Right (Word64
val, ByteString
rest)
{-# INLINE decode_varint_16 #-}

-- | Decode a 32-bit varint payload.
decode_varint_32 :: BS.ByteString -> Either DecodeError (Word64, BS.ByteString)
decode_varint_32 :: ByteString -> Either DecodeError (Word64, ByteString)
decode_varint_32 !ByteString
bs
  | ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
4 = DecodeError -> Either DecodeError (Word64, ByteString)
forall a b. a -> Either a b
Left (Int -> Int -> DecodeError
InsufficientBytes Int
4 (ByteString -> Int
BS.length ByteString
bs))
  | Bool
otherwise =
      let !b0 :: Word64
b0 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
0) :: Word64
          !b1 :: Word64
b1 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
1) :: Word64
          !b2 :: Word64
b2 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
2) :: Word64
          !b3 :: Word64
b3 = Word8 -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
3) :: Word64
          !val :: Word64
val = Word64
b0 Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b1 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
8) Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b2 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
16)
                    Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. (Word64
b3 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`shiftL` Int
24)
          !rest :: ByteString
rest = Int -> ByteString -> ByteString
BS.drop Int
4 ByteString
bs
      in (Word64, ByteString) -> Either DecodeError (Word64, ByteString)
forall a b. b -> Either a b
Right (Word64
val, ByteString
rest)
{-# INLINE decode_varint_32 #-}

-- | Decode a transaction outpoint (txid + output index).
--
-- Format: 32 bytes txid (little-endian) + 4 bytes index (little-endian)
--
-- >>> let txid = 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
  :: BS.ByteString
  -> Either DecodeError (Outpoint, BS.ByteString)
decode_outpoint :: ByteString -> Either DecodeError (Outpoint, ByteString)
decode_outpoint !ByteString
bs
  | ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
36 = DecodeError -> Either DecodeError (Outpoint, ByteString)
forall a b. a -> Either a b
Left (Int -> Int -> DecodeError
InsufficientBytes Int
36 (ByteString -> Int
BS.length ByteString
bs))
  | Bool
otherwise =
      let !txid :: TxId
txid = ByteString -> TxId
TxId (Int -> ByteString -> ByteString
BS.take Int
32 ByteString
bs)
          !rest1 :: ByteString
rest1 = Int -> ByteString -> ByteString
BS.drop Int
32 ByteString
bs
      in case ByteString -> Either DecodeError (Word32, ByteString)
decode_le32 ByteString
rest1 of
           Left DecodeError
err -> DecodeError -> Either DecodeError (Outpoint, ByteString)
forall a b. a -> Either a b
Left DecodeError
err
           Right (!Word32
idx, !ByteString
rest2) ->
             let !outpoint :: Outpoint
outpoint = TxId -> Word32 -> Outpoint
Outpoint TxId
txid Word32
idx
             in (Outpoint, ByteString) -> Either DecodeError (Outpoint, ByteString)
forall a b. b -> Either a b
Right (Outpoint
outpoint, ByteString
rest2)
{-# INLINE decode_outpoint #-}

-- | Decode a transaction output (value + scriptPubKey).
--
-- Format: 8 bytes value (little-endian) + varint script length + script
decode_output :: BS.ByteString -> Either DecodeError (RawOutput, BS.ByteString)
decode_output :: ByteString -> Either DecodeError (RawOutput, ByteString)
decode_output !ByteString
bs = do
  (!value, !rest1) <- ByteString -> Either DecodeError (Word64, ByteString)
decode_le64 ByteString
bs
  (!scriptLen, !rest2) <- decode_varint rest1
  let !len = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
scriptLen
  if BS.length rest2 < len
    then Left (InsufficientBytes len (BS.length rest2))
    else
      let !script = ByteString -> Script
Script (Int -> ByteString -> ByteString
BS.take Int
len ByteString
rest2)
          !rest3 = Int -> ByteString -> ByteString
BS.drop Int
len ByteString
rest2
          !output = Satoshi -> Script -> RawOutput
RawOutput (Word64 -> Satoshi
Satoshi Word64
value) Script
script
      in Right (output, rest3)
{-# INLINE decode_output #-}

-- witness parsing -------------------------------------------------------------

-- | Decode a witness stack for one input.
--
-- Format: varint num_items + (varint length + data) for each item
decode_witness
  :: BS.ByteString
  -> Either DecodeError (Witness, BS.ByteString)
decode_witness :: ByteString -> Either DecodeError (Witness, ByteString)
decode_witness !ByteString
bs = do
  (!numItems, !rest1) <- ByteString -> Either DecodeError (Word64, ByteString)
decode_varint ByteString
bs
  (!items, !rest2) <- decode_witness_items (fromIntegral numItems) rest1 []
  Right (Witness items, rest2)
{-# INLINE decode_witness #-}

-- | Decode witness items recursively.
decode_witness_items
  :: Int
  -> BS.ByteString
  -> [BS.ByteString]
  -> Either DecodeError ([BS.ByteString], BS.ByteString)
decode_witness_items :: Int
-> ByteString
-> [ByteString]
-> Either DecodeError ([ByteString], ByteString)
decode_witness_items Int
0 !ByteString
bs ![ByteString]
acc = ([ByteString], ByteString)
-> Either DecodeError ([ByteString], ByteString)
forall a b. b -> Either a b
Right ([ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse [ByteString]
acc, ByteString
bs)
decode_witness_items !Int
n !ByteString
bs ![ByteString]
acc = do
  (!itemLen, !rest1) <- ByteString -> Either DecodeError (Word64, ByteString)
decode_varint ByteString
bs
  let !len = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
itemLen
  if BS.length rest1 < len
    then Left (InsufficientBytes len (BS.length rest1))
    else
      let !item = Int -> ByteString -> ByteString
BS.take Int
len ByteString
rest1
          !rest2 = Int -> ByteString -> ByteString
BS.drop Int
len ByteString
rest1
      in decode_witness_items (n - 1) rest2 (item : acc)

-- | Decode witness stacks for all inputs (internal, returns list).
decode_witness_stacks
  :: Int
  -> BS.ByteString
  -> [[BS.ByteString]]
  -> Either DecodeError ([[BS.ByteString]], BS.ByteString)
decode_witness_stacks :: Int
-> ByteString
-> [[ByteString]]
-> Either DecodeError ([[ByteString]], ByteString)
decode_witness_stacks Int
0 !ByteString
bs ![[ByteString]]
acc = ([[ByteString]], ByteString)
-> Either DecodeError ([[ByteString]], ByteString)
forall a b. b -> Either a b
Right ([[ByteString]] -> [[ByteString]]
forall a. [a] -> [a]
reverse [[ByteString]]
acc, ByteString
bs)
decode_witness_stacks !Int
n !ByteString
bs ![[ByteString]]
acc = do
  (Witness !items, !rest) <- ByteString -> Either DecodeError (Witness, ByteString)
decode_witness ByteString
bs
  decode_witness_stacks (n - 1) rest (items : acc)

-- transaction parsing ---------------------------------------------------------

-- | Decode a raw Bitcoin transaction from bytes.
--
-- Handles both legacy and SegWit transaction formats.
--
-- SegWit format:
-- * version (4 bytes LE)
-- * marker (0x00) + flag (0x01)
-- * input count (varint)
-- * inputs: outpoint (32+4), scriptSig length (varint), scriptSig, sequence
-- * output count (varint)
-- * outputs: value (8 LE), scriptPubKey length (varint), scriptPubKey
-- * witness data (for each input)
-- * locktime (4 bytes LE)
--
-- >>> decode_tx rawTxBytes
-- Right (RawTx {...})
decode_tx :: BS.ByteString -> Either DecodeError RawTx
decode_tx :: ByteString -> Either DecodeError RawTx
decode_tx !ByteString
bs = do
  -- Version (4 bytes LE)
  (!version, !rest1) <- ByteString -> Either DecodeError (Word32, ByteString)
decode_le32 ByteString
bs

  -- Check for SegWit marker/flag
  let !hasWitness = ByteString -> Int
BS.length ByteString
rest1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
2 Bool -> Bool -> Bool
&&
                    HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
rest1 Int
0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x00 Bool -> Bool -> Bool
&&
                    HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
rest1 Int
1 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x01

  if hasWitness
    then decode_tx_segwit version (BS.drop 2 rest1)
    else decode_tx_legacy version rest1
{-# INLINE decode_tx #-}

-- | Decode a SegWit transaction (after marker/flag consumed).
decode_tx_segwit
  :: Word32
  -> BS.ByteString
  -> Either DecodeError RawTx
decode_tx_segwit :: Word32 -> ByteString -> Either DecodeError RawTx
decode_tx_segwit !Word32
version !ByteString
bs = do
  -- Input count and inputs
  (!inputCount, !rest1) <- ByteString -> Either DecodeError (Word64, ByteString)
decode_varint ByteString
bs
  (!inputs, !rest2) <- decode_inputs (fromIntegral inputCount) rest1 []

  -- Output count and outputs
  (!outputCount, !rest3) <- decode_varint rest2
  (!outputs, !rest4) <- decode_outputs (fromIntegral outputCount) rest3 []

  -- Witness data for each input
  (!witnesses, !rest5) <- decode_witness_stacks (length inputs) rest4 []

  -- Locktime (4 bytes LE)
  (!locktime, !_rest6) <- decode_le32 rest5

  Right RawTx
    { rtx_version  = version
    , rtx_inputs   = inputs
    , rtx_outputs  = outputs
    , rtx_witness  = witnesses
    , rtx_locktime = Locktime locktime
    }

-- | Decode a legacy (non-SegWit) transaction.
decode_tx_legacy
  :: Word32
  -> BS.ByteString
  -> Either DecodeError RawTx
decode_tx_legacy :: Word32 -> ByteString -> Either DecodeError RawTx
decode_tx_legacy !Word32
version !ByteString
bs = do
  -- Input count and inputs
  (!inputCount, !rest1) <- ByteString -> Either DecodeError (Word64, ByteString)
decode_varint ByteString
bs
  (!inputs, !rest2) <- decode_inputs (fromIntegral inputCount) rest1 []

  -- Output count and outputs
  (!outputCount, !rest3) <- decode_varint rest2
  (!outputs, !rest4) <- decode_outputs (fromIntegral outputCount) rest3 []

  -- Locktime (4 bytes LE)
  (!locktime, !_rest5) <- decode_le32 rest4

  Right RawTx
    { rtx_version  = version
    , rtx_inputs   = inputs
    , rtx_outputs  = outputs
    , rtx_witness  = []
    , rtx_locktime = Locktime locktime
    }

-- | Decode transaction inputs recursively.
decode_inputs
  :: Int
  -> BS.ByteString
  -> [RawInput]
  -> Either DecodeError ([RawInput], BS.ByteString)
decode_inputs :: Int
-> ByteString
-> [RawInput]
-> Either DecodeError ([RawInput], ByteString)
decode_inputs Int
0 !ByteString
bs ![RawInput]
acc = ([RawInput], ByteString)
-> Either DecodeError ([RawInput], ByteString)
forall a b. b -> Either a b
Right ([RawInput] -> [RawInput]
forall a. [a] -> [a]
reverse [RawInput]
acc, ByteString
bs)
decode_inputs !Int
n !ByteString
bs ![RawInput]
acc = do
  (!input, !rest) <- ByteString -> Either DecodeError (RawInput, ByteString)
decode_input ByteString
bs
  decode_inputs (n - 1) rest (input : acc)

-- | Decode a single transaction input.
--
-- Format: outpoint (36 bytes) + scriptSig length (varint) + scriptSig +
--         sequence (4 bytes LE)
decode_input :: BS.ByteString -> Either DecodeError (RawInput, BS.ByteString)
decode_input :: ByteString -> Either DecodeError (RawInput, ByteString)
decode_input !ByteString
bs = do
  (!outpoint, !rest1) <- ByteString -> Either DecodeError (Outpoint, ByteString)
decode_outpoint ByteString
bs
  (!scriptLen, !rest2) <- decode_varint rest1
  let !len = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
scriptLen
  if BS.length rest2 < len
    then Left (InsufficientBytes len (BS.length rest2))
    else do
      let !scriptSig = Int -> ByteString -> ByteString
BS.take Int
len ByteString
rest2
          !rest3 = Int -> ByteString -> ByteString
BS.drop Int
len ByteString
rest2
      (!seqNum, !rest4) <- decode_le32 rest3
      let !input = Outpoint -> ByteString -> Sequence -> RawInput
RawInput Outpoint
outpoint ByteString
scriptSig (Word32 -> Sequence
Sequence Word32
seqNum)
      Right (input, rest4)

-- | Decode transaction outputs recursively.
decode_outputs
  :: Int
  -> BS.ByteString
  -> [RawOutput]
  -> Either DecodeError ([RawOutput], BS.ByteString)
decode_outputs :: Int
-> ByteString
-> [RawOutput]
-> Either DecodeError ([RawOutput], ByteString)
decode_outputs Int
0 !ByteString
bs ![RawOutput]
acc = ([RawOutput], ByteString)
-> Either DecodeError ([RawOutput], ByteString)
forall a b. b -> Either a b
Right ([RawOutput] -> [RawOutput]
forall a. [a] -> [a]
reverse [RawOutput]
acc, ByteString
bs)
decode_outputs !Int
n !ByteString
bs ![RawOutput]
acc = do
  (!output, !rest) <- ByteString -> Either DecodeError (RawOutput, ByteString)
decode_output ByteString
bs
  decode_outputs (n - 1) rest (output : acc)