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