Main.hs (3602B)
1 module Main where 2 3 import qualified Data.Char as C 4 import qualified Data.ByteString as BS 5 import qualified Data.ByteString.Char8 as B8 6 import qualified Data.ByteString.Bech32 as Bech32 7 import qualified Data.ByteString.Bech32m as Bech32m 8 import qualified Data.ByteString.Base32 as B32 9 import Test.Tasty 10 import qualified Test.Tasty.QuickCheck as Q 11 import qualified Reference.Bech32 as R 12 13 newtype BS = BS BS.ByteString 14 deriving (Eq, Show) 15 16 data ValidInput = ValidInput BS.ByteString BS.ByteString 17 deriving (Eq, Show) 18 19 data InvalidInput = InvalidInput BS.ByteString BS.ByteString 20 deriving (Eq, Show) 21 22 instance Q.Arbitrary ValidInput where 23 arbitrary = do 24 h <- hrp 25 let l = 83 - BS.length h 26 a = l * 5 `quot` 8 27 b <- bytes a 28 pure (ValidInput h b) 29 30 instance Q.Arbitrary InvalidInput where 31 arbitrary = do 32 h <- invalid_hrp 33 let l = 83 - BS.length h 34 a = l * 5 `quot` 8 35 b <- bytes a 36 pure (InvalidInput h b) 37 38 instance Q.Arbitrary BS where 39 arbitrary = do 40 b <- bytes 1024 41 pure (BS b) 42 43 hrp :: Q.Gen BS.ByteString 44 hrp = do 45 l <- Q.chooseInt (1, 83) 46 v <- Q.vectorOf l (Q.choose (33, 126)) 47 pure (B8.map C.toLower (BS.pack v)) 48 49 invalid_hrp :: Q.Gen BS.ByteString 50 invalid_hrp = do 51 l <- Q.oneof [pure 0, Q.chooseInt (84, 100)] 52 v <- Q.vectorOf l (Q.oneof [Q.choose (0, 32), Q.choose (127, 255)]) 53 pure (B8.map C.toLower (BS.pack v)) 54 55 bytes :: Int -> Q.Gen BS.ByteString 56 bytes k = do 57 l <- Q.chooseInt (0, k) 58 v <- Q.vectorOf l Q.arbitrary 59 pure (BS.pack v) 60 61 matches_reference :: ValidInput -> Bool 62 matches_reference (ValidInput h b) = 63 let ref = R.bech32Encode h (R.toBase32 (BS.unpack b)) 64 our = Bech32.encode h b 65 in ref == our 66 67 bech32_decode_inverts_encode :: ValidInput -> Bool 68 bech32_decode_inverts_encode (ValidInput h b) = case Bech32.encode h b of 69 Nothing -> error "generated faulty input" 70 Just enc -> case Bech32.decode enc of 71 Nothing -> False 72 Just (h', dat) -> h == h' && b == dat 73 74 bech32m_decode_inverts_encode :: ValidInput -> Bool 75 bech32m_decode_inverts_encode (ValidInput h b) = case Bech32m.encode h b of 76 Nothing -> error "generated faulty input" 77 Just enc -> case Bech32m.decode enc of 78 Nothing -> False 79 Just (h', dat) -> h == h' && b == dat 80 81 base32_decode_inverts_encode :: BS -> Bool 82 base32_decode_inverts_encode (BS bs) = case B32.decode (B32.encode bs) of 83 Nothing -> False 84 Just b -> b == bs 85 86 bech32_invalid_input_fails_encode :: InvalidInput -> Bool 87 bech32_invalid_input_fails_encode (InvalidInput h b) = 88 case Bech32.encode h b of 89 Nothing -> True 90 Just _ -> False 91 92 bech32m_invalid_input_fails_encode :: InvalidInput -> Bool 93 bech32m_invalid_input_fails_encode (InvalidInput h b) = 94 case Bech32m.encode h b of 95 Nothing -> True 96 Just _ -> False 97 98 main :: IO () 99 main = defaultMain $ testGroup "ppad-bech32" [ 100 testGroup "base32" [ 101 Q.testProperty "decode . encode ~ id" $ 102 Q.withMaxSuccess 1000 base32_decode_inverts_encode 103 ] 104 , testGroup "bech32" [ 105 Q.testProperty "Bech32.encode ~ R.bech32Encode" $ 106 Q.withMaxSuccess 1000 matches_reference 107 , Q.testProperty "decode . encode ~ id" $ 108 Q.withMaxSuccess 1000 bech32_decode_inverts_encode 109 , Q.testProperty "invalid bech32 input fails to encode" $ 110 Q.withMaxSuccess 1000 bech32_invalid_input_fails_encode 111 ] 112 , testGroup "bech32m" [ 113 Q.testProperty "decode . encode ~ id" $ 114 Q.withMaxSuccess 1000 bech32m_decode_inverts_encode 115 , Q.testProperty "invalid bech32m input fails to encode" $ 116 Q.withMaxSuccess 1000 bech32_invalid_input_fails_encode 117 ] 118 ] 119