summaryrefslogtreecommitdiff
path: root/src/Network/RPC.hs
blob: 7fb0e571cb499777703058f93f8ead68872c5329 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
{-# LANGUAGE ConstraintKinds        #-}
{-# LANGUAGE DeriveDataTypeable     #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE RankNTypes             #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeFamilies           #-}
module Network.RPC where

import Data.Bits
import Data.ByteString (ByteString)
import Data.Kind       (Constraint)
import Data.Data
import Network.Socket
import Text.PrettyPrint.HughesPJClass  hiding (($$), (<>))
import Data.Serialize                  as S
import qualified Data.ByteString.Char8 as Char8
import Data.ByteString.Base16          as Base16

data MessageClass = Error | Query | Response
 deriving (Eq,Ord,Enum,Bounded,Data,Show,Read)

class Envelope envelope where
    type TransactionID envelope
    type NodeId envelope

    envelopePayload     :: envelope a -> a
    envelopeTransaction :: envelope a -> TransactionID envelope
    envelopeClass       :: envelope a -> MessageClass

    -- | > buildReply self addr qry response
    --
    --    [ self ]     this node's id.
    --
    --    [ addr ]     SockAddr of query origin.
    --
    --    [ qry ]      received query message.
    --
    --    [ response ] response payload.
    --
    -- Returns: response message envelope
    buildReply :: NodeId envelope -> SockAddr -> envelope a -> b -> envelope b

-- | In Kademlia, the distance metric is XOR and the result is
-- interpreted as an unsigned integer.
newtype NodeDistance nodeid = NodeDistance nodeid
  deriving (Eq, Ord)

-- | distance(A,B) = |A xor B| Smaller values are closer.
distance :: Bits nid => nid -> nid -> NodeDistance nid
distance a b = NodeDistance $ xor a b

instance Serialize nodeid => Show (NodeDistance nodeid) where
  show (NodeDistance w) = Char8.unpack $ Base16.encode $ S.encode w

instance Serialize nodeid => Pretty (NodeDistance nodeid) where
  pPrint n = text $ show n


class Envelope envelope => WireFormat raw envelope where
    type SerializableTo raw :: * -> Constraint
    type CipherContext raw envelope

    decodeHeaders :: CipherContext raw envelope -> ByteString -> Either String (envelope raw)
    decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a)

    encodeHeaders :: CipherContext raw envelope -> envelope raw -> ByteString
    encodePayload :: SerializableTo raw a => envelope a -> envelope raw