1+ {-# LANGUAGE MultiWayIf #-}
12module Ergvein.Index.Protocol.Deserialization where
23
34import Codec.Compression.GZip
45import Control.Monad
56import Data.Attoparsec.Binary
67import Data.Attoparsec.ByteString
7- import Data.Scientific
8+ import Data.Fixed
9+ import Data.Text (Text )
10+ import Data.Text.Encoding
11+ import Data.Text.Encoding.Error
812import Data.Word
913
1014import Ergvein.Index.Protocol.Types
@@ -42,7 +46,7 @@ word32toMessageType = \case
4246
4347currencyCodeParser :: Parser CurrencyCode
4448currencyCodeParser = do
45- w <- anyWord32le
49+ w <- varInt
4650 case word32ToCurrencyCode w of
4751 Nothing -> fail " Invalid currency code"
4852 Just c -> pure c
@@ -63,6 +67,14 @@ word8toFeeLevel = \case
6367 2 -> Just FeeCheap
6468 _ -> Nothing
6569
70+ varInt :: Integral a => Parser a
71+ varInt = do
72+ w <- anyWord8
73+ if | w == 0xFF -> fmap fromIntegral anyWord64le
74+ | w == 0xFE -> fmap fromIntegral anyWord32le
75+ | w == 0xFD -> fmap fromIntegral anyWord16le
76+ | otherwise -> pure $ fromIntegral w
77+
6678versionParser :: Parser ProtocolVersion
6779versionParser = do
6880 bs :: S. Bitstream S. Right <- S. fromBits <$> anyWord32be
@@ -77,25 +89,29 @@ versionParser = do
7789
7890messageHeaderParser :: Parser MessageHeader
7991messageHeaderParser = do
80- messageType <- messageTypeParser
81- messageSize <- anyWord32le
82- pure $ MessageHeader messageType messageSize
92+ mt <- messageTypeParser
93+ if not $ messageHasPayload mt then pure $ MessageHeader mt 0 else do
94+ messageSize <- varInt
95+ pure $ MessageHeader mt messageSize
96+
97+ messageLengthParser :: Parser Word32
98+ messageLengthParser = varInt
8399
84100messageTypeParser :: Parser MessageType
85- messageTypeParser = guardJust " out of message type bounds" . word32toMessageType =<< anyWord32le
101+ messageTypeParser = guardJust " out of message type bounds" . word32toMessageType =<< varInt
86102
87103rejectCodeParser :: Parser RejectCode
88- rejectCodeParser = guardJust " out of reject type bounds" . word32toRejectType =<< anyWord32le
104+ rejectCodeParser = guardJust " out of reject type bounds" . word32toRejectType =<< varInt
89105
90106feeLevelParser :: Parser FeeLevel
91107feeLevelParser = guardJust " out of feeLevel type bounds" . word8toFeeLevel =<< anyWord8
92108
93109versionBlockParser :: Parser ScanBlock
94110versionBlockParser = do
95111 currency <- currencyCodeParser
96- version <- anyWord32le
97- scanHeight <- anyWord64le
98- height <- anyWord64le
112+ version <- versionParser
113+ scanHeight <- varInt
114+ height <- varInt
99115
100116 pure $ ScanBlock
101117 { scanBlockCurrency = currency
@@ -104,11 +120,16 @@ versionBlockParser = do
104120 , scanBlockHeight = height
105121 }
106122
107- filterParser :: Parser BlockFilter
108- filterParser = do
109- blockIdLength <- fromIntegral <$> anyWord32le
110- blockId <- Parse. take blockIdLength
111- blockFilterLength <- fromIntegral <$> anyWord32le
123+ blockIdLength :: CurrencyCode -> Int
124+ blockIdLength = \ case
125+ BTC -> 32
126+ TBTC -> 32
127+ _ -> 32 -- TODO: edit for other currencies if differ
128+
129+ filterParser :: CurrencyCode -> Parser BlockFilter
130+ filterParser c = do
131+ blockId <- Parse. take $ blockIdLength c
132+ blockFilterLength <- fromIntegral <$> (varInt :: Parser Word32 )
112133 blockFilter <- Parse. take blockFilterLength
113134
114135 pure $ BlockFilter
@@ -121,26 +142,32 @@ addressParser = do
121142 addrType <- maybe (fail " Invalid address type" ) pure
122143 . word8ToIPType
123144 =<< anyWord8
124- addrPort <- anyWord16le
125- addr <- Parse. take (if addrType == IPV4 then 4 else 16 )
126- pure $ Address
127- { addressType = addrType
128- , addressPort = addrPort
129- , addressAddress = addr
130- }
145+ case addrType of
146+ IPV4 -> AddressIpv4 <$> anyWord32be <*> anyWord16be
147+ IPV6 -> AddressIpv6 <$> (IpV6 <$> anyWord32be <*> anyWord32be <*> anyWord32be <*> anyWord32be) <*> anyWord16be
148+ OnionV3 -> AddressOnionV3 <$> Parse. take (fromIntegral $ addressSize OnionV3 ) <*> anyWord16be
149+
150+ textParser :: Parser Text
151+ textParser = do
152+ l :: Word32 <- varInt
153+ bs <- Parse. take (fromIntegral l)
154+ pure $ decodeUtf8With lenientDecode bs
131155
132156messageParser :: MessageType -> Parser Message
133157messageParser MPingType = MPing <$> anyWord64le
134158
135159messageParser MPongType = MPong <$> anyWord64le
136160
137- messageParser MRejectType = MReject . Reject <$> rejectCodeParser
161+ messageParser MRejectType = fmap MReject $ Reject
162+ <$> messageTypeParser
163+ <*> rejectCodeParser
164+ <*> textParser
138165
139166messageParser MVersionType = do
140167 version <- versionParser
141168 time <- fromIntegral <$> anyWord64le
142169 nonce <- anyWord64le
143- currencies <- anyWord32le
170+ currencies <- varInt :: Parser Word32
144171 versionBlocks <- UV. fromList <$> replicateM (fromIntegral currencies) versionBlockParser
145172
146173 pure $ MVersion $ Version
@@ -150,12 +177,12 @@ messageParser MVersionType = do
150177 , versionScanBlocks = versionBlocks
151178 }
152179
153- messageParser MVersionACKType = MVersionACK VersionACK <$ word8 0
180+ messageParser MVersionACKType = pure $ MVersionACK VersionACK
154181
155182messageParser MFiltersRequestType = do
156183 currency <- currencyCodeParser
157- start <- anyWord64le
158- amount <- anyWord64le
184+ start <- varInt
185+ amount <- varInt
159186
160187 pure $ MFiltersRequest $ FilterRequest
161188 { filterRequestMsgCurrency = currency
@@ -165,11 +192,11 @@ messageParser MFiltersRequestType = do
165192
166193messageParser MFiltersResponseType = do
167194 currency <- currencyCodeParser
168- amount <- anyWord32le
195+ amount :: Word32 <- varInt
169196 filtersString <- takeLazyByteString
170197
171198 let unzippedFilters = LBS. toStrict $ decompress filtersString
172- parser = V. fromList <$> replicateM (fromIntegral amount) filterParser
199+ parser = V. fromList <$> replicateM (fromIntegral amount) ( filterParser currency)
173200
174201 case parseOnly parser unzippedFilters of
175202 Right parsedFilters -> pure $ MFiltersResponse $ FilterResponse
@@ -181,10 +208,9 @@ messageParser MFiltersResponseType = do
181208
182209messageParser MFilterEventType = do
183210 currency <- currencyCodeParser
184- height <- anyWord64le
185- blockIdLength <- fromIntegral <$> anyWord32le
186- blockId <- Parse. take blockIdLength
187- blockFilterLength <- fromIntegral <$> anyWord32le
211+ height <- varInt
212+ blockId <- Parse. take (blockIdLength currency)
213+ blockFilterLength <- fromIntegral <$> (varInt :: Parser Word32 )
188214 blockFilter <- Parse. take blockFilterLength
189215
190216 pure $ MFiltersEvent $ FilterEvent
@@ -195,69 +221,68 @@ messageParser MFilterEventType = do
195221 }
196222
197223messageParser MFeeRequestType = do
198- amount <- anyWord32le
224+ amount :: Word32 <- varInt
199225 curs <- replicateM (fromIntegral amount) currencyCodeParser
200226 pure $ MFeeRequest curs
201227
202228messageParser MFeeResponseType = do
203- amount <- anyWord32le
229+ amount :: Word32 <- varInt
204230 resps <- replicateM (fromIntegral amount) parseFeeResp
205231 pure $ MFeeResponse resps
206232
207- messageParser MPeerRequestType = MPeerRequest PeerRequest <$ word8 0
233+ messageParser MPeerRequestType = pure $ MPeerRequest PeerRequest
208234
209235messageParser MPeerResponseType = do
210- amount <- anyWord32le
236+ amount :: Word32 <- varInt
211237 addresses <- V. fromList <$> replicateM (fromIntegral amount) addressParser
212238 pure $ MPeerResponse $ PeerResponse
213239 { peerResponseAddresses = addresses
214240 }
215241
216242messageParser MIntroducePeerType = do
217- amount <- anyWord32le
243+ amount :: Word32 <- varInt
218244 addresses <- V. fromList <$> replicateM (fromIntegral amount) addressParser
219245 pure $ MPeerIntroduce $ PeerIntroduce
220246 { peerIntroduceAddresses = addresses
221247 }
222248
223249messageParser MRatesRequestType = do
224- n <- fmap fromIntegral anyWord32le
250+ n <- fmap fromIntegral (varInt :: Parser Word32 )
225251 cfs <- replicateM n cfParser
226252 pure $ MRatesRequest $ RatesRequest $ M. fromList cfs
227253
228254messageParser MRatesResponseType = do
229- n <- fmap fromIntegral anyWord32le
255+ n <- fmap fromIntegral (varInt :: Parser Word32 )
230256 cfds <- replicateM n cfdParser
231257 pure $ MRatesResponse $ RatesResponse $ M. fromList cfds
232258
233259enumParser :: Enum a => Parser a
234- enumParser = fmap (toEnum . fromIntegral ) anyWord32le
260+ enumParser = fmap (toEnum . fromIntegral ) (varInt :: Parser Word32 )
235261
236262cfParser :: Parser (CurrencyCode , [Fiat ])
237263cfParser = do
238264 c <- enumParser
239- n <- fmap fromIntegral anyWord32le
265+ n <- fmap fromIntegral (varInt :: Parser Word32 )
240266 fmap (c, ) $ replicateM n enumParser
241267
242- cfdParser :: Parser (CurrencyCode , M. Map Fiat Double )
268+ cfdParser :: Parser (CurrencyCode , M. Map Fiat Centi )
243269cfdParser = do
244270 c <- enumParser
245- n <- fmap fromIntegral anyWord32le
271+ n <- fmap fromIntegral (varInt :: Parser Word32 )
246272 fmap ((c,) . M. fromList) $ replicateM n fdParser
247273
248- fdParser :: Parser (Fiat , Double )
249- fdParser = (,) <$> enumParser <*> parseDouble
274+ fdParser :: Parser (Fiat , Centi )
275+ fdParser = (,) <$> enumParser <*> parseCenti
250276
251- parseDouble :: Parser Double
252- parseDouble = do
253- c <- fromIntegral <$> anyWord64le
254- e <- fromIntegral <$> anyWord64le
255- pure $ toRealFloat $ scientific c e
277+ parseCenti :: Parser Centi
278+ parseCenti = do
279+ w <- anyWord64le
280+ pure $ MkFixed $ fromIntegral w
256281
257282parseCurrencyPair :: Parser (CurrencyCode , Fiat )
258283parseCurrencyPair = (,)
259- <$> (fmap (toEnum . fromIntegral ) anyWord32le )
260- <*> (fmap (toEnum . fromIntegral ) anyWord32le )
284+ <$> (fmap (toEnum . fromIntegral ) (varInt :: Parser Word32 ) )
285+ <*> (fmap (toEnum . fromIntegral ) (varInt :: Parser Word32 ) )
261286
262287parseFeeResp :: Parser FeeResp
263288parseFeeResp = do
@@ -268,14 +293,14 @@ parseFeeResp = do
268293 _ -> genericParser currency
269294 where
270295 btcParser isTest = do
271- h <- (,) <$> anyWord64le <*> anyWord64le
272- m <- (,) <$> anyWord64le <*> anyWord64le
273- l <- (,) <$> anyWord64le <*> anyWord64le
296+ h <- (,) <$> varInt <*> varInt
297+ m <- (,) <$> varInt <*> varInt
298+ l <- (,) <$> varInt <*> varInt
274299 pure $ FeeRespBTC isTest $ FeeBundle h m l
275300 genericParser cur = FeeRespGeneric cur
276- <$> anyWord64le
277- <*> anyWord64le
278- <*> anyWord64le
301+ <$> varInt
302+ <*> varInt
303+ <*> varInt
279304
280305parseMessage :: MessageType -> BS. ByteString -> Either String (Message , BS. ByteString )
281306parseMessage msgType source =
0 commit comments