multiaddr 0.2.0 → 0.3.0
raw patch · 3 files changed
+184/−172 lines, 3 filesdep ~multiaddr
Dependency ranges changed: multiaddr
Files
- multiaddr.cabal +2/−2
- src/Network/Multiaddr.hs +158/−147
- test/test.hs +24/−23
multiaddr.cabal view
@@ -1,5 +1,5 @@ name: multiaddr-version: 0.2.0+version: 0.3.0 cabal-version: >=1.10 build-type: Simple license: BSD3@@ -44,7 +44,7 @@ main-is: test.hs build-depends: base >=4.7 && <5,- multiaddr >=0.2.0 && <0.3,+ multiaddr >=0.3.0 && <0.4, tasty >=0.11.0.2 && <0.12, tasty-hunit >=0.9.2 && <0.10, tasty-quickcheck >=0.8.4 && <0.9,
src/Network/Multiaddr.hs view
@@ -32,21 +32,23 @@ , hasIPv6 , hasUDP , hasTCP+ , hasUDT+ , hasUTP , hasDCCP , hasSCTP , hasIPFS - -- * AddrPart type- , AddrPart(..)+ -- * MultiaddrPart type+ , MultiaddrPart(..) -- * IPv4 type- , IPv4(..)- , readIPv4+ , IPv4Addr(..)+ , readIPv4Addr , fromBytes -- * IPv6 type- , IPv6(..)- , readIPv6+ , IPv6Addr(..)+ , readIPv6Addr , fromPieces ) where @@ -80,35 +82,35 @@ import Data.Serialize.Varint -- | An IPv4 address.-data IPv4 = IPv4 {-# UNPACK #-} !Word32+data IPv4Addr = IPv4Addr {-# UNPACK #-} !Word32 deriving (Eq, Ord, Bounded, Generic, Typeable) -instance Hashable IPv4+instance Hashable IPv4Addr -instance Serialize IPv4 where- get = IPv4 <$> get- put (IPv4 w) = put w+instance Serialize IPv4Addr where+ get = IPv4Addr <$> get+ put (IPv4Addr w) = put w -- | An IPv6 address.-data IPv6 = IPv6+data IPv6Addr = IPv6Addr {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 {-# UNPACK #-} !Word32 deriving (Eq, Ord, Bounded, Generic, Typeable) -instance Hashable IPv6+instance Hashable IPv6Addr -instance Serialize IPv6 where- get = IPv6 <$> get <*> get <*> get <*> get- put (IPv6 a b c d) = put a >> put b >> put c >> put d+instance Serialize IPv6Addr where+ get = IPv6Addr <$> get <*> get <*> get <*> get+ put (IPv6Addr a b c d) = put a >> put b >> put c >> put d -asBytes :: IPv4 -> [Word8]-asBytes (IPv4 ip) =+asBytes :: IPv4Addr -> [Word8]+asBytes (IPv4Addr ip) = map (\n -> fromIntegral (ip `shiftR` n) .&. 0xFF) [24, 16, 8, 0] -asPieces :: IPv6 -> [Word16]-asPieces (IPv6 a b c d) =+asPieces :: IPv6Addr -> [Word16]+asPieces (IPv6Addr a b c d) = map fromIntegral [ a `shiftR` 16 , a .&. 0xFFFF , b `shiftR` 16@@ -119,16 +121,16 @@ , d .&. 0xFFFF ] -fromBytes :: (Word8, Word8, Word8, Word8) -> IPv4-fromBytes (a, b, c, d) = IPv4 ( shiftL (fromIntegral a) 24- `xor` shiftL (fromIntegral b) 16- `xor` shiftL (fromIntegral c) 8- `xor` fromIntegral d)+fromBytes :: (Word8, Word8, Word8, Word8) -> IPv4Addr+fromBytes (a, b, c, d) = IPv4Addr ( shiftL (fromIntegral a) 24+ `xor` shiftL (fromIntegral b) 16+ `xor` shiftL (fromIntegral c) 8+ `xor` fromIntegral d) fromPieces :: (Word16, Word16, Word16, Word16, Word16, Word16, Word16, Word16)- -> IPv6+ -> IPv6Addr fromPieces (a, b, c, d, e, f, g, h) =- IPv6 (word a b) (word c d) (word e f) (word g h)+ IPv6Addr (word a b) (word c d) (word e f) (word g h) where word x y = (fromIntegral x `shiftL` 16) `xor` fromIntegral y word8P :: Parser Word8@@ -137,8 +139,8 @@ guard (w >= 0 && w <= 255) pure (fromIntegral w) -ipv4P :: Parser IPv4-ipv4P = do+ipv4AddrP :: Parser IPv4Addr+ipv4AddrP = do a <- word8P <* char '.' b <- word8P <* char '.' c <- word8P <* char '.'@@ -146,8 +148,8 @@ pure (fromBytes (a, b, c, d)) -- | Try to read an IPv4 address (e.g. @"192.168.1.2"@)-readIPv4 :: Text -> Maybe IPv4-readIPv4 = hush . parseOnly (ipv4P <* endOfInput)+readIPv4Addr :: Text -> Maybe IPv4Addr+readIPv4Addr = hush . parseOnly (ipv4AddrP <* endOfInput) word16P :: Parser Word16 word16P = do@@ -155,8 +157,8 @@ guard (w >= 0 && w <= 65535) pure (fromIntegral w) -ipv6P :: Parser IPv6-ipv6P = do+ipv6AddrP :: Parser IPv6Addr+ipv6AddrP = do heads <- sepBy' word16P (char ':') _ <- option ':' (char ':' *> char ':') tails <- sepBy' word16P (char ':')@@ -170,18 +172,19 @@ -- | Try to read an IPv6 address -- (e.g. @"2001:db8:85a3:8d3:1319:8a2e:370:7348"@)-readIPv6 :: Text -> Maybe IPv6-readIPv6 = hush . parseOnly (ipv6P <* endOfInput)+readIPv6Addr :: Text -> Maybe IPv6Addr+readIPv6Addr = hush . parseOnly (ipv6AddrP <* endOfInput) -ipv4B :: IPv4 -> Builder-ipv4B = mconcat . intersperse (singleton '.') . map Builder.decimal . asBytes+ipv4AddrB :: IPv4Addr -> Builder+ipv4AddrB =+ mconcat . intersperse (singleton '.') . map Builder.decimal . asBytes {- All we need for ipv6B to pass LiquidHaskell -} {-@ assert scanl' :: (b -> a -> b) -> b -> xs:[a] -> {v : [b] | len(v) = 1 + len(xs) } @-} -ipv6B :: IPv6 -> Builder-ipv6B ip =+ipv6AddrB :: IPv6Addr -> Builder+ipv6AddrB ip = if maxRun <= 1 then trans pieces else trans start <> fromText "::" <> trans end@@ -197,76 +200,76 @@ class TextAddr a where toText :: a -> Text -instance TextAddr IPv4 where- toText = toStrict . toLazyText . ipv4B+instance TextAddr IPv4Addr where+ toText = toStrict . toLazyText . ipv4AddrB -- | RFC 5952 Compliant-instance TextAddr IPv6 where- toText = toStrict . toLazyText . ipv6B+instance TextAddr IPv6Addr where+ toText = toStrict . toLazyText . ipv6AddrB -instance Show IPv4 where+instance Show IPv4Addr where show = T.unpack . toText -instance Show IPv6 where+instance Show IPv6Addr where show = T.unpack . toText -- | An individual component of a multiaddr.-data AddrPart = IPv4Part !IPv4- | IPv6Part !IPv6- | UDPPart !Word16- | TCPPart !Word16- | DCCPPart !Word16- | SCTPPart !Word16- | IPFSPart !ByteString- | UDTPart- | UTPPart- | HTTPPart- | HTTPSPart+data MultiaddrPart = IPv4 !IPv4Addr+ | IPv6 !IPv6Addr+ | UDP !Word16+ | TCP !Word16+ | DCCP !Word16+ | SCTP !Word16+ | IPFS !ByteString+ | UDT+ | UTP+ | HTTP+ | HTTPS deriving (Eq, Generic, Typeable) -instance Hashable AddrPart+instance Hashable MultiaddrPart -instance Serialize AddrPart where- put (IPv4Part ip) = put (4 :: Varint Word32) >> put ip- put (IPv6Part ip) = put (41 :: Varint Word32) >> put ip- put (UDPPart port) = put (17 :: Varint Word32) >> put port- put (TCPPart port) = put (6 :: Varint Word32) >> put port- put (DCCPPart port) = put (33 :: Varint Word32) >> put port- put (SCTPPart port) = put (132 :: Varint Word32) >> put port- put (IPFSPart addr) = do+instance Serialize MultiaddrPart where+ put (IPv4 ip) = put (4 :: Varint Word32) >> put ip+ put (IPv6 ip) = put (41 :: Varint Word32) >> put ip+ put (UDP port) = put (17 :: Varint Word32) >> put port+ put (TCP port) = put (6 :: Varint Word32) >> put port+ put (DCCP port) = put (33 :: Varint Word32) >> put port+ put (SCTP port) = put (132 :: Varint Word32) >> put port+ put (IPFS addr) = do put (421 :: Varint Word32) case decodeBase58 bitcoinAlphabet addr of Nothing -> fail "invalid base58" Just bytes -> do put (fromIntegral (BS.length bytes) :: Varint Word64) mapM_ put (BS.unpack bytes)- put UDTPart = put (301 :: Varint Word32)- put UTPPart = put (302 :: Varint Word32)- put HTTPPart = put (480 :: Varint Word32)- put HTTPSPart = put (443 :: Varint Word32)+ put UDT = put (301 :: Varint Word32)+ put UTP = put (302 :: Varint Word32)+ put HTTP = put (480 :: Varint Word32)+ put HTTPS = put (443 :: Varint Word32) get = do code <- get :: Get (Varint Word32) case code of- 4 -> IPv4Part <$> get- 6 -> TCPPart <$> get- 17 -> UDPPart <$> get- 33 -> DCCPPart <$> get- 41 -> IPv6Part <$> get- 132 -> SCTPPart <$> get+ 4 -> IPv4 <$> get+ 6 -> TCP <$> get+ 17 -> UDP <$> get+ 33 -> DCCP <$> get+ 41 -> IPv6 <$> get+ 132 -> SCTP <$> get 421 -> do len <- get :: Get (Varint Word64) bytes <- getBytes (fromIntegral len) let base58 = encodeBase58 bitcoinAlphabet bytes- pure (IPFSPart base58)- 301 -> pure UDTPart- 302 -> pure UTPPart- 480 -> pure HTTPPart- 443 -> pure HTTPSPart+ pure (IPFS base58)+ 301 -> pure UDT+ 302 -> pure UTP+ 480 -> pure HTTP+ 443 -> pure HTTPS _ -> fail "invalid multiaddr code" -- | A network address.-newtype Multiaddr = Multiaddr { _parts :: [AddrPart] }+newtype Multiaddr = Multiaddr { _parts :: [MultiaddrPart] } deriving (Eq, Monoid, Generic, Typeable) instance Hashable Multiaddr@@ -278,75 +281,75 @@ -- | Get the individual parts of the multiaddr, in order -- (e.g. @["\/ip4\/8.8.8.8", "\/tcp\/80"]@)-parts :: Multiaddr -> [AddrPart]+parts :: Multiaddr -> [MultiaddrPart] parts = _parts -ipv4PartP :: Parser AddrPart-ipv4PartP = IPv4Part <$> (string "/ip4/" *> ipv4P)+ipv4PartP :: Parser MultiaddrPart+ipv4PartP = IPv4 <$> (string "/ip4/" *> ipv4AddrP) -ipv6PartP :: Parser AddrPart-ipv6PartP = IPv6Part <$> (string "/ip6/" *> ipv6P)+ipv6PartP :: Parser MultiaddrPart+ipv6PartP = IPv6 <$> (string "/ip6/" *> ipv6AddrP) -udpPartP :: Parser AddrPart+udpPartP :: Parser MultiaddrPart udpPartP = do _ <- string "/udp/" port <- decimal :: Parser Integer guard (port >= 0 && port <= 65535)- pure (UDPPart (fromIntegral port))+ pure (UDP (fromIntegral port)) -dccpPartP :: Parser AddrPart+dccpPartP :: Parser MultiaddrPart dccpPartP = do _ <- string "/dccp/" port <- decimal :: Parser Integer guard (port >= 0 && port <= 65535)- pure (DCCPPart (fromIntegral port))+ pure (DCCP (fromIntegral port)) -sctpPartP :: Parser AddrPart+sctpPartP :: Parser MultiaddrPart sctpPartP = do _ <- string "/sctp/" port <- decimal :: Parser Integer guard (port >= 0 && port <= 65535)- pure (SCTPPart (fromIntegral port))+ pure (SCTP (fromIntegral port)) -udtPartP :: Parser AddrPart-udtPartP = string "/udt" >> pure UDTPart+udtPartP :: Parser MultiaddrPart+udtPartP = string "/udt" >> pure UDT -utpPartP :: Parser AddrPart-utpPartP = string "/utp" >> pure UTPPart+utpPartP :: Parser MultiaddrPart+utpPartP = string "/utp" >> pure UTP -httpPartP :: Parser AddrPart-httpPartP = string "/http" >> pure HTTPPart+httpPartP :: Parser MultiaddrPart+httpPartP = string "/http" >> pure HTTP -httpsPartP :: Parser AddrPart-httpsPartP = string "/https" >> pure HTTPSPart+httpsPartP :: Parser MultiaddrPart+httpsPartP = string "/https" >> pure HTTPS -tcpPartP :: Parser AddrPart+tcpPartP :: Parser MultiaddrPart tcpPartP = do _ <- string "/tcp/" port <- decimal :: Parser Integer guard (port >= 0 && port <= 65535)- pure (TCPPart (fromIntegral port))+ pure (TCP (fromIntegral port)) -ipfsPartP :: Parser AddrPart+ipfsPartP :: Parser MultiaddrPart ipfsPartP = do _ <- string "/ipfs/" addr <- takeTill (== '/')- pure (IPFSPart (T.encodeUtf8 addr))+ pure (IPFS (T.encodeUtf8 addr)) multiaddrP :: Parser Multiaddr multiaddrP = do _parts <- many' (choice [ ipv4PartP- , ipv6PartP- , ipfsPartP- , udpPartP- , tcpPartP- , dccpPartP- , sctpPartP- , udtPartP- , utpPartP- , httpPartP- , httpsPartP- ])+ , ipv6PartP+ , ipfsPartP+ , udpPartP+ , tcpPartP+ , dccpPartP+ , sctpPartP+ , udtPartP+ , utpPartP+ , httpPartP+ , httpsPartP+ ]) pure Multiaddr{..} -- | Try to read a multiaddr in the standard text format@@ -354,23 +357,23 @@ readMultiaddr :: Text -> Maybe Multiaddr readMultiaddr = hush . parseOnly (multiaddrP <* endOfInput) -addrPartB :: AddrPart -> Builder-addrPartB (IPv4Part ip) = fromText "/ip4/" <> ipv4B ip-addrPartB (IPv6Part ip) = fromText "/ip6/" <> ipv6B ip-addrPartB (UDPPart port) = fromText "/udp/" <> Builder.decimal port-addrPartB (TCPPart port) = fromText "/tcp/" <> Builder.decimal port-addrPartB (DCCPPart port) = fromText "/dccp/" <> Builder.decimal port-addrPartB (SCTPPart port) = fromText "/sctp/" <> Builder.decimal port-addrPartB (IPFSPart addr) = fromText "/ipfs/" <> fromText (T.decodeUtf8 addr)-addrPartB UDTPart = fromText "/udt"-addrPartB UTPPart = fromText "/utp"-addrPartB HTTPPart = fromText "/http"-addrPartB HTTPSPart = fromText "/https"+addrPartB :: MultiaddrPart -> Builder+addrPartB (IPv4 ip) = fromText "/ip4/" <> ipv4AddrB ip+addrPartB (IPv6 ip) = fromText "/ip6/" <> ipv6AddrB ip+addrPartB (UDP port) = fromText "/udp/" <> Builder.decimal port+addrPartB (TCP port) = fromText "/tcp/" <> Builder.decimal port+addrPartB (DCCP port) = fromText "/dccp/" <> Builder.decimal port+addrPartB (SCTP port) = fromText "/sctp/" <> Builder.decimal port+addrPartB (IPFS addr) = fromText "/ipfs/" <> fromText (T.decodeUtf8 addr)+addrPartB UDT = fromText "/udt"+addrPartB UTP = fromText "/utp"+addrPartB HTTP = fromText "/http"+addrPartB HTTPS = fromText "/https" -instance TextAddr AddrPart where+instance TextAddr MultiaddrPart where toText = toStrict . toLazyText . addrPartB -instance Show AddrPart where+instance Show MultiaddrPart where show = T.unpack . toText multiaddrB :: Multiaddr -> Builder@@ -392,31 +395,39 @@ -- | Does the multiaddr contain an IPv4 part? hasIPv4 :: Multiaddr -> Bool-hasIPv4 (Multiaddr ps) = any (\case IPv4Part _ -> True; _ -> False) ps+hasIPv4 (Multiaddr ps) = any (\case IPv4 _ -> True; _ -> False) ps -- | Does the multiaddr contain an IPv6 part? hasIPv6 :: Multiaddr -> Bool-hasIPv6 (Multiaddr ps) = any (\case IPv6Part _ -> True; _ -> False) ps+hasIPv6 (Multiaddr ps) = any (\case IPv6 _ -> True; _ -> False) ps -- | Does the multiaddr contain a TCP part? hasTCP :: Multiaddr -> Bool-hasTCP (Multiaddr ps) = any (\case TCPPart _ -> True; _ -> False) ps+hasTCP (Multiaddr ps) = any (\case TCP _ -> True; _ -> False) ps -- | Does the multiaddr contain a UDP part? hasUDP :: Multiaddr -> Bool-hasUDP (Multiaddr ps) = any (\case UDPPart _ -> True; _ -> False) ps+hasUDP (Multiaddr ps) = any (\case UDP _ -> True; _ -> False) ps +-- | Does the multiaddr contain a UDT part?+hasUDT :: Multiaddr -> Bool+hasUDT (Multiaddr ps) = any (== UDT) ps++-- | Does the multiaddr contain a UTP part?+hasUTP :: Multiaddr -> Bool+hasUTP (Multiaddr ps) = any (== UTP) ps+ -- | Does the multiaddr contain a DCCP part? hasDCCP :: Multiaddr -> Bool-hasDCCP (Multiaddr ps) = any (\case DCCPPart _ -> True; _ -> False) ps+hasDCCP (Multiaddr ps) = any (\case DCCP _ -> True; _ -> False) ps -- | Does the multiaddr contain an SCTP part? hasSCTP :: Multiaddr -> Bool-hasSCTP (Multiaddr ps) = any (\case SCTPPart _ -> True; _ -> False) ps+hasSCTP (Multiaddr ps) = any (\case SCTP _ -> True; _ -> False) ps -- | Does the multiaddr contain an IPFS part? hasIPFS :: Multiaddr -> Bool-hasIPFS (Multiaddr ps) = any (\case IPFSPart _ -> True; _ -> False) ps+hasIPFS (Multiaddr ps) = any (\case IPFS _ -> True; _ -> False) ps -- | Append two multiaddrs (alias for @(<>)@) encapsulate :: Multiaddr -> Multiaddr -> Multiaddr@@ -426,14 +437,14 @@ -- Protocols can appear more than once if they are repeated in the multiaddr. protocolNames :: Multiaddr -> [Text] protocolNames (Multiaddr ms) = map protoName ms- where protoName (IPv4Part _) = "ip4"- protoName (IPv6Part _) = "ip6"- protoName (UDPPart _) = "udp"- protoName (TCPPart _) = "tcp"- protoName (DCCPPart _) = "dccp"- protoName (SCTPPart _) = "sctp"- protoName (IPFSPart _) = "ipfs"- protoName UDTPart = "udt"- protoName UTPPart = "utp"- protoName HTTPPart = "http"- protoName HTTPSPart = "https"+ where protoName (IPv4 _) = "ip4"+ protoName (IPv6 _) = "ip6"+ protoName (UDP _) = "udp"+ protoName (TCP _) = "tcp"+ protoName (DCCP _) = "dccp"+ protoName (SCTP _) = "sctp"+ protoName (IPFS _) = "ipfs"+ protoName UDT = "udt"+ protoName UTP = "utp"+ protoName HTTP = "http"+ protoName HTTPS = "https"
test/test.hs view
@@ -8,10 +8,10 @@ import Network.Multiaddr -instance QC.Arbitrary IPv4 where+instance QC.Arbitrary IPv4Addr where arbitrary = fromBytes <$> QC.arbitrary -instance QC.Arbitrary IPv6 where+instance QC.Arbitrary IPv6Addr where arbitrary = do (a, b, c, d) <- QC.arbitrary (e, f, g, h) <- QC.arbitrary@@ -20,10 +20,10 @@ ipv4Tests :: TestTree ipv4Tests = testGroup "IPv4" [ QC.testProperty "round trips" $- \ipv4 -> readIPv4 (toText (ipv4 :: IPv4)) == Just ipv4+ \ipv4 -> readIPv4Addr (toText (ipv4 :: IPv4Addr)) == Just ipv4 , testCase "parses and formats sample IPs" $- mapM_ (\t -> toText (fromJust (readIPv4 t)) @?= t)+ mapM_ (\t -> toText (fromJust (readIPv4Addr t)) @?= t) [ "0.0.0.0" , "0.0.0.1" , "1.1.1.1"@@ -33,7 +33,7 @@ , "255.255.255.255" ] - , testCase "fails to parse bad IPs" $ mapM_ (\t -> readIPv4 t @?= Nothing)+ , testCase "fails to parse bad IPs" $ mapM_ (\t -> readIPv4Addr t @?= Nothing) [ "" , "foo" , "..."@@ -46,28 +46,28 @@ ] , testCase "Bounded instance" $ do- (minBound :: IPv4) @?= IPv4 0- (maxBound :: IPv4) @?= IPv4 0xFFFFFFFF+ (minBound :: IPv4Addr) @?= IPv4Addr 0+ (maxBound :: IPv4Addr) @?= IPv4Addr 0xFFFFFFFF ] ipv6Tests :: TestTree ipv6Tests = testGroup "IPv6" [ testCase "parses sample IPs" $ do- readIPv6 "::" @?= Just (IPv6 0 0 0 0)- readIPv6 "::1" @?= Just (IPv6 0 0 0 1)- readIPv6 "::2" @?= Just (IPv6 0 0 0 2)- readIPv6 "::ffff" @?= Just (IPv6 0 0 0 0xFFFF)- readIPv6 "::ff:ffff" @?= Just (IPv6 0 0 0 0xFFFFFF)- readIPv6 "::ffff:ffff" @?= Just (IPv6 0 0 0 0xFFFFFFFF)- readIPv6 "2001:0DB8:AC10:FE01:0000:0000:0000:0000" @?=- Just (IPv6 0x20010DB8 0xAC10FE01 0 0)- readIPv6 "2001:0DB8:AC10:FE01:0:0:0:0" @?=- Just (IPv6 0x20010DB8 0xAC10FE01 0 0)- readIPv6 "2001:db8:ac10:fe01::" @?=- Just (IPv6 0x20010DB8 0xAC10FE01 0 0)+ readIPv6Addr "::" @?= Just (IPv6Addr 0 0 0 0)+ readIPv6Addr "::1" @?= Just (IPv6Addr 0 0 0 1)+ readIPv6Addr "::2" @?= Just (IPv6Addr 0 0 0 2)+ readIPv6Addr "::ffff" @?= Just (IPv6Addr 0 0 0 0xFFFF)+ readIPv6Addr "::ff:ffff" @?= Just (IPv6Addr 0 0 0 0xFFFFFF)+ readIPv6Addr "::ffff:ffff" @?= Just (IPv6Addr 0 0 0 0xFFFFFFFF)+ readIPv6Addr "2001:0DB8:AC10:FE01:0000:0000:0000:0000" @?=+ Just (IPv6Addr 0x20010DB8 0xAC10FE01 0 0)+ readIPv6Addr "2001:0DB8:AC10:FE01:0:0:0:0" @?=+ Just (IPv6Addr 0x20010DB8 0xAC10FE01 0 0)+ readIPv6Addr "2001:db8:ac10:fe01::" @?=+ Just (IPv6Addr 0x20010DB8 0xAC10FE01 0 0) , testCase "round trips RFC 5952 IPs" $- mapM_ (\t -> toText (fromJust (readIPv6 t)) @?= t)+ mapM_ (\t -> toText (fromJust (readIPv6Addr t)) @?= t) [ "::" , "::1" , "::1:1"@@ -78,11 +78,12 @@ ] , QC.testProperty "round trips arbitrary IPs" $- \ip -> readIPv6 (toText (ip :: IPv6)) == Just ip+ \ip -> readIPv6Addr (toText (ip :: IPv6Addr)) == Just ip , testCase "Bounded instance" $ do- (minBound :: IPv6) @?= IPv6 0 0 0 0- (maxBound :: IPv6) @?= IPv6 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF+ (minBound :: IPv6Addr) @?= IPv6Addr 0 0 0 0+ (maxBound :: IPv6Addr) @?=+ IPv6Addr 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF ] multiaddrTests :: TestTree