packages feed

haskoin-core-1.2.2: test/Haskoin/Transaction/PartialSpec.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}

module Haskoin.Transaction.PartialSpec (spec) where

import Control.Monad ((<=<))
import Data.Aeson (FromJSON, parseJSON, withObject, (.:))
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import Data.ByteString.Base64 (decodeBase64Untyped)
import Data.Bytes.Get
import Data.Bytes.Put
import Data.Bytes.Serial
import Data.Either (fromRight, isLeft, isRight)
import Data.HashMap.Strict (fromList, singleton)
import Data.Maybe (fromJust, isJust)
import Data.Serialize
import Data.Text (Text)
import Data.Text qualified as Text
import Data.Text.Encoding (encodeUtf8)
import Haskoin.Address
import Haskoin.Crypto
import Haskoin.Network.Constants
import Haskoin.Network.Data
import Haskoin.Script
import Haskoin.Transaction
import Haskoin.Util
import Haskoin.Util.Arbitrary
import Test.HUnit (Assertion, assertBool, assertEqual)
import Test.Hspec
import Test.QuickCheck

spec :: Spec
spec = prepareContext $ \ctx ->
  describe "PSBT unit tests" $ do
    it "encodes trivial PSBT" $
      (encodeHex . runPut . putPSBT ctx) trivialPSBT == trivialPSBTHex
    it "decodes trivial PSBT" $
      decodeHexPSBT ctx trivialPSBTHex == Right trivialPSBT
    it "encodes and decodes non-empty transactions" $
      (runGet (getPSBT ctx) . runPut . putPSBT ctx) nonEmptyTransactionPSBT == Right nonEmptyTransactionPSBT
    it "does not decode invalid BIP test vectors" $
      mapM_ (invalidVecTest ctx) invalidVec
    it "encodes valid BIP test vectors" $
      mapM_ (uncurry (encodeVecTest ctx)) validEncodeVec
    it "decodes valid BIP test vectors" $
      mapM_ (uncurry (decodeVecTest ctx)) $
        zip [1 ..] validVec
    it "decodes vector 2" $ vec2Test ctx
    it "decodes vector 3" $ vec3Test ctx
    it "decodes vector 4" $ vec4Test ctx
    it "decodes vector 5" $ vec5Test ctx
    it "decodes vector 6" $ vec6Test ctx
    it "signed and finalized P2PKH PSBTs verify" $
      property $
        forAll (arbitraryKeyPair ctx) $
          verifyNonWitnessPSBT btc ctx . unfinalizedPkhPSBT btc ctx
    it "signed and finalized multisig PSBTs verify" $
      property $
        forAll (arbitraryMultiSig ctx) $
          verifyNonWitnessPSBT btc ctx . unfinalizedMsPSBT btc ctx
    it "encodes and decodes PSBT with final witness script" $
      (fmap (encodeHex . runPut . putPSBT ctx) . decodeHexPSBT ctx) validVec7Hex == Right validVec7Hex
    it "handles complex PSBTs correctly" $ complexPsbtTest ctx
    it "calculates keys properly" $ psbtSignerTest ctx

vec2Test :: Ctx -> Assertion
vec2Test ctx = do
  psbt <- decodeHexPSBTM ctx "Cannot parse validVec2" validVec2Hex
  assertEqual "2 inputs" 2 (length psbt.inputs)
  assertEqual "2 outputs" 2 (length psbt.outputs)
  assertBool "final script sig" $ isJust (head psbt.inputs).finalScriptSig

  let rdmScript = fromJust (psbt.inputs !! 1).inputRedeemScript
  assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput ctx rdmScript) == Right True

  let scrptPubKey = witnessScriptPubKey ctx (psbt.inputs !! 1)
      rdmScriptP2SH = toP2SH rdmScript
  assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey
  assertEqual "expected redeem script" (expectedOut ctx) rdmScriptP2SH

  mapM_ (assertEqual "outputs are empty" emptyOutput) psbt.outputs

vec3Test :: Ctx -> Assertion
vec3Test ctx = do
  psbt <- decodeHexPSBTM ctx "Cannot parse validVec3" validVec3Hex
  assertEqual "1 input" 1 (length psbt.inputs)
  assertEqual "2 outputs" 2 (length psbt.outputs)
  let (txInput : _) = psbt.unsignedTransaction.inputs
      (firstInput : _) = psbt.inputs
      Just utx = firstInput.nonWitnessUtxo
      OutPoint prevHash prevVOut = txInput.outpoint
  assertEqual "txids of inputs match" prevHash (txHash utx)
  let prevOutputKey =
        fromRight (error "Could not decode key")
          . unmarshal ctx
          . (.script)
          $ utx.outputs !! fromIntegral prevVOut
  assertBool "p2pkh" $ isPayPKHash prevOutputKey
  assertEqual "sighash type" sigHashAll (fromJust firstInput.sigHashType)

vec4Test :: Ctx -> Assertion
vec4Test ctx = do
  psbt <- decodeHexPSBTM ctx "Cannot parse validVec4" validVec4Hex
  assertEqual "2 inputs" 2 (length psbt.inputs)
  assertEqual "2 outputs" 2 (length psbt.outputs)
  let (firstInput : _) = psbt.inputs
      (_ : secondInput : _) = psbt.inputs
  assertEqual "first input not final script sig" Nothing firstInput.finalScriptSig
  assertEqual "second input not final script sig" Nothing secondInput.finalScriptSig

  let Just rdmScript = secondInput.inputRedeemScript
  assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput ctx rdmScript) == Right True

  let scrptPubKey = witnessScriptPubKey ctx secondInput
      rdmScriptP2SH = toP2SH rdmScript
  assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey
  assertEqual "expected redeem script" (expectedOut ctx) rdmScriptP2SH

  assertBool "all non-empty outputs" $ emptyOutput `notElem` psbt.outputs

vec5Test :: Ctx -> Assertion
vec5Test ctx = do
  psbt <- decodeHexPSBTM ctx "Cannot parse validVec5" validVec5Hex
  assertEqual "Correctly decode PSBT" expectedPsbt psbt
  let (input : _) = psbt.inputs

  let Just rdmScript = input.inputRedeemScript
  assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput ctx rdmScript) == Right True

  let scrptPubKey = witnessScriptPubKey ctx 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")
        . unmarshal ctx
        . fromJust
        $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"
    -- From the bitcoind decodepsbt rpc call
    expectedPsbt =
      PSBT
        { unsignedTransaction =
            Tx
              { version = 2,
                inputs =
                  [ TxIn
                      { outpoint =
                          OutPoint
                            { hash = "39bc5c3b33d66ce3d7852a7942331e3ec10f8ba50f225fc41fb5dfa523239a27",
                              index = 0
                            },
                        script = "",
                        sequence = 4294967295
                      }
                  ],
                outputs =
                  [ TxOut
                      { value = 199908000,
                        script = (fromJust . decodeHex) "76a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac"
                      }
                  ],
                witness = mempty,
                locktime = 0
              },
          globalUnknown = mempty,
          inputs =
            [ Input
                { nonWitnessUtxo = Nothing,
                  witnessUtxo =
                    Just
                      ( TxOut
                          { value = 199909013,
                            script = (fromJust . decodeHex) "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"
                          }
                      ),
                  partialSigs =
                    fromList
                      [ ( PublicKey
                            { point =
                                fromJust $
                                  importPubKey ctx
                                    =<< decodeHex
                                      "03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46",
                              compress = True
                            },
                          (fromJust . decodeHex) "304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01"
                        )
                      ],
                  sigHashType = Nothing,
                  inputRedeemScript =
                    Just
                      . fromRight (error "vec5Test: Could not decode redeem script")
                      . decode
                      . fromJust
                      $ decodeHex "0020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681",
                  inputWitnessScript =
                    Just
                      . fromRight (error "vec5Test: Could not decode witness script")
                      . decode
                      . fromJust
                      $ decodeHex "522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae",
                  inputHDKeypaths =
                    fromList
                      [ ( PublicKey
                            { point =
                                fromJust $
                                  importPubKey ctx
                                    =<< decodeHex
                                      "03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46",
                              compress = True
                            },
                          ("b4a6ba67", [hardIndex 0, hardIndex 0, hardIndex 4])
                        ),
                        ( PublicKey
                            { point =
                                fromJust $
                                  importPubKey ctx
                                    =<< decodeHex
                                      "03de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd",
                              compress = True
                            },
                          ("b4a6ba67", [hardIndex 0, hardIndex 0, hardIndex 5])
                        )
                      ],
                  finalScriptSig = Nothing,
                  finalScriptWitness = Nothing,
                  inputUnknown = mempty
                }
            ],
          outputs =
            [ Output
                { outputRedeemScript = Nothing,
                  outputWitnessScript = Nothing,
                  outputHDKeypaths = mempty,
                  outputUnknown = mempty
                }
            ]
        }
    hardIndex = (+ 2 ^ 31)

vec6Test :: Ctx -> Assertion
vec6Test ctx = do
  psbt <- decodeHexPSBTM ctx "Cannot parse validVec6" validVec6Hex
  assertEqual "1 input" 1 (length psbt.inputs)
  assertEqual "1 output" 1 (length psbt.outputs)

  let tx = psbt.unsignedTransaction
  assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)

  assertEqual "correct unknowns" expectedUnknowns (head psbt.inputs).inputUnknown
  where
    expectedUnknowns =
      UnknownMap $
        singleton
          (Key 0x0f (fromJust $ decodeHex "010203040506070809"))
          (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")

complexPsbtTest :: Ctx -> Assertion
complexPsbtTest ctx = do
  complex <- readTestFileParser (parseComplexJSON ctx) "complex_psbt.json"

  let computedCombinedPsbt = mergeMany $ complexSignedPsbts complex
      expectedCombinedPsbt = stripRedundantUtxos $ complexCombinedPsbt complex
  assertEqual "combined psbt" computedCombinedPsbt (Just expectedCombinedPsbt)

  let computedCompletePsbt = complete ctx $ complexCombinedPsbt complex
      expectedCompletePsbt = complexCompletePsbt complex
  assertEqual "complete psbt" computedCompletePsbt expectedCompletePsbt

  let computedFinalTx = finalTransaction $ complexCompletePsbt complex
  assertEqual "final tx" computedFinalTx (complexFinalTx complex)
  where
    stripRedundantUtxos PSBT {..} = PSBT {inputs = stripRedundantUtxo <$> inputs, ..}
    stripRedundantUtxo input
      | Just {} <- input.witnessUtxo = input {nonWitnessUtxo = Nothing}
      | otherwise = input

psbtSignerTest :: Ctx -> Assertion
psbtSignerTest ctx = do
  assertEqual "recover explicit secret key" (Just theSecKey) (getSignerKey signer thePubKey Nothing)
  assertEqual
    "recover key for origin path"
    (Just originPathSecKey)
    (getSignerKey signer originPathPubKey (Just (rootFP, originKeyPath)))
  assertEqual
    "recover key for direct path"
    (Just directPathSecKey)
    (getSignerKey signer directPathPubKey (Just (keyFP, directPath)))
  where
    signer = secKeySigner ctx theSecKey <> xPrvSigner ctx xprv (Just origin)

    Just theSecKey = secKey "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    thePubKey = PublicKey {point = derivePubKey ctx theSecKey, compress = True}

    rootXPrv = makeXPrvKey "PSBTSignerTest"
    rootFP = xPubFP ctx $ deriveXPubKey ctx rootXPrv
    xprv = derivePath ctx keyPath rootXPrv
    keyFP = xPubFP ctx $ deriveXPubKey ctx xprv
    keyPath = Deriv :| 444
    origin = (rootFP, keyPath)

    originKeyPath = Deriv :| 444 :/ 0
    originPathSecKey = (derivePath ctx originKeyPath rootXPrv).key
    originPathPubKey = PublicKey {point = derivePubKey ctx originPathSecKey, compress = True}

    directPath = Deriv :/ 1
    directPathSecKey = (derivePath ctx directPath xprv).key
    directPathPubKey = PublicKey {point = derivePubKey ctx directPathSecKey, compress = True}

expectedOut :: Ctx -> ScriptOutput
expectedOut ctx =
  fromRight (error "could not decode expected output")
    . unmarshal ctx
    . fromJust
    $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"

witnessScriptPubKey :: Ctx -> Input -> ScriptOutput
witnessScriptPubKey ctx =
  fromRight (error "could not decode witness utxo")
    . unmarshal ctx
    . (.script)
    . fromJust
    . (.witnessUtxo)

decodeHexPSBT :: Ctx -> Text -> Either String PSBT
decodeHexPSBT ctx = runGet (getPSBT ctx) . fromJust . decodeHex

decodeHexPSBTM :: (Monad m, MonadFail m) => Ctx -> String -> Text -> m PSBT
decodeHexPSBTM ctx errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT ctx

hexScript :: Text -> ByteString
hexScript =
  either (error "Could not decode script") encodeScript
    . runGetS deserialize
    . fromJust
    . decodeHex
  where
    encodeScript :: Script -> ByteString
    encodeScript = runPutS . serialize

invalidVecTest :: Ctx -> Text -> Assertion
invalidVecTest ctx = assertBool "invalid psbt" . isLeft . decodeHexPSBT ctx

decodeVecTest :: Ctx -> Int -> Text -> Assertion
decodeVecTest ctx i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT ctx

encodeVecTest :: Ctx -> PSBT -> Text -> Assertion
encodeVecTest ctx psbt hex =
  assertEqual
    "encodes correctly"
    ((runPut . putPSBT ctx) psbt)
    ((fromJust . decodeHex) hex)

trivialPSBT :: PSBT
trivialPSBT =
  PSBT
    { unsignedTransaction = Tx {version = 2, inputs = [], outputs = [], witness = [], locktime = 0},
      globalUnknown = UnknownMap mempty,
      inputs = [],
      outputs = []
    }

trivialPSBTHex :: Text
trivialPSBTHex = "70736274ff01000a0200000000000000000000"

nonEmptyTransactionPSBT :: PSBT
nonEmptyTransactionPSBT = emptyPSBT testTx1

verifyNonWitnessPSBT :: Network -> Ctx -> PSBT -> Bool
verifyNonWitnessPSBT net ctx psbt =
  verifyStdTx net ctx (finalTransaction (complete ctx psbt)) sigData
  where
    sigData = inputSigData =<< zip psbt.inputs psbt.unsignedTransaction.inputs
    decodeOutScript = fromRight (error "Could not parse output script") . unmarshal ctx
    inputSigData (input@Input {}, txInput@TxIn {}) =
      map
        (\(TxOut val script) -> (decodeOutScript script, val, txInput.outpoint))
        (fromJust input.nonWitnessUtxo).outputs

unfinalizedPkhPSBT :: Network -> Ctx -> (PrivateKey, PublicKey) -> PSBT
unfinalizedPkhPSBT net ctx (prvKey, pubKey) =
  let PSBT {..} = emptyPSBT currTx
   in PSBT {inputs = [input], ..}
  where
    input =
      emptyInput
        { nonWitnessUtxo = Just prevTx,
          partialSigs = singleton pubKey sig
        }
    currTx =
      unfinalizedTx (txHash prevTx)
    prevTx =
      testUtxo [prevOut]
    prevOutScript =
      addressToScript ctx (pubKeyAddr ctx pubKey)
    prevOut =
      TxOut
        { value = 200000000,
          script = runPutS (serialize prevOutScript)
        }
    h = txSigHash net currTx prevOutScript prevOut.value 0 sigHashAll
    sig =
      encodeTxSig net ctx $
        TxSignature (signHash ctx prvKey.key h) sigHashAll

arbitraryMultiSig :: Ctx -> Gen ([(PrivateKey, PublicKey)], Int)
arbitraryMultiSig ctx = do
  (m, n) <- arbitraryMSParam
  keys <- vectorOf n (arbitraryKeyPair ctx)
  return (keys, m)

unfinalizedMsPSBT :: Network -> Ctx -> ([(PrivateKey, PublicKey)], Int) -> PSBT
unfinalizedMsPSBT net ctx (keys, m) =
  let PSBT {..} = emptyPSBT currTx
   in PSBT {inputs = [input], ..}
  where
    input =
      emptyInput
        { nonWitnessUtxo = Just prevTx,
          partialSigs = sigs,
          inputRedeemScript = Just prevOutScript
        }
    currTx = unfinalizedTx (txHash prevTx)
    prevTx = testUtxo [prevOut]
    prevOutScript = encodeOutput ctx $ PayMulSig (map snd keys) m
    prevOut =
      TxOut
        { value = 200000000,
          script = marshal ctx (toP2SH prevOutScript)
        }
    h = txSigHash net currTx prevOutScript prevOut.value 0 sigHashAll
    sigs = fromList $ map sig keys
    sig (prvKey@PrivateKey {key}, pubKey) =
      let sh = signHash ctx key h
          ts = TxSignature sh sigHashAll
       in (pubKey, encodeTxSig net ctx ts)

unfinalizedTx :: TxHash -> Tx
unfinalizedTx prevHash =
  Tx
    { version = 2,
      inputs =
        [ TxIn
            { outpoint = OutPoint prevHash 0,
              script = "",
              sequence = 4294967294
            }
        ],
      outputs =
        [ TxOut {value = 99999699, script = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"},
          TxOut {value = 100000000, script = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"}
        ],
      witness = [],
      locktime = 1257139
    }

invalidVec :: [Text]
invalidVec =
  [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300",
    "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",
    "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000",
    "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",
    "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000",
    "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",
    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",
    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",
    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",
    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",
    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",
    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",
    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",
    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",
    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",
    "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",
    "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",
    "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"
  ]

validEncodeVec :: [(PSBT, Text)]
validEncodeVec = [(validVec1, validVec1Hex)]

testTx1 :: Tx
testTx1 =
  Tx
    { version = 2,
      inputs =
        [ TxIn
            { outpoint = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0,
              script = "",
              sequence = 4294967294
            }
        ],
      outputs =
        [ TxOut {value = 99999699, script = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"},
          TxOut {value = 100000000, script = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"}
        ],
      witness = [],
      locktime = 1257139
    }

testUtxo :: [TxOut] -> Tx
testUtxo prevOuts =
  Tx
    { version = 1,
      inputs =
        [ TxIn
            { outpoint = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1,
              script = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985",
              sequence = 4294967295
            },
          TxIn
            { outpoint = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1,
              script = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353",
              sequence = 4294967295
            }
        ],
      outputs = prevOuts,
      witness =
        [ [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01",
            fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"
          ],
          [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01",
            fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"
          ]
        ],
      locktime = 0
    }

testUtxo1 :: Tx
testUtxo1 =
  testUtxo
    [ TxOut {value = 200000000, script = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"},
      TxOut {value = 190303501938, script = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"}
    ]

validVec1 :: PSBT
validVec1 =
  let PSBT {..} = emptyPSBT testTx1
   in PSBT {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"

-- Example of a PSBT with a `finalWitnessScript`
validVec7Hex :: Text
validVec7Hex = "70736274ff0100520200000001815dd29e16fd2f567a040ce24f5337fb9cfd0c05bacd8890714a33edc7cbbc920000000000ffffffff0192f1052a01000000160014ef9ade26f63015d57f4ecdb268d1a9b8d6cd8872000000000001008402000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a010000001600145f4ffa19dbbe464561c50fc4d8d8ac0b41009dd20000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90000000001011f00f2052a010000001600145f4ffa19dbbe464561c50fc4d8d8ac0b41009dd201086b02473044022026a9f7afdb0128970bb3577e536ec3d3dc10c1e82650d11c9da1df9003b31d0202202258b11f962f12e0897c642cd6f38a0181db17197f3693a530c9431eb44dde7d0121033dc786e9628bb6c41c08fceb9b37458ad7a95e7e6b04e0bde45b6879398c3ac100220203a6affb58dda998a4ffdce652feb91038fdfc78c748ae687372e11292af8d312d101c4c5bfc00000080000000800100008000"

data ComplexPSBT = ComplexPSBT
  { complexSignedPsbts :: [PSBT],
    complexCombinedPsbt :: PSBT,
    complexCompletePsbt :: PSBT,
    complexFinalTx :: Tx
  }
  deriving (Eq, Show)

parseComplexJSON ctx = withObject "ComplexPSBT" $ \obj -> do
  ComplexPSBT
    <$> sequence
      [ psbtField "miner_psbt" obj,
        psbtField "p2pkh_psbt" obj,
        psbtField "p2sh_ms_1_psbt" obj,
        psbtField "p2sh_ms_2_psbt" obj,
        psbtField "p2sh_pk_psbt" obj,
        psbtField "p2sh_wsh_pk_psbt" obj,
        psbtField "p2sh_wsh_ms_1_psbt" obj,
        psbtField "p2sh_wsh_ms_2_psbt" obj,
        psbtField "p2wpkh_psbt" obj,
        psbtField "p2wsh_pk_psbt" obj,
        psbtField "p2wsh_ms_1_psbt" obj,
        psbtField "p2wsh_ms_2_psbt" obj
      ]
    <*> psbtField "combined_psbt" obj
    <*> psbtField "complete_psbt" obj
    <*> (obj .: "final_tx" >>= parseTx)
  where
    parseTx =
      either fail pure
        . ( decode
              <=< maybe (Left "hex") Right
                . decodeHex
          )
    parsePsbt =
      either fail pure
        . ( runGet (getPSBT ctx)
              <=< first Text.unpack . decodeBase64Untyped
          )
        . encodeUtf8
    psbtField fieldName obj =
      obj .: fieldName >>= parsePsbt