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