Main.hs (4110B)
1 {-# LANGUAGE LambdaCase #-} 2 {-# LANGUAGE OverloadedStrings #-} 3 {-# LANGUAGE RecordWildCards #-} 4 5 module Main where 6 7 import Data.Aeson ((.:)) 8 import qualified Data.Aeson as A 9 import qualified Data.ByteString as BS 10 import qualified Data.ByteString.Base16 as B16 11 import qualified Data.ByteString.Base58 as B58 12 import qualified Data.ByteString.Base58Check as B58Check 13 import qualified Data.Text.Encoding as TE 14 import qualified Data.Text.IO as TIO 15 import Test.Tasty 16 import Test.Tasty.HUnit 17 import qualified Test.Tasty.QuickCheck as Q 18 19 data Valid_Base58Check = Valid_Base58Check { 20 vc_string :: !BS.ByteString 21 , vc_payload :: !BS.ByteString 22 } deriving Show 23 24 instance A.FromJSON Valid_Base58Check where 25 parseJSON = A.withObject "Valid_Base58Check" $ \m -> Valid_Base58Check 26 <$> fmap TE.encodeUtf8 (m .: "string") 27 <*> fmap (B16.decodeLenient . TE.encodeUtf8) (m .: "payload") 28 29 data Invalid_Base58Check = Invalid_Base58Check { 30 ic_string :: !BS.ByteString 31 } deriving Show 32 33 instance A.FromJSON Invalid_Base58Check where 34 parseJSON = A.withObject "Invalid_Base58Check" $ \m -> Invalid_Base58Check 35 <$> fmap TE.encodeUtf8 (m .: "string") 36 37 data Base58Check = Base58Check { 38 b58c_valid :: ![Valid_Base58Check] 39 , b58c_invalid :: ![Invalid_Base58Check] 40 } deriving Show 41 42 instance A.FromJSON Base58Check where 43 parseJSON = A.withObject "Base58Check" $ \m -> Base58Check 44 <$> (m .: "valid") 45 <*> (m .: "invalid") 46 47 48 execute_base58check :: Base58Check -> TestTree 49 execute_base58check Base58Check {..} = testGroup "base58check" [ 50 testGroup "valid" (fmap execute_valid b58c_valid) 51 , testGroup "invalid" (fmap execute_invalid b58c_invalid) 52 ] 53 where 54 execute_valid Valid_Base58Check {..} = testCase "valid" $ do -- label 55 let enc = B58Check.encode vc_payload 56 assertEqual mempty enc vc_string 57 58 execute_invalid Invalid_Base58Check {..} = testCase "invalid" $ do -- label 59 let dec = B58Check.decode ic_string 60 is_just = \case 61 Nothing -> False 62 Just _ -> True 63 assertBool mempty (not (is_just dec)) 64 65 data Valid_Base58 = Valid_Base58 { 66 vb_decodedHex :: !BS.ByteString 67 , vb_encoded :: !BS.ByteString 68 } deriving Show 69 70 instance A.FromJSON Valid_Base58 where 71 parseJSON = A.withObject "Valid_Base58" $ \m -> Valid_Base58 72 <$> fmap (B16.decodeLenient . TE.encodeUtf8) (m .: "decodedHex") 73 <*> fmap TE.encodeUtf8 (m .: "encoded") 74 75 execute_base58 :: Valid_Base58 -> TestTree -- XX label 76 execute_base58 Valid_Base58 {..} = testCase "base58" $ do 77 let enc = B58.encode vb_decodedHex 78 assertEqual mempty enc vb_encoded 79 80 newtype BS = BS BS.ByteString 81 deriving (Eq, Show) 82 83 bytes :: Int -> Q.Gen BS.ByteString 84 bytes k = do 85 l <- Q.chooseInt (0, k) 86 v <- Q.vectorOf l Q.arbitrary 87 pure (BS.pack v) 88 89 instance Q.Arbitrary BS where 90 arbitrary = do 91 b <- bytes 1024 92 pure (BS b) 93 94 base58_decode_inverts_encode :: BS -> Bool 95 base58_decode_inverts_encode (BS bs) = case B58.decode (B58.encode bs) of 96 Nothing -> False 97 Just b -> b == bs 98 99 base58check_decode_inverts_encode :: BS -> Bool 100 base58check_decode_inverts_encode (BS bs) = 101 case B58Check.decode (B58Check.encode bs) of 102 Nothing -> False 103 Just b -> b == bs 104 105 main :: IO () 106 main = do 107 scure_base58 <- TIO.readFile "etc/base58.json" 108 scure_base58check <- TIO.readFile "etc/base58_check.json" 109 let per = do 110 b0 <- A.decodeStrictText scure_base58 :: Maybe [Valid_Base58] 111 b1 <- A.decodeStrictText scure_base58check :: Maybe Base58Check 112 pure (b0, b1) 113 case per of 114 Nothing -> error "couldn't parse vectors" 115 Just (b58, b58c) -> defaultMain $ testGroup "ppad-base58" [ 116 testGroup "unit tests" [ 117 testGroup "base58" (fmap execute_base58 b58) 118 , execute_base58check b58c 119 ] 120 , testGroup "property tests" [ 121 Q.testProperty "(base58) decode . encode ~ id" $ 122 Q.withMaxSuccess 250 base58_decode_inverts_encode 123 , Q.testProperty "(base58check) decode . encode ~ id" $ 124 Q.withMaxSuccess 250 base58check_decode_inverts_encode 125 ] 126 ] 127