-
Notifications
You must be signed in to change notification settings - Fork 53
/
Base.hs
133 lines (116 loc) · 4.52 KB
/
Base.hs
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module Juno.Types.Base
( NodeID(..)
, Term(..), startTerm
, LogIndex(..), startIndex
, RequestId(..), startRequestId, toRequestId
, ReceivedAt(..)
-- for simplicity, re-export some core types that we need all over the place
, PublicKey, PrivateKey, Signature(..), sign, valid, importPublic, importPrivate
, Role(..)
) where
import Control.Monad (mzero)
import Crypto.Ed25519.Pure ( PublicKey, PrivateKey, Signature(..), sign, valid
, importPublic, importPrivate, exportPublic, exportPrivate)
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import Data.Serialize (Serialize)
import qualified Data.Serialize as S
import Data.Word (Word64)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Thyme.Clock
import Data.Thyme.Time.Core ()
import Data.Thyme.Internal.Micro (Micro)
import Data.Aeson (genericParseJSON,genericToJSON,parseJSON,toJSON,ToJSON,FromJSON,Value(..))
import Data.Aeson.Types (defaultOptions,Options(..))
import GHC.Int (Int64)
import GHC.Generics hiding (from)
data NodeID = NodeID { _host :: !String, _port :: !Word64, _fullAddr :: !String }
deriving (Eq,Ord,Read,Show,Generic)
instance Serialize NodeID
instance ToJSON NodeID where
toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 1 }
instance FromJSON NodeID where
parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = drop 1 }
newtype Term = Term Int
deriving (Show, Read, Eq, Enum, Num, Ord, Generic, Serialize)
startTerm :: Term
startTerm = Term (-1)
newtype LogIndex = LogIndex Int
deriving (Show, Read, Eq, Ord, Enum, Num, Real, Integral, Generic, Serialize)
startIndex :: LogIndex
startIndex = LogIndex (-1)
newtype RequestId = RequestId Int64
deriving (Show, Read, Eq, Ord, Enum, Num, Generic, Serialize)
startRequestId :: RequestId
startRequestId = RequestId 0
toRequestId :: Int64 -> RequestId
toRequestId a = RequestId a
deriving instance Eq Signature
deriving instance Ord Signature
instance Serialize Signature where
put (Sig s) = S.put s
get = Sig <$> (S.get >>= S.getByteString)
instance Eq PublicKey where
b == b' = exportPublic b == exportPublic b'
instance Ord PublicKey where
b <= b' = exportPublic b <= exportPublic b'
instance ToJSON PublicKey where
toJSON = toJSON . decodeUtf8 . B16.encode . exportPublic
instance FromJSON PublicKey where
parseJSON (String s) = do
(s',leftovers) <- return $ B16.decode $ encodeUtf8 s
if leftovers == B.empty
then case importPublic s' of
Just pk -> return pk
Nothing -> mzero
else mzero
parseJSON _ = mzero
instance ToJSON (Map NodeID PublicKey) where
toJSON = toJSON . Map.toList
instance FromJSON (Map NodeID PublicKey) where
parseJSON = fmap Map.fromList . parseJSON
instance Eq PrivateKey where
b == b' = exportPrivate b == exportPrivate b'
instance Ord PrivateKey where
b <= b' = exportPrivate b <= exportPrivate b'
instance ToJSON PrivateKey where
toJSON = toJSON . decodeUtf8 . B16.encode . exportPrivate
instance FromJSON PrivateKey where
parseJSON (String s) = do
(s',leftovers) <- return $ B16.decode $ encodeUtf8 s
if leftovers == B.empty
then case importPrivate s' of
Just pk -> return pk
Nothing -> mzero
else mzero
parseJSON _ = mzero
instance ToJSON (Map NodeID PrivateKey) where
toJSON = toJSON . Map.toList
instance FromJSON (Map NodeID PrivateKey) where
parseJSON = fmap Map.fromList . parseJSON
-- These instances suck, but I can't figure out how to use the Get monad to fail out if not
-- length = 32. For the record, if the getByteString 32 works the imports will not fail
instance Serialize PublicKey where
put s = S.putByteString (exportPublic s)
get = maybe (error "Invalid PubKey") id . importPublic <$> S.getByteString (32::Int)
instance Serialize PrivateKey where
put s = S.putByteString (exportPrivate s)
get = maybe (error "Invalid PubKey") id . importPrivate <$> S.getByteString (32::Int)
-- | UTCTime from Thyme of when ZMQ received the message
newtype ReceivedAt = ReceivedAt {_unReceivedAt :: UTCTime}
deriving (Show, Eq, Ord, Generic)
instance Serialize ReceivedAt
instance Serialize UTCTime
instance Serialize NominalDiffTime
instance Serialize Micro
data Role = Follower
| Candidate
| Leader
deriving (Show, Generic, Eq)