module LibP2P.DHT.Validator
(
Validator (..)
, namespacedValidator
, pkValidator
, defaultValidator
, splitRecordKey
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import LibP2P.Crypto.PeerId (fromPublicKey, peerIdBytes)
import LibP2P.Crypto.Protobuf (decodePublicKey)
data Validator = Validator
{ Validator -> ByteString -> ByteString -> Either String ()
valValidate :: ByteString -> ByteString -> Either String ()
, Validator -> ByteString -> [ByteString] -> Either String Int
valSelect :: ByteString -> [ByteString] -> Either String Int
}
splitRecordKey :: ByteString -> Either String (ByteString, ByteString)
splitRecordKey :: ByteString -> Either String (ByteString, ByteString)
splitRecordKey ByteString
key = case ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
key of
Just (Word8
0x2F, ByteString
rest) ->
let (ByteString
ns, ByteString
pathWithSlash) = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
BS.break (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x2F) ByteString
rest
in case ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
pathWithSlash of
Just (Word8
0x2F, ByteString
path) -> (ByteString, ByteString) -> Either String (ByteString, ByteString)
forall a b. b -> Either a b
Right (ByteString
ns, ByteString
path)
Maybe (Word8, ByteString)
_ -> String -> Either String (ByteString, ByteString)
forall a b. a -> Either a b
Left String
"invalid record key: missing namespace separator"
Maybe (Word8, ByteString)
_ -> String -> Either String (ByteString, ByteString)
forall a b. a -> Either a b
Left String
"invalid record key: missing leading '/'"
namespacedValidator :: Map ByteString Validator -> Validator
namespacedValidator :: Map ByteString Validator -> Validator
namespacedValidator Map ByteString Validator
validators = Validator
{ valValidate :: ByteString -> ByteString -> Either String ()
valValidate = \ByteString
key ByteString
value -> do
v <- ByteString -> Either String Validator
validatorFor ByteString
key
valValidate v key value
, valSelect :: ByteString -> [ByteString] -> Either String Int
valSelect = \ByteString
key [ByteString]
values -> do
v <- ByteString -> Either String Validator
validatorFor ByteString
key
valSelect v key values
}
where
validatorFor :: ByteString -> Either String Validator
validatorFor ByteString
key = do
(ns, _) <- ByteString -> Either String (ByteString, ByteString)
splitRecordKey ByteString
key
case Map.lookup ns validators of
Maybe Validator
Nothing -> String -> Either String Validator
forall a b. a -> Either a b
Left (String
"invalid record keytype: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ ByteString -> String
BSC.unpack ByteString
ns)
Just Validator
v -> Validator -> Either String Validator
forall a b. b -> Either a b
Right Validator
v
pkValidator :: Validator
pkValidator :: Validator
pkValidator = Validator
{ valValidate :: ByteString -> ByteString -> Either String ()
valValidate = \ByteString
key ByteString
value -> do
(_, mh) <- ByteString -> Either String (ByteString, ByteString)
splitRecordKey ByteString
key
pub <- decodePublicKey value
let derived = PeerId -> ByteString
peerIdBytes (PublicKey -> PeerId
fromPublicKey PublicKey
pub)
if derived == mh
then Right ()
else Left "public key does not match record key"
, valSelect :: ByteString -> [ByteString] -> Either String Int
valSelect = \ByteString
_ [ByteString]
values ->
if [ByteString] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ByteString]
values
then String -> Either String Int
forall a b. a -> Either a b
Left String
"no values to select from"
else Int -> Either String Int
forall a b. b -> Either a b
Right Int
0
}
defaultValidator :: Validator
defaultValidator :: Validator
defaultValidator = Map ByteString Validator -> Validator
namespacedValidator ([(ByteString, Validator)] -> Map ByteString Validator
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(String -> ByteString
BSC.pack String
"pk", Validator
pkValidator)])