diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## 0.8.3
+### Added
+- Add reward halving interval parameter to network constants.
+
 ## 0.8.2
 ### Added
 - Recognize `OP_CHECKDATASIG` and `OP_CHECKDATASIGVERIFY` opcodes.
diff --git a/haskoin-core.cabal b/haskoin-core.cabal
--- a/haskoin-core.cabal
+++ b/haskoin-core.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: fb07b24827c8cd485087089da804cd759c477e30d0ea540ed2c8f3152853d517
+-- hash: 47347401d0d4e92d643d282dd4bf993ced972b2b0300de57d02efc74c705a647
 
 name:           haskoin-core
-version:        0.8.2
+version:        0.8.3
 synopsis:       Bitcoin & Bitcoin Cash library for Haskell
 description:    Haskoin Core is a complete Bitcoin and Bitcoin Cash library of functions and data types for Haskell developers.
 category:       Bitcoin, Finance, Network
@@ -49,6 +49,7 @@
       Network.Haskoin.Script
       Network.Haskoin.Test
       Network.Haskoin.Transaction
+      Network.Haskoin.Transaction.Partial
       Network.Haskoin.Util
   other-modules:
       Network.Haskoin.Block.Common
@@ -121,6 +122,7 @@
       Network.Haskoin.KeysSpec
       Network.Haskoin.NetworkSpec
       Network.Haskoin.ScriptSpec
+      Network.Haskoin.Transaction.PartialSpec
       Network.Haskoin.TransactionSpec
       Network.Haskoin.UtilSpec
       Paths_haskoin_core
@@ -141,6 +143,7 @@
     , split
     , string-conversions
     , text
+    , unordered-containers
     , vector
   default-language: Haskell2010
   build-tool-depends: hspec-discover:hspec-discover
diff --git a/src/Network/Haskoin/Constants.hs b/src/Network/Haskoin/Constants.hs
--- a/src/Network/Haskoin/Constants.hs
+++ b/src/Network/Haskoin/Constants.hs
@@ -105,6 +105,8 @@
     , getBech32Prefix             :: !(Maybe Text)
       -- | Replace-By-Fee (BIP-125)
     , getReplaceByFee             :: !Bool
+      -- | Subsidy halving interval
+    , getHalvingInterval          :: !Word32
     } deriving (Eq, Generic)
 
 instance Serialize Network where
@@ -217,6 +219,7 @@
     , getCashAddrPrefix = Nothing
     , getBech32Prefix = Just "bc"
     , getReplaceByFee = True
+    , getHalvingInterval = 210000
     }
 
 -- | Testnet for Bitcoin SegWit network.
@@ -273,6 +276,7 @@
     , getCashAddrPrefix = Nothing
     , getBech32Prefix = Just "tb"
     , getReplaceByFee = True
+    , getHalvingInterval = 210000
     }
 
 -- | RegTest for Bitcoin SegWit network.
@@ -321,6 +325,7 @@
     , getCashAddrPrefix = Nothing
     , getBech32Prefix = Just "bcrt"
     , getReplaceByFee = True
+    , getHalvingInterval = 150
     }
 
 -- | Bitcoin Cash network. Symbol: BCH.
@@ -409,6 +414,7 @@
     , getCashAddrPrefix = Just "bitcoincash"
     , getBech32Prefix = Nothing
     , getReplaceByFee = False
+    , getHalvingInterval = 210000
     }
 
 -- | Testnet for Bitcoin Cash network.
@@ -472,6 +478,7 @@
     , getCashAddrPrefix = Just "bchtest"
     , getBech32Prefix = Nothing
     , getReplaceByFee = False
+    , getHalvingInterval = 210000
     }
 
 -- | RegTest for Bitcoin Cash network.
@@ -523,6 +530,7 @@
     , getCashAddrPrefix = Just "bchreg"
     , getBech32Prefix = Nothing
     , getReplaceByFee = False
+    , getHalvingInterval = 150
     }
 
 -- | List of all networks supported by this library.
diff --git a/src/Network/Haskoin/Crypto/Hash.hs b/src/Network/Haskoin/Crypto/Hash.hs
--- a/src/Network/Haskoin/Crypto/Hash.hs
+++ b/src/Network/Haskoin/Crypto/Hash.hs
@@ -145,7 +145,7 @@
 
 -- | Calculate RIPEMD160 hash.
 ripemd160 :: ByteArrayAccess b => b -> Hash160
-ripemd160 = Hash160 . BSS.toShort . BA.convert. hashWith RIPEMD160
+ripemd160 = Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160
 
 -- | Claculate SHA1 hash.
 sha1 :: ByteArrayAccess b => b -> Hash160
diff --git a/src/Network/Haskoin/Crypto/Signature.hs b/src/Network/Haskoin/Crypto/Signature.hs
--- a/src/Network/Haskoin/Crypto/Signature.hs
+++ b/src/Network/Haskoin/Crypto/Signature.hs
@@ -67,7 +67,7 @@
             Just s  -> return s
             Nothing -> fail "Invalid signature"
 
--- | Serialize an ECDSA signatur for Bitcoin use.
+-- | Serialize an ECDSA signature for Bitcoin use.
 putSig :: Putter Sig
 putSig s = putByteString $ exportSig s
 
diff --git a/src/Network/Haskoin/Script/Common.hs b/src/Network/Haskoin/Script/Common.hs
--- a/src/Network/Haskoin/Script/Common.hs
+++ b/src/Network/Haskoin/Script/Common.hs
@@ -27,6 +27,8 @@
     , encodeOutputBS
     , decodeOutput
     , decodeOutputBS
+    , toP2SH
+    , toP2WSH
     , isPushOp
     , opPushData
     , intToScriptOp
@@ -706,6 +708,14 @@
 -- | Similar to 'encodeOutput' but encodes to a ByteString
 encodeOutputBS :: ScriptOutput -> ByteString
 encodeOutputBS = S.encode . encodeOutput
+
+-- | Encode script as pay-to-script-hash script
+toP2SH :: Script -> ScriptOutput
+toP2SH = PayScriptHash . addressHash . S.encode
+
+-- | Encode script as a pay-to-witness-script-hash script
+toP2WSH :: Script -> ScriptOutput
+toP2WSH = PayWitnessScriptHash . sha256 . S.encode
 
 -- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@
 matchPayMulSig :: Script -> Either String ScriptOutput
diff --git a/src/Network/Haskoin/Transaction/Builder.hs b/src/Network/Haskoin/Transaction/Builder.hs
--- a/src/Network/Haskoin/Transaction/Builder.hs
+++ b/src/Network/Haskoin/Transaction/Builder.hs
@@ -405,7 +405,7 @@
 -- | Merge partially-signed multisig transactions.
 mergeTxs :: Network -> [Tx] -> [(ScriptOutput, Word64, OutPoint)] -> Either String Tx
 mergeTxs net txs os
-    | null txs = error "Transaction list is empty"
+    | null txs = Left "Transaction list is empty"
     | length (nub emptyTxs) /= 1 = Left "Transactions do not match"
     | length txs == 1 = return $ head txs
     | otherwise = foldM (mergeTxInput net txs) (head emptyTxs) outs
diff --git a/src/Network/Haskoin/Transaction/Partial.hs b/src/Network/Haskoin/Transaction/Partial.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/Haskoin/Transaction/Partial.hs
@@ -0,0 +1,450 @@
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE NamedFieldPuns             #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+
+module Network.Haskoin.Transaction.Partial
+    ( PartiallySignedTransaction (..)
+    , Input (..)
+    , Output (..)
+    , UnknownMap (..)
+    , Key (..)
+    , merge
+    , mergeInput
+    , mergeOutput
+    , complete
+    , finalTransaction
+    , emptyPSBT
+    , emptyInput
+    , emptyOutput
+    ) where
+
+import           Control.Applicative         ((<|>))
+import           Control.Monad               (guard, replicateM, void)
+import           Data.ByteString             (ByteString)
+import qualified Data.ByteString             as B
+import           Data.Hashable               (Hashable)
+import           Data.HashMap.Strict         (HashMap)
+import qualified Data.HashMap.Strict         as HashMap
+import           Data.List                   (foldl')
+import           Data.Maybe                  (fromMaybe, isJust)
+import           Data.Proxy                  (Proxy)
+import           Data.Serialize              as S
+import           GHC.Generics                (Generic)
+import           GHC.Word                    (Word64, Word8)
+import           Network.Haskoin.Address     (Address (..), pubKeyAddr)
+import           Network.Haskoin.Keys        (Fingerprint, KeyIndex, PubKeyI)
+import           Network.Haskoin.Network     (VarInt (..), VarString (..),
+                                              putVarInt)
+import           Network.Haskoin.Script      (Script (..), ScriptInput (..),
+                                              ScriptOp (..), ScriptOutput (..),
+                                              SigHash, SimpleInput (..),
+                                              decodeOutput, decodeOutputBS,
+                                              encodeInput, encodeOutputBS,
+                                              isPayScriptHash, opPushData,
+                                              toP2SH, toP2WSH)
+import           Network.Haskoin.Transaction (Tx (..), TxOut, WitnessStack,
+                                              outPointIndex, prevOutput,
+                                              scriptInput, scriptOutput)
+import           Network.Haskoin.Util        (eitherToMaybe)
+
+data PartiallySignedTransaction = PartiallySignedTransaction
+    { unsignedTransaction :: Tx
+    , globalUnknown       :: UnknownMap
+    , inputs              :: [Input]
+    , outputs             :: [Output]
+    } deriving (Show, Eq)
+
+data Input = Input
+    { nonWitnessUtxo     :: Maybe Tx
+    , witnessUtxo        :: Maybe TxOut
+    , partialSigs        :: HashMap PubKeyI ByteString
+    , sigHashType        :: Maybe SigHash
+    , inputRedeemScript  :: Maybe Script
+    , inputWitnessScript :: Maybe Script
+    , inputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])
+    , finalScriptSig     :: Maybe Script
+    , finalScriptWitness :: Maybe WitnessStack
+    , inputUnknown       :: UnknownMap
+    } deriving (Show, Eq)
+
+data Output = Output
+    { outputRedeemScript  :: Maybe Script
+    , outputWitnessScript :: Maybe Script
+    , outputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])
+    , outputUnknown       :: UnknownMap
+    } deriving (Show, Eq)
+
+newtype UnknownMap = UnknownMap { unknownMap :: HashMap Key ByteString }
+    deriving (Show, Eq, Semigroup, Monoid)
+
+data Key = Key
+    { keyType :: Word8
+    , key     :: ByteString
+    } deriving (Show, Eq, Generic)
+
+instance Hashable Key
+
+merge :: PartiallySignedTransaction -> PartiallySignedTransaction -> Maybe PartiallySignedTransaction
+merge psbt1 psbt2
+    | unsignedTransaction psbt1 == unsignedTransaction psbt2
+    = Just $ psbt1
+        { globalUnknown = globalUnknown psbt1 <> globalUnknown psbt2
+        , inputs = zipWith mergeInput (inputs psbt1) (inputs psbt2)
+        , outputs = zipWith mergeOutput (outputs psbt1) (outputs psbt2)
+        }
+merge _ _ = Nothing
+
+mergeInput :: Input -> Input -> Input
+mergeInput a b = Input
+    { nonWitnessUtxo = if isJust witUtx then Nothing else nonWitnessUtxo a <|> nonWitnessUtxo b
+    , witnessUtxo = witUtx
+    , sigHashType = sigHashType a <|> sigHashType b
+    , partialSigs = partialSigs a <> partialSigs b
+    , inputHDKeypaths = inputHDKeypaths a <> inputHDKeypaths b
+    , inputUnknown = inputUnknown a <> inputUnknown b
+    , inputRedeemScript = inputRedeemScript a <|> inputRedeemScript b
+    , inputWitnessScript = inputWitnessScript a <|> inputWitnessScript b
+    , finalScriptSig = finalScriptSig a <|> finalScriptSig b
+    , finalScriptWitness = finalScriptWitness a <|> finalScriptWitness b
+    }
+  where
+    witUtx = witnessUtxo a <|> witnessUtxo b
+
+mergeOutput :: Output -> Output -> Output
+mergeOutput a b = Output
+    { outputRedeemScript = outputRedeemScript a <|> outputRedeemScript b
+    , outputWitnessScript = outputWitnessScript a <|> outputWitnessScript b
+    , outputHDKeypaths = outputHDKeypaths a <> outputHDKeypaths b
+    , outputUnknown = outputUnknown a <> outputUnknown b
+    }
+
+complete :: PartiallySignedTransaction -> PartiallySignedTransaction
+complete psbt = psbt { inputs = map (completeInput . analyzeInputs) (zip [0..] $ inputs psbt) }
+  where
+    analyzeInputs (i, input) = (outputScript =<< witnessUtxo input <|> nonWitScript, input)
+      where
+        nonWitScript = getPrevOut i =<< nonWitnessUtxo input
+
+    getPrevOut i tx =
+       (txOut tx !!?) . fromIntegral . outPointIndex . prevOutput =<< txIn (unsignedTransaction psbt) !!? i
+    xs !!? i = lookup i $ zip [0..] xs
+
+    outputScript = eitherToMaybe . decodeOutputBS . scriptOutput
+
+    completeInput (Nothing, input)     = input
+    completeInput (Just script, input) = completeSig input script
+
+completeSig :: Input -> ScriptOutput -> Input
+completeSig input (PayPK k) =
+    input { finalScriptSig = eitherToMaybe . S.decode =<< HashMap.lookup k (partialSigs input) }
+completeSig input (PayPKHash h)
+    | [(k, sig)] <- HashMap.toList $ partialSigs input
+    , PubKeyAddress h == pubKeyAddr k
+    = input { finalScriptSig = Just $ Script [opPushData sig, opPushData (S.encode k)] }
+completeSig input (PayMulSig pubKeys m) | length sigs >= m = input { finalScriptSig = finalSig }
+  where
+    sigs = collectSigs m pubKeys input
+    finalSig = Script . (OP_0 :) . (map opPushData sigs <>) . pure . opPushData . S.encode <$> inputRedeemScript input
+completeSig input (PayScriptHash h)
+    | Just rdmScript <- inputRedeemScript input
+    , PayScriptHash h == toP2SH rdmScript
+    , Right decodedScript <- decodeOutput rdmScript
+    , not (isPayScriptHash decodedScript)
+    = completeSig input decodedScript
+completeSig input (PayWitnessPKHash h)
+    | [(k, sig)] <- HashMap.toList $ partialSigs input
+    , PubKeyAddress h == pubKeyAddr k
+    = input { finalScriptWitness = Just [sig, S.encode k]
+            , finalScriptSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input
+            }
+completeSig input (PayWitnessScriptHash h)
+    | Just witScript <- inputWitnessScript input
+    , PayWitnessScriptHash h == toP2WSH witScript
+    , Right decodedScript <- decodeOutput witScript
+    = completeWitnessSig input decodedScript
+completeSig input _ = input
+
+completeWitnessSig :: Input -> ScriptOutput -> Input
+completeWitnessSig input script@(PayMulSig pubKeys m) | length sigs >= m = input
+    { finalScriptWitness = Just finalWit
+    , finalScriptSig = finalSig
+    }
+  where
+    sigs = collectSigs m pubKeys input
+    finalSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input
+    finalWit = mempty : sigs <> [encodeOutputBS script]
+completeWitnessSig input _ = input
+
+collectSigs :: Int -> [PubKeyI] -> Input -> [ByteString]
+collectSigs m pubKeys input = take m . reverse $ foldl' lookupKey [] pubKeys
+  where
+    lookupKey sigs key = maybe sigs (:sigs) $ HashMap.lookup key (partialSigs input)
+
+finalTransaction :: PartiallySignedTransaction -> Tx
+finalTransaction psbt = setInputs . foldl' finalizeInput ([], []) $ zip (txIn tx) (inputs psbt)
+  where
+    tx = unsignedTransaction psbt
+    hasWitness = any (isJust . finalScriptWitness) (inputs psbt)
+    setInputs (ins, witData) = tx { txIn = reverse ins, txWitness = if hasWitness then reverse witData else [] }
+    finalizeInput (ins, witData) (txInput, psbtInput) = maybe finalWitness finalScript $ finalScriptSig psbtInput
+      where
+        finalScript script = (txInput { scriptInput = encode script }:ins, []:witData)
+        finalWitness = (ins, fromMaybe [] (finalScriptWitness psbtInput):witData)
+
+emptyPSBT :: Tx -> PartiallySignedTransaction
+emptyPSBT tx = PartiallySignedTransaction
+    { unsignedTransaction = tx
+    , globalUnknown = mempty
+    , inputs = replicate (length (txIn tx)) emptyInput
+    , outputs = replicate (length (txOut tx)) emptyOutput
+    }
+
+emptyInput :: Input
+emptyInput = Input
+    Nothing Nothing HashMap.empty Nothing
+    Nothing Nothing HashMap.empty
+    Nothing Nothing (UnknownMap HashMap.empty)
+
+emptyOutput :: Output
+emptyOutput = Output Nothing Nothing HashMap.empty (UnknownMap HashMap.empty)
+
+instance Serialize PartiallySignedTransaction where
+    get = do
+        magic <- getBytes 4
+        guard $ magic == "psbt"
+        headerSep <- getWord8
+        guard $ headerSep == 0xff
+
+        keySize <- getWord8
+        guard $ keySize == 1
+        globalUnsignedTxType <- getWord8
+        guard $ globalUnsignedTxType == 0x00
+        unsignedTransaction <- getSizedBytes
+        guard $ all (B.null . scriptInput) (txIn unsignedTransaction)
+        guard $ null (txWitness unsignedTransaction)
+
+        globalUnknown <- get
+        globalEnd <- getWord8
+        guard $ globalEnd == 0x00
+
+        inputs <- replicateM (length $ txIn unsignedTransaction) get
+        outputs <- replicateM (length $ txOut unsignedTransaction) get
+
+        return PartiallySignedTransaction { unsignedTransaction, globalUnknown, inputs, outputs }
+
+    put PartiallySignedTransaction{ unsignedTransaction, globalUnknown, inputs, outputs } = do
+        putByteString "psbt"
+        putWord8 0xff -- Header separator
+
+        putWord8 0x01 -- Key size
+        putWord8 0x00 -- Unsigned Transaction type
+        putSizedBytes unsignedTransaction
+        put globalUnknown
+        putWord8 0x00 -- Global end
+
+        mapM_ put inputs
+        mapM_ put outputs
+
+instance Serialize Key where
+    get = do
+        VarInt keySize <- get
+        guard $ keySize > 0
+        t <- getWord8
+        k <- getBytes (fromIntegral keySize - 1)
+        return (Key t k)
+
+    put (Key t k) = do
+        putVarInt $ 1 + B.length k
+        putWord8 t
+        putByteString k
+
+instance Serialize UnknownMap where
+    get = go HashMap.empty
+      where
+        getItem m = do
+            k <- get
+            VarString v <- get
+            go $ HashMap.insert k v m
+        go m = do
+            isEnd <- lookAhead getWord8
+            if isEnd == 0x00
+                then return (UnknownMap m)
+                else getItem m
+
+    put (UnknownMap m) = void $ HashMap.traverseWithKey (\k v -> put k >> put (VarString v)) m
+
+instance Serialize Input where
+    get = getMap getInputItem setInputUnknown emptyInput
+      where
+        setInputUnknown f input = input { inputUnknown = UnknownMap $ f (unknownMap $ inputUnknown input) }
+
+    put Input { nonWitnessUtxo, witnessUtxo, partialSigs, sigHashType
+                  , inputRedeemScript, inputWitnessScript, inputHDKeypaths
+                  , finalScriptSig, finalScriptWitness, inputUnknown
+                  } = do
+        whenJust (putKeyValue InNonWitnessUtxo) nonWitnessUtxo
+        whenJust (putKeyValue InWitnessUtxo) witnessUtxo
+        putPartialSig partialSigs
+        whenJust putSigHash sigHashType
+        whenJust (putKeyValue InRedeemScript) inputRedeemScript
+        whenJust (putKeyValue InWitnessScript) inputWitnessScript
+        putHDPath InBIP32Derivation inputHDKeypaths
+        whenJust (putKeyValue InFinalScriptSig) finalScriptSig
+        whenJust (putKeyValue InFinalScriptWitness) finalScriptWitness
+        put inputUnknown
+        putWord8 0x00
+      where
+        putPartialSig = putPubKeyMap InPartialSig . fmap VarString
+        putSigHash sigHash = do
+            putVarInt 1 >> putWord8 (enumWord8 InSigHashType)
+            putWord8 0x04
+            putWord32le (fromIntegral sigHash)
+
+instance Serialize Output where
+    get = getMap getOutputItem setOutputUnknown emptyOutput
+      where
+        setOutputUnknown f output = output { outputUnknown = UnknownMap $ f (unknownMap $ outputUnknown output) }
+
+    put Output{ outputRedeemScript, outputWitnessScript, outputHDKeypaths, outputUnknown } = do
+        whenJust (putKeyValue OutRedeemScript) outputRedeemScript
+        whenJust (putKeyValue OutWitnessScript) outputWitnessScript
+        putHDPath OutBIP32Derivation outputHDKeypaths
+        put outputUnknown
+        putWord8 0x00
+
+putSizedBytes :: Serialize a => a -> Put
+putSizedBytes a = putVarInt (B.length bs) >> putByteString bs
+  where bs = encode a
+
+getSizedBytes :: Serialize a => Get a
+getSizedBytes = getNested (fromIntegral . getVarInt <$> get) get
+
+putKeyValue :: (Enum t, Serialize v) => t -> v -> Put
+putKeyValue t v = putVarInt 1 >> putWord8 (enumWord8 t) >> putSizedBytes v
+
+getMap :: (Bounded t, Enum t)
+       => (Int -> a -> t -> Get a)
+       -> ((HashMap Key ByteString -> HashMap Key ByteString) -> a -> a)
+       -> a -> Get a
+getMap getMapItem setUnknown = go
+  where
+    getItem keySize m (Right t) = getMapItem (fromIntegral keySize - 1) m t >>= go
+    getItem keySize m (Left t)  = do
+        k <- getBytes (fromIntegral keySize - 1)
+        VarString v <- get
+        go $ setUnknown (HashMap.insert (Key t k) v) m
+    go m = do
+        keySize <- getVarInt <$> get
+        if keySize == 0
+            then return m
+            else getItem keySize m =<< (word8Enum <$> getWord8)
+
+data InputType
+    = InNonWitnessUtxo
+    | InWitnessUtxo
+    | InPartialSig
+    | InSigHashType
+    | InRedeemScript
+    | InWitnessScript
+    | InBIP32Derivation
+    | InFinalScriptSig
+    | InFinalScriptWitness
+    deriving (Show, Eq, Enum, Bounded)
+
+data OutputType
+    = OutRedeemScript
+    | OutWitnessScript
+    | OutBIP32Derivation
+    deriving (Show, Eq, Enum, Bounded)
+
+getInputItem :: Int -> Input -> InputType -> Get Input
+getInputItem 0 input@Input{nonWitnessUtxo = Nothing} InNonWitnessUtxo = do
+    utxo <- getSizedBytes
+    return $ input { nonWitnessUtxo = Just utxo }
+getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do
+    utxo <- getSizedBytes
+    return $ input { witnessUtxo = Just utxo }
+getInputItem keySize input InPartialSig = do
+    (k, v) <- getPartialSig keySize
+    return $ input { partialSigs = HashMap.insert k v (partialSigs input) }
+  where
+    getPartialSig keySize = (,) <$> isolate keySize get <*> (getVarString <$> get)
+getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do
+    VarInt size <- get
+    guard $ size == 0x04
+    sigHash <- fromIntegral <$> getWord32le
+    return $ input { sigHashType = Just sigHash }
+getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do
+    script <- getSizedBytes
+    return $ input { inputRedeemScript = Just script }
+getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do
+    script <- getSizedBytes
+    return $ input { inputWitnessScript = Just script }
+getInputItem keySize input InBIP32Derivation = do
+    (k, v) <- getHDPath keySize
+    return $ input { inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input) }
+getInputItem 0 input@Input{finalScriptSig = Nothing} InFinalScriptSig = do
+    script <- getSizedBytes
+    return $ input { finalScriptSig = Just script }
+getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do
+    scripts <- map getVarString <$> getVarIntList
+    return $ input { finalScriptWitness = Just scripts }
+  where
+    getVarIntList = do
+        VarInt n <- get
+        replicateM (fromIntegral n) get
+getInputItem keySize input inputType = fail $
+    "Incorrect key size for input item or item already existed: " <>
+    show (keySize, input, inputType)
+
+getOutputItem :: Int -> Output -> OutputType -> Get Output
+getOutputItem 0 output@Output{outputRedeemScript = Nothing} OutRedeemScript = do
+    script <- getSizedBytes
+    return $ output { outputRedeemScript = Just script }
+getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do
+    script <- getSizedBytes
+    return $ output { outputWitnessScript = Just script }
+getOutputItem keySize output OutBIP32Derivation = do
+    (k, v) <- getHDPath keySize
+    return $ output { outputHDKeypaths = HashMap.insert k v (outputHDKeypaths output) }
+getOutputItem keySize output outputType = fail $
+    "Incorrect key size for output item or item already existed: " <>
+    show (keySize, output, outputType)
+
+getHDPath :: Int -> Get (PubKeyI, (Fingerprint, [KeyIndex]))
+getHDPath keySize = (,) <$> isolate keySize get <*> (unPSBTHDPath <$> get)
+
+putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put
+putHDPath t = putPubKeyMap t . fmap PSBTHDPath
+
+newtype PSBTHDPath = PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) }
+    deriving (Show, Eq)
+
+instance Serialize PSBTHDPath where
+    get = do
+        VarInt valueSize <- get
+        guard $ valueSize `mod` 4 == 0
+        let numIndices = (fromIntegral valueSize - 4) `div` 4
+        PSBTHDPath <$> isolate (fromIntegral valueSize) ((,) <$> getWord32le <*> getKeyIndexList numIndices)
+      where
+        getKeyIndexList n = replicateM n getWord32le
+
+    put (PSBTHDPath (fp, kis)) = putVarInt (B.length bs) >> putByteString bs
+      where bs = runPut $ putWord32le fp >> mapM_ putWord32le kis
+
+putPubKeyMap :: (Serialize a, Enum t) => t -> HashMap PubKeyI a -> Put
+putPubKeyMap t = void . HashMap.traverseWithKey putItem
+  where
+    putItem k v = put (Key (enumWord8 t) (encode k)) >> put v
+
+enumWord8 :: Enum a => a -> Word8
+enumWord8 = fromIntegral . fromEnum
+
+word8Enum :: forall a. (Bounded a, Enum a) => Word8 -> Either Word8 a
+word8Enum n | n <= enumWord8 (maxBound :: a) = Right . toEnum $ fromIntegral n
+word8Enum n = Left n
+
+whenJust :: Monad m => (a -> m ()) -> Maybe a -> m ()
+whenJust = maybe (return ())
diff --git a/test/Network/Haskoin/Transaction/PartialSpec.hs b/test/Network/Haskoin/Transaction/PartialSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Haskoin/Transaction/PartialSpec.hs
@@ -0,0 +1,335 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Network.Haskoin.Transaction.PartialSpec (spec) where
+
+import           Data.ByteString                     (ByteString)
+import           Data.Either                         (fromRight, isLeft,
+                                                      isRight)
+import           Data.HashMap.Strict                 (fromList, singleton)
+import           Data.Maybe                          (fromJust, isJust)
+import           Data.Serialize                      as S
+import           Data.Text                           (Text)
+import           Test.Hspec
+import           Test.HUnit                          (Assertion, assertBool,
+                                                      assertEqual)
+import           Test.QuickCheck
+
+import           Network.Haskoin.Address
+import           Network.Haskoin.Constants
+import           Network.Haskoin.Crypto
+import           Network.Haskoin.Keys
+import           Network.Haskoin.Script
+import           Network.Haskoin.Test
+import           Network.Haskoin.Transaction
+import           Network.Haskoin.Transaction.Partial
+import           Network.Haskoin.Util
+
+spec :: Spec
+spec = describe "partially signed bitcoin transaction unit tests" $ do
+    it "encodes trivial psbt" $
+        encodeHex (S.encode trivialPSBT) == trivialPSBTHex
+    it "decodes trivial psbt" $
+        decodeHexPSBT trivialPSBTHex == Right trivialPSBT
+    it "encodes and decodes non-empty transactions" $
+        S.decode (S.encode nonEmptyTransactionPSBT) == Right nonEmptyTransactionPSBT
+    it "does not decode invalid bip vectors" $
+        mapM_ invalidVecTest invalidVec
+    it "encodes valid bip vecs" $
+        mapM_ (uncurry encodeVecTest) validEncodeVec
+    it "decodes valid bip vecs" $
+        mapM_ (uncurry decodeVecTest) $ zip [1..] validVec
+    it "decodes vector 2" vec2Test
+    it "decodes vector 3" vec3Test
+    it "decodes vector 4" vec4Test
+    it "decodes vector 5" vec5Test
+    it "decodes vector 6" vec6Test
+    it "signed and finalized p2pkh PSBTs verify" $ property $
+        forAll arbitraryKeyPair $ verifyNonWitnessPSBT btc . unfinalizedPkhPSBT btc
+    it "signed and finalized multisig PSBTs verify" $ property $
+        forAll arbitraryMultiSig $ verifyNonWitnessPSBT btc . unfinalizedMsPSBT btc
+
+vec2Test :: Assertion
+vec2Test = do
+    psbt <- decodeHexPSBTM "Cannot parse validVec2" validVec2Hex
+    assertEqual "2 inputs" 2 (length $ inputs psbt)
+    assertEqual "2 outputs" 2 (length $ outputs psbt)
+    assertBool "final script sig" $ isJust (finalScriptSig . head $ inputs psbt)
+
+    let rdmScript = fromJust . inputRedeemScript $ inputs psbt !! 1
+    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True
+
+    let scrptPubKey = witnessScriptPubKey $ inputs psbt !! 1
+        rdmScriptP2SH = toP2SH rdmScript
+    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey
+    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH
+
+    mapM_ (assertEqual "outputs are empty" emptyOutput) (outputs psbt)
+
+vec3Test :: Assertion
+vec3Test = do
+    psbt <- decodeHexPSBTM "Cannot parse validVec3" validVec3Hex
+    assertEqual "1 input" 1 (length $ inputs psbt)
+    assertEqual "2 outputs" 2 (length $ outputs psbt)
+    let txInput = head . txIn $ unsignedTransaction psbt
+        firstInput = head $ inputs psbt
+        Just utx = nonWitnessUtxo firstInput
+        OutPoint prevHash prevVOut = prevOutput txInput
+    assertEqual "txids of inputs match" prevHash (txHash utx)
+    let prevOutputKey = fromRight (error "Could not decode key")
+                      . decodeOutputBS . scriptOutput $ txOut utx !! fromIntegral prevVOut
+    assertBool "p2pkh" $ isPayPKHash prevOutputKey
+    assertEqual "sighash type" sigHashAll (fromJust $ sigHashType firstInput)
+
+vec4Test :: Assertion
+vec4Test = do
+    psbt <- decodeHexPSBTM "Cannot parse validVec4" validVec4Hex
+    assertEqual "2 inputs" 2 (length $ inputs psbt)
+    assertEqual "2 outputs" 2 (length $ outputs psbt)
+    let firstInput = head $ inputs psbt
+        secondInput = inputs psbt !! 1
+    assertEqual "first input not final script sig" Nothing (finalScriptSig firstInput)
+    assertEqual "second input not final script sig" Nothing (finalScriptSig secondInput)
+
+    let rdmScript = fromJust $ inputRedeemScript secondInput
+    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True
+
+    let scrptPubKey = witnessScriptPubKey secondInput
+        rdmScriptP2SH = toP2SH rdmScript
+    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey
+    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH
+
+    assertBool "all non-empty outputs" $ emptyOutput `notElem` outputs psbt
+
+vec5Test :: Assertion
+vec5Test = do
+    psbt <- decodeHexPSBTM "Cannot parse validVec5" validVec5Hex
+    assertEqual "1 input" 1 (length $ inputs psbt)
+    assertEqual "1 output" 1 (length $ outputs psbt)
+    let input = head $ inputs psbt
+    assertEqual "input not final script sig" Nothing (finalScriptSig input)
+
+    let rdmScript = fromJust $ inputRedeemScript input
+    assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput rdmScript) == Right True
+
+    let scrptPubKey = witnessScriptPubKey input
+        rdmScriptP2SH = toP2SH rdmScript
+    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey
+    assertEqual "expected redeem script"  expectedOut2 rdmScriptP2SH
+  where
+    expectedOut2 = fromRight (error "could not decode expected output")
+                . decodeOutputBS . fromJust $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"
+
+vec6Test :: Assertion
+vec6Test = do
+    psbt <- decodeHexPSBTM "Cannot parse validVec6" validVec6Hex
+    assertEqual "1 input" 1 (length $ inputs psbt)
+    assertEqual "1 output" 1 (length $ outputs psbt)
+
+    let tx = unsignedTransaction psbt
+    assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)
+
+    assertEqual "correct unknowns" expectedUnknowns (inputUnknown . head $ inputs psbt)
+  where
+    expectedUnknowns = UnknownMap $ singleton
+        (Key 0x0f (fromJust $ decodeHex "010203040506070809"))
+        (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")
+
+expectedOut :: ScriptOutput
+expectedOut = fromRight (error "could not decode expected output")
+            . decodeOutputBS . fromJust $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"
+
+witnessScriptPubKey :: Input -> ScriptOutput
+witnessScriptPubKey = fromRight (error "could not decode witness utxo")
+                    . decodeOutputBS . scriptOutput . fromJust . witnessUtxo
+
+decodeHexPSBT :: Text -> Either String PartiallySignedTransaction
+decodeHexPSBT = S.decode . fromJust . decodeHex
+
+decodeHexPSBTM :: Monad m => String -> Text -> m PartiallySignedTransaction
+decodeHexPSBTM errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT
+
+hexScript :: Text -> ByteString
+hexScript =
+    either (error "Could not decode script") encodeScript . S.decode . fromJust . decodeHex
+  where
+    encodeScript :: Script -> ByteString
+    encodeScript = S.encode
+
+invalidVecTest :: Text -> Assertion
+invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT
+
+decodeVecTest :: Int -> Text -> Assertion
+decodeVecTest i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT
+
+encodeVecTest :: PartiallySignedTransaction -> Text -> Assertion
+encodeVecTest psbt hex = assertEqual "encodes correctly" (S.encode psbt) (fromJust $ decodeHex hex)
+
+trivialPSBT :: PartiallySignedTransaction
+trivialPSBT = PartiallySignedTransaction
+    { unsignedTransaction = Tx { txVersion = 2, txIn = [], txOut = [], txWitness = [], txLockTime = 0 }
+    , globalUnknown = UnknownMap mempty
+    , inputs = []
+    , outputs = []
+    }
+
+trivialPSBTHex :: Text
+trivialPSBTHex = "70736274ff01000a0200000000000000000000"
+
+nonEmptyTransactionPSBT :: PartiallySignedTransaction
+nonEmptyTransactionPSBT = emptyPSBT testTx1
+
+verifyNonWitnessPSBT :: Network -> PartiallySignedTransaction -> Bool
+verifyNonWitnessPSBT net psbt = verifyStdTx net (finalTransaction (complete psbt)) sigData
+  where
+    sigData = inputSigData =<< zip (inputs psbt) (txIn $ unsignedTransaction psbt)
+    decodeOutScript = fromRight (error "Could not parse output script") . decodeOutputBS
+    inputSigData (input, txInput) =
+        map (\(TxOut val script) -> (decodeOutScript script, val, prevOutput txInput))
+            (txOut . fromJust $ nonWitnessUtxo input)
+
+unfinalizedPkhPSBT :: Network -> (SecKeyI, PubKeyI) -> PartiallySignedTransaction
+unfinalizedPkhPSBT net (prvKey, pubKey) = (emptyPSBT currTx)
+    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = singleton pubKey sig } ] }
+  where
+    currTx = unfinalizedTx (txHash prevTx)
+    prevTx = testUtxo [prevOut]
+    prevOutScript = addressToScript (pubKeyAddr pubKey)
+    prevOut = TxOut { outValue = 200000000, scriptOutput = S.encode prevOutScript }
+    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll
+    sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll
+
+arbitraryMultiSig :: Gen ([(SecKeyI, PubKeyI)], Int)
+arbitraryMultiSig = do
+    (m, n) <- arbitraryMSParam
+    keys <- vectorOf n arbitraryKeyPair
+    return (keys, m)
+
+unfinalizedMsPSBT :: Network -> ([(SecKeyI, PubKeyI)], Int) -> PartiallySignedTransaction
+unfinalizedMsPSBT net (keys, m) = (emptyPSBT currTx)
+    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = sigs
+                            , inputRedeemScript = Just prevOutScript
+                            } ] }
+  where
+    currTx = unfinalizedTx (txHash prevTx)
+    prevTx = testUtxo [prevOut]
+    prevOutScript = encodeOutput $ PayMulSig (map snd keys) m
+    prevOut = TxOut { outValue = 200000000, scriptOutput = encodeOutputBS (toP2SH prevOutScript) }
+    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll
+    sigs = fromList $ map sig keys
+    sig (prvKey, pubKey) = (pubKey, encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll)
+
+unfinalizedTx :: TxHash -> Tx
+unfinalizedTx prevHash = Tx
+        { txVersion = 2
+        , txIn = [ TxIn
+            { prevOutput = OutPoint prevHash 0
+            , scriptInput = ""
+            , txInSequence = 4294967294
+            } ]
+        , txOut =
+            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }
+            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }
+            ]
+        , txWitness = []
+        , txLockTime = 1257139
+        }
+
+invalidVec :: [Text]
+invalidVec =
+    [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300"
+    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"
+    , "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"
+    , "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"
+    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000"
+    , "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"
+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"
+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"
+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"
+    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"
+    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"
+    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"
+    ]
+
+
+validEncodeVec :: [(PartiallySignedTransaction, Text)]
+validEncodeVec = [(validVec1, validVec1Hex)]
+
+testTx1 :: Tx
+testTx1 = Tx
+        { txVersion = 2
+        , txIn = [ TxIn
+            { prevOutput = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0
+            , scriptInput = ""
+            , txInSequence = 4294967294
+            } ]
+        , txOut =
+            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }
+            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }
+            ]
+        , txWitness = []
+        , txLockTime = 1257139
+        }
+
+testUtxo :: [TxOut] -> Tx
+testUtxo prevOuts = Tx
+        { txVersion = 1
+        , txIn =
+            [ TxIn
+                { prevOutput = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1
+                , scriptInput = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985"
+                , txInSequence = 4294967295
+                }
+            , TxIn
+                { prevOutput = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1
+                , scriptInput = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353"
+                , txInSequence = 4294967295
+                }
+            ]
+        , txOut = prevOuts
+        , txWitness =
+            [
+              [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01"
+              , fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"
+              ]
+            , [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01"
+              , fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"
+              ]
+            ]
+        , txLockTime = 0
+        }
+
+testUtxo1 :: Tx
+testUtxo1 = testUtxo
+    [ TxOut { outValue = 200000000, scriptOutput = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac" }
+    , TxOut { outValue = 190303501938, scriptOutput = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587" }
+    ]
+
+validVec1 :: PartiallySignedTransaction
+validVec1 = (emptyPSBT testTx1) { inputs = [ emptyInput { nonWitnessUtxo = Just testUtxo1 } ] }
+
+validVec :: [Text]
+validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]
+
+validVec1Hex :: Text
+validVec1Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000"
+
+validVec2Hex :: Text
+validVec2Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"
+
+validVec3Hex :: Text
+validVec3Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000"
+
+validVec4Hex :: Text
+validVec4Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000"
+
+validVec5Hex :: Text
+validVec5Hex = "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"
+
+validVec6Hex :: Text
+validVec6Hex = "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000"
