{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-} module Network.Haskoin.Transaction.Units (tests, satoshiCoreTxTests) where import Test.HUnit (Assertion, assertBool) import Test.Framework (Test, testGroup) import Test.Framework.Providers.HUnit (testCase) import Data.Word (Word32, Word64) import Data.Maybe (fromJust) import Data.Binary.Get (getWord32le) import Data.Binary.Put (putWord32le) import Data.ByteString (ByteString) import qualified Data.ByteString as BS (reverse) import qualified Data.ByteString.Lazy as LBS import Safe (readMay) import GHC.Exts( IsString(..) ) import qualified Data.Aeson as Aeson import qualified Data.Aeson.Types as Aeson.Types import qualified Data.Vector as V import Data.String.Conversions (convertString) import Data.List (groupBy) import qualified Data.Either import Network.Haskoin.Transaction import Network.Haskoin.Script import Network.Haskoin.Util tests :: [Test] tests = [ testGroup "Computing TxID from Tx" ( map mapTxIDVec $ zip txIDVec [0..] ) , testGroup "Build PKHash Transaction (generated from bitcoind)" ( map mapPKHashVec $ zip pkHashVec [0..] ) , testGroup "Verify transaction (bitcoind /test/data/tx_valid.json)" ( map mapVerifyVec $ zip verifyVec [0..] ) , testCase "" tEncodeSatoshiCoreScriptPubKey ] mapTxIDVec :: ((ByteString, ByteString), Int) -> Test.Framework.Test mapTxIDVec (v,i) = testCase name $ runTxIDVec v where name = "Compute TxID " ++ (show i) runTxIDVec :: (ByteString, ByteString) -> Assertion runTxIDVec (tid,tx) = assertBool "TxID" $ (txHashToHex $ txHash txBS) == tid where txBS = decode' $ fromJust $ decodeHex tx txIDVec :: [(ByteString, ByteString)] txIDVec = [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63" , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000" ) , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73" , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000" ) , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb" , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000" ) , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae" , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000" ) ] mapPKHashVec :: (([(ByteString, Word32)], [(ByteString, Word64)], ByteString), Int) -> Test.Framework.Test mapPKHashVec (v, i) = testCase name $ runPKHashVec v where name = "Build PKHash Tx " ++ (show i) runPKHashVec :: ([(ByteString, Word32)], [(ByteString, Word64)], ByteString) -> Assertion runPKHashVec (xs, ys, res) = assertBool "Build PKHash Tx" $ (encodeHex $ encode' tx) == res where tx = fromRight $ buildAddrTx (map f xs) ys f (tid,ix) = OutPoint (fromJust $ hexToTxHash tid) ix mapVerifyVec :: (SatoshiCoreTxTest, Int) -> Test.Framework.Test mapVerifyVec (v, i) = testCase name $ runVerifyVec v i where name = "Verify Tx " ++ (show i) ++ ", about: " ++ (satCoreTxTestDescription $ v) runVerifyVec :: SatoshiCoreTxTest -> Int -> Assertion runVerifyVec (SatoshiCoreTxTest description is bsTx) i = assertBool name $ verifyStdTx tx outputsAndOutpoints where name = " > Verify transaction " ++ (show i) ++ "bsTx: " ++ convertString bsTx tx :: Tx tx = decode' . fromJust . decodeHex $ bsTx outputsAndOutpoints :: [(ScriptOutput, OutPoint)] outputsAndOutpoints = map f is f (SatoshiCoreTxTestInput bsOutputHash bsOutputIndex bsOutputScriptPubKey) = let s :: ScriptOutput s = either (\e -> error $ "could not decode: " ++ convertString bsOutputScriptPubKey) id . decodeOutputBS . fromJust . decodeHex $ bsOutputScriptPubKey op :: OutPoint op = OutPoint (decode' . BS.reverse . fromJust . decodeHex $ bsOutputHash) (runGet' getWord32le . fromJust . decodeHex $ bsOutputIndex) in (s, op) -- These test vectors have been generated from bitcoind raw transaction api pkHashVec :: [([(ByteString, Word32)], [(ByteString, Word64)], ByteString)] pkHashVec = [ ( [("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",14)] , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",90000000)] , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000" ) , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0) , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647) ] , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1) , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000) ] , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000" ) , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0) , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647) ] , [] , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0000000000" ) , ( [] , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1) , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000) ] , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000" ) ] data SatoshiCoreTxTest = SatoshiCoreTxTest { satCoreTxTestDescription :: String, satCoreTxTestInputs :: [SatoshiCoreTxTestInput], satCoreTxTestSerTx :: ByteString} deriving (Read,Show) data SatoshiCoreTxTestInput = SatoshiCoreTxTestInput { satCoreTxTestPrevoutHash :: ByteString, satCoreTxTestPrevoutIndex :: ByteString, satCoreTxTestPrevoutScriptPubKey :: ByteString} deriving (Read,Show) {- Test vectors from bitcoind -} -- github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json -- [[(prevout hash, prevout index, prevout scriptPubKey)], serialized tx], verifyVec :: [SatoshiCoreTxTest] verifyVec = let toSatCoreTest (is, sertx, description) = SatoshiCoreTxTest description (map toInputs is) sertx toInputs (a,b,c) = SatoshiCoreTxTestInput a b c in map toSatCoreTest [ ( [ ( "60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1" , "00000000" , "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae" ) ] , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000" , "It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts" ) , ( [ ( "60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1" , "00000000" , "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae" ) ] , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004A0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000" , "It has an arbitrary extra byte stuffed into the signature at pos length - 2" ) , ( [ ( "406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602" , "00000000" , "76a914dc44b1164188067c3a32d4780f5996fa14a4f2d988ac" ) ] , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000" , "it is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)" ) , ( [ ( "b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d" , "00000000" , "76a914bef80ecf3a44500fda1bc92176e442891662aed288ac" ) , ( "b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21" , "01000000" , "76a914bef80ecf3a44500fda1bc92176e442891662aed288ac" ) ] , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000" , "It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation" ) , ( [ ( "0000000000000000000000000000000000000000000000000000000000000100" , "00000000" , "76a914e52b482f2faa8ecbf0db344f93c84ac908557f3388ac" ) , ( "0000000000000000000000000000000000000000000000000000000000000200" , "00000000" , "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac" ) ] , "01000000020002000000000000000000000000000000000000000000000000000000000000000000006a47304402200469f169b8091cd18a2770136be7411f079b3ac2b5c199885eb66a80aa3ed75002201fa89f3e6f80974e1b3474e70a0fbe907c766137ff231e4dd05a555d8544536701210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000" , "-- It results in signing the constant 1, instead of something generated based on the transaction,\ \-- when the input doing the signing has an index greater than the maximum output index" ) , ( [ ( "0000000000000000000000000000000000000000000000000000000000000100" , "00000000" , "a9148febbed40483661de6958d957412f82deed8e2f787" ) ] , "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000" , "A valid P2SH Transaction using the standard transaction type put forth in BIP 16" ) , ( [ ( "0000000000000000000000000000000000000000000000000000000000000100" , "00000000" , "a91432afac281462b822adbec5094b8d4d337dd5bd6a87" ) ] , "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000" , "MAX_MONEY output" ) , ( [ ( "0000000000000000000000000000000000000000000000000000000000000100" , "00000000" , "a914b558cbf4930954aa6a344363a15668d7477ae71687" ) ] , "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000" , "MAX_MONEY output + 0 output" ) , ( [ ( "0000000000000000000000000000000000000000000000000000000000000100" , "00000000" , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac" ) , ( "0000000000000000000000000000000000000000000000000000000000000200" , "00000000" , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac" ) ] , "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000" , "Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY" ) , ( [ ( "0000000000000000000000000000000000000000000000000000000000000100" , "00000000" , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac" ) , ( "0000000000000000000000000000000000000000000000000000000000000200" , "00000000" , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac" ) ] , "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000" , "Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed" ) , ( [ ( "63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70" , "00000000" , "76a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88ac" ) , ( "04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d" , "01000000" , "76a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88ac" ) , ( "ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f" , "01000000" , "76a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88ac" ) ] , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000" , "several SIGHASH_SINGLE signatures" ) ] tEncodeSatoshiCoreScriptPubKey :: Assertion tEncodeSatoshiCoreScriptPubKey = assertBool "tEncodeSatoshiCoreScriptPubKey" $ t1BsOutputScriptPubKey == encodeSatoshiCoreScriptPubKey t1SatoshiCoreJsonScriptPubKey where t1BsOutputScriptPubKey :: ByteString t1BsOutputScriptPubKey = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae" t1SatoshiCoreJsonScriptPubKey :: String t1SatoshiCoreJsonScriptPubKey = "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG" encodeSatoshiCoreScriptPubKey :: String -> ByteString encodeSatoshiCoreScriptPubKey = mconcat . map encodeSatoshiCoreScriptPiece . words where encodeSatoshiCoreScriptPiece :: String -> ByteString encodeSatoshiCoreScriptPiece s = case (readMay ("OP_" ++ s) :: Maybe ScriptOp) of Just op -> encodeHex . encode' $ op Nothing -> case (take 2 s) of "OP" -> encodeHex . encode' . (read :: String -> ScriptOp) $ s "0x" -> ( fromString . drop 2 :: String -> ByteString) $ s _ -> case (readMay s :: Maybe Int) of -- can we get rid of this case now? Just i -> encodeHex . encode' . intToScriptOp $ i Nothing -> error $ "encodeSatoshiCoreScriptPubKey: " ++ s satoshiCoreTxTests :: IO [Test] satoshiCoreTxTests = do txVec <- satoshiCoreTxVec return $ [ testGroup "Verify transaction (bitcoind /test/data/tx_valid.json) (using copied source json)" ( map mapVerifyVec . filter isCurrentlyPassing $ zip txVec [0..] ) ] where passingTests = [0..5] ++ [8] ++ [11..13] ++ [16..18] ++ [20] ++ [52] isCurrentlyPassing (_, testNum) = elem testNum passingTests type TestComment = String satoshiCoreTxVec :: IO [SatoshiCoreTxTest] satoshiCoreTxVec = do tx_validBS <- LBS.readFile "tests/data/tx_valid.json" let testsAndComments = maybe (error $ "satoshiCoreTxVec, couldn't decode json") id . Aeson.decode $ tx_validBS return $ case testsAndComments of (Aeson.Array arr) -> let testsOrComments = map toTestOrComment . V.toList $ arr in processTestsAndComments testsOrComments _ -> error $ "satoshiCoreTxVec, testsAndComments not an array" where processTestsAndComments :: [Either TestComment SatoshiCoreTxTest] -> [SatoshiCoreTxTest] processTestsAndComments testOrComments = -- ghetto parser, because this older version of ootb aeson parser isn't parsec based -- to do ideally soon, upgrade aeson, use aeson/attoparsec, should be much cleaner let grouper = Data.List.groupBy (\x y -> (Data.Either.isLeft x && Data.Either.isLeft y) || (Data.Either.isRight x && Data.Either.isRight y)) takePairs (a:b:xs) = (a,b):takePairs xs takePairs _ = [] -- ugh, wish we were using a real parser. includeDescriptions (descriptionLines,tests') = map updateDescription tests' where updateDescription (Right (SatoshiCoreTxTest _ inputs ser)) = SatoshiCoreTxTest description inputs ser updateDescription e = error $ "updateDescription: " ++ show e description = unwords . map fromLeft $ descriptionLines in concat . map includeDescriptions . takePairs . grouper $ testOrComments toTestOrComment :: Aeson.Value -> (Either TestComment SatoshiCoreTxTest) toTestOrComment testVectorOrComment = case testVectorOrComment of (Aeson.Types.Array arr) -> case (V.length arr) of 1 -> let comment = arr V.! 0 in case comment of Aeson.Types.String txt -> Left . convertString $ txt _ -> error $ "toTestOrComment, comment not text" 3 -> let inputs = case ( arr V.! 0 ) of (Aeson.Array inputsV) -> let toInput ( Aeson.Array oneInputV ) = let hash = case oneInputV V.! 0 of Aeson.String txt -> convertString txt _ -> error "processItem, hash not a string" index = case oneInputV V.! 01 of Aeson.Number n -> encodeHex . runPut' . putWord32le . floor $ n _ -> error "processItem, n not a number" pubkey = case oneInputV V.! 2 of Aeson.String txt -> encodeSatoshiCoreScriptPubKey . convertString $ txt _ -> error "processItem, pubkey not a string" in SatoshiCoreTxTestInput hash index pubkey toInput _ = error "processItem, oneInputV not an array" in map toInput . V.toList $ inputsV _ -> error "inputs not an array" tx = let txVal = arr V.! 1 in case txVal of Aeson.Types.String txt -> convertString txt _ -> error $ "toTestOrComment, tx, not text" -- flags -- v V.! 2 -- ignored for now? in Right $ SatoshiCoreTxTest "" inputs tx i -> error $ "toTestOrComment, bad length: " ++ show i _ -> error "testVectorOrComment is not an array"