packages feed

haskoin-core 0.8.2 → 0.8.3

raw patch · 9 files changed

+815/−5 lines, 9 files

Files

CHANGELOG.md view
@@ -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.
haskoin-core.cabal view
@@ -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
src/Network/Haskoin/Constants.hs view
@@ -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.
src/Network/Haskoin/Crypto/Hash.hs view
@@ -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
src/Network/Haskoin/Crypto/Signature.hs view
@@ -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 
src/Network/Haskoin/Script/Common.hs view
@@ -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
src/Network/Haskoin/Transaction/Builder.hs view
@@ -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
+ src/Network/Haskoin/Transaction/Partial.hs view
@@ -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 ())
+ test/Network/Haskoin/Transaction/PartialSpec.hs view
@@ -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"