haskoin-store 0.53.2 → 0.53.3
raw patch · 4 files changed
+112/−22 lines, 4 filesdep ~haskoin-coredep ~haskoin-store-dataPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: haskoin-core, haskoin-store-data
API changes (from Hackage documentation)
+ Haskoin.Store.Web: [maxLimitBody] :: WebLimits -> !Word32
- Haskoin.Store.Web: WebLimits :: !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> WebLimits
+ Haskoin.Store.Web: WebLimits :: !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> WebLimits
Files
- CHANGELOG.md +7/−0
- app/Main.hs +9/−0
- haskoin-store.cabal +8/−8
- src/Haskoin/Store/Web.hs +88/−14
CHANGELOG.md view
@@ -4,6 +4,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html) +## 0.53.3+### Added+- Blockchain.info endpoint: `q/pubkeyhash/:addr`.+- Blockchain.info endpoint: `q/getsentbyaddress/:addr`.+- Blockchain.info endpoint: `q/pubkeyaddr/:addr`.+- Configurable request body limit size.+ ## 0.53.2 ### Fixed - Return output in satoshi for some endpoints.
app/Main.hs view
@@ -198,12 +198,14 @@ def_limit <- defEnv "DEF_LIMIT" (maxLimitDefault def) readMaybe max_gap <- defEnv "MAX_GAP" (maxLimitGap def) readMaybe init_gap <- defEnv "INIT_GAP" (maxLimitInitialGap def) readMaybe+ max_body <- defEnv "MAX_BODY" (maxLimitBody def) readMaybe return WebLimits { maxLimitCount = max_limit , maxLimitFull = max_full , maxLimitOffset = max_offset , maxLimitDefault = def_limit , maxLimitGap = max_gap , maxLimitInitialGap = init_gap+ , maxLimitBody = max_body } {-# NOINLINE defWebLimits #-} @@ -409,6 +411,13 @@ <> help "Max gap for empty xpub" <> showDefault <> value (maxLimitInitialGap (configWebLimits def))+ maxLimitBody <-+ option auto $+ metavar "BYTES"+ <> long "max-body"+ <> help "Maximum request body size"+ <> showDefault+ <> value (maxLimitBody (configWebLimits def)) blockTimeout <- option auto $ metavar "SECONDS"
haskoin-store.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 7f0440e3b9697fe1c6eead20f4f9dad0b4795c894bbd89689d0049b68916587d+-- hash: b3b7762ba16d37c51367999ec82a9bf98fde10493c5fa48171a6a05ca6e94d45 name: haskoin-store-version: 0.53.2+version: 0.53.3 synopsis: Storage and index for Bitcoin and Bitcoin Cash description: Please see the README on GitHub at <https://github.com/haskoin/haskoin-store#readme> category: Bitcoin, Finance, Network@@ -60,9 +60,9 @@ , ekg-statsd >=0.2.5 , foldl >=1.4.10 , hashable >=1.3.0.0- , haskoin-core >=0.19.0+ , haskoin-core >=0.20.4 , haskoin-node >=0.17.0- , haskoin-store-data ==0.53.2+ , haskoin-store-data ==0.53.3 , hedis >=0.12.13 , http-types >=0.12.3 , lens >=4.18.1@@ -113,10 +113,10 @@ , filepath , foldl >=1.4.10 , hashable >=1.3.0.0- , haskoin-core >=0.19.0+ , haskoin-core >=0.20.4 , haskoin-node >=0.17.0 , haskoin-store- , haskoin-store-data ==0.53.2+ , haskoin-store-data ==0.53.3 , hedis >=0.12.13 , http-types >=0.12.3 , lens >=4.18.1@@ -172,10 +172,10 @@ , ekg-statsd >=0.2.5 , foldl >=1.4.10 , hashable >=1.3.0.0- , haskoin-core >=0.19.0+ , haskoin-core >=0.20.4 , haskoin-node >=0.17.0 , haskoin-store- , haskoin-store-data ==0.53.2+ , haskoin-store-data ==0.53.3 , hedis >=0.12.13 , hspec >=2.7.1 , http-types >=0.12.3
src/Haskoin/Store/Web.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -19,11 +20,13 @@ ) where import Conduit (ConduitT, await,+ concatMapC, concatMapMC, dropC,- dropWhileC, mapC,- runConduit, sinkList,- takeC, takeWhileC,- yield, (.|))+ dropWhileC, headC,+ mapC, runConduit,+ sinkList, takeC,+ takeWhileC, yield,+ (.|)) import Control.Applicative ((<|>)) import Control.Arrow (second) import Control.Lens ((.~), (^.))@@ -69,6 +72,7 @@ fromMaybe, isJust, mapMaybe, maybeToList) import Data.Proxy (Proxy (..))+import Data.Serialize (decode) import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text)@@ -95,6 +99,7 @@ chainGetAncestor, chainGetBest, getPeers, sendMessage)+import Haskoin.Script import Haskoin.Store.BlockStore import Haskoin.Store.Cache import Haskoin.Store.Common@@ -152,6 +157,7 @@ , maxLimitDefault :: !Word32 , maxLimitGap :: !Word32 , maxLimitInitialGap :: !Word32+ , maxLimitBody :: !Word32 } deriving (Eq, Show) @@ -164,6 +170,7 @@ , maxLimitDefault = 100 , maxLimitGap = 32 , maxLimitInitialGap = 20+ , maxLimitBody = 1024 * 1024 } data WebConfig = WebConfig@@ -358,6 +365,7 @@ , webStore = store' , webStats = stats , webPriceGet = pget+ , webMaxLimits = WebLimits{..} } = do ticker <- newTVarIO HashMap.empty metrics <- mapM createMetrics stats@@ -372,7 +380,7 @@ runner <- askRunInIO S.scottyOptsT opts (runner . (`runReaderT` st)) $ do S.middleware reqLogger- S.middleware reqSizeLimit+ S.middleware (reqSizeLimit maxLimitBody) S.defaultHandler defHandler handlePaths S.notFound $ raise_ ThingNotFound@@ -678,7 +686,9 @@ S.get "/blockchain/export-history" scottyBinfoHistory S.post "/blockchain/export-history" scottyBinfoHistory S.get "/blockchain/q/addresstohash/:addr" scottyBinfoAddrToHash+ S.get "/blockchain/q/hashtoaddress/:hash" scottyBinfoHashToAddr S.get "/blockchain/q/addrpubkey/:pubkey" scottyBinfoAddrPubkey+ S.get "/blockchain/q/pubkeyaddr/:addr" scottyBinfoPubKeyAddr S.get "/blockchain/q/hashpubkey/:pubkey" scottyBinfoHashPubkey S.get "/blockchain/q/getblockcount" scottyBinfoGetBlockCount S.get "/blockchain/q/latesthash" scottyBinfoLatestHash@@ -688,6 +698,7 @@ S.get "/blockchain/q/txfee/:txid" scottyBinfoTxFees S.get "/blockchain/q/txresult/:txid/:addr" scottyBinfoTxResult S.get "/blockchain/q/getreceivedbyaddress/:addr" scottyBinfoReceived+ S.get "/blockchain/q/getsentbyaddress/:addr" scottyBinfoSent S.get "/blockchain/q/addressbalance/:addr" scottyBinfoAddrBalance S.get "/blockchain/q/addressfirstseen/:addr" scottyFirstSeen where@@ -1853,6 +1864,14 @@ setHeaders S.text . cs . show $ balanceTotalReceived b +scottyBinfoSent :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m ()+scottyBinfoSent = do+ setMetrics addrBalanceStat 1+ a <- getAddress "addr"+ b <- fromMaybe (zeroBalance a) <$> getBalance a+ setHeaders+ S.text . cs . show $ balanceTotalReceived b - balanceAmount b - balanceZero b+ scottyBinfoAddrBalance :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m () scottyBinfoAddrBalance = do setMetrics addrBalanceStat 1@@ -2164,23 +2183,78 @@ H.computeSubsidy net (H.nodeHeight bn + 1) scottyBinfoAddrToHash :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m ()-scottyBinfoAddrToHash =- getAddress "addr" >>= \addr -> do+scottyBinfoAddrToHash = do+ addr <- getAddress "addr" setHeaders S.text . encodeHexLazy . runPutL . serialize $ getAddrHash160 addr +scottyBinfoHashToAddr :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m ()+scottyBinfoHashToAddr = do+ bs <- maybe S.next return . decodeHex =<< S.param "hash"+ net <- asks (storeNetwork . webStore . webConfig)+ hash <- either (const S.next) return (decode bs)+ addr <- maybe S.next return (addrToText net (PubKeyAddress hash))+ setHeaders+ S.text $ TL.fromStrict addr+ scottyBinfoAddrPubkey :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m () scottyBinfoAddrPubkey = do- pubkey <- pubKeyAddr . fromString <$> S.param "pubkey"+ hex <- S.param "pubkey"+ pubkey <- maybe S.next (return . pubKeyAddr) $+ eitherToMaybe . runGetS deserialize =<< decodeHex hex net <- lift $ asks (storeNetwork . webStore . webConfig) setHeaders case addrToText net pubkey of- Nothing -> raise rawaddrStat ThingNotFound- Just a -> S.text $ TL.fromStrict a+ Nothing -> raise rawaddrStat ThingNotFound+ Just a -> S.text $ TL.fromStrict a +scottyBinfoPubKeyAddr :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m ()+scottyBinfoPubKeyAddr = do+ addr <- getAddress "addr"+ mi <- strm addr+ i <- case mi of+ Nothing -> raise_ ThingNotFound+ Just i -> return i+ pk <- case extr addr i of+ Left e -> raise_ $ UserError e+ Right t -> return t+ setHeaders+ S.text $ encodeHexLazy $ L.fromStrict pk+ where+ strm addr = runConduit $+ streamThings (getAddressTxs addr) (Just txRefHash) def{limit = 50} .|+ concatMapMC (getTransaction . txRefHash) .|+ concatMapC (filter (inp addr) . transactionInputs) .|+ headC+ inp addr StoreInput{inputAddress = Just a} = a == addr+ inp _ _ = False+ extr addr StoreInput{inputSigScript, inputPkScript, inputWitness} = do+ Script sig <- decode inputSigScript+ Script pks <- decode inputPkScript+ case addr of+ PubKeyAddress{} ->+ case sig of+ [OP_PUSHDATA _ _, OP_PUSHDATA pub _] ->+ Right pub+ [OP_PUSHDATA _ _] ->+ case pks of+ [OP_PUSHDATA pub _, OP_CHECKSIG] ->+ Right pub+ _ -> Left "Could not parse scriptPubKey"+ _ -> Left "Could not parse scriptSig"+ WitnessPubKeyAddress{} ->+ case inputWitness of+ [_, pub] -> return pub+ _ -> Left "Could not parse scriptPubKey"+ _ -> Left "Address does not have public key"+ extr _ _ = Left "Incorrect input type"+ scottyBinfoHashPubkey :: (MonadUnliftIO m, MonadLoggerIO m) => WebT m () scottyBinfoHashPubkey = do- addr <- pubKeyAddr . fromString <$> S.param "pubkey"+ pkm <- (eitherToMaybe . runGetS deserialize <=< decodeHex) <$> S.param "pubkey"+ addr <- case pkm of+ Nothing -> raise_ $ UserError "Could not decode public key"+ Just pk -> return $ pubKeyAddr pk setHeaders S.text . encodeHexLazy . runPutL . serialize $ getAddrHash160 addr @@ -2497,10 +2571,10 @@ runner $ $(logWarnS) "Web" $ "Slow [" <> cs (show diff) <> " ms]: " <> fmtReq b req -reqSizeLimit :: Middleware-reqSizeLimit = requestSizeLimitMiddleware lim+reqSizeLimit :: Integral i => i -> Middleware+reqSizeLimit i = requestSizeLimitMiddleware lim where- max_len _req = return (Just (256 * 1024))+ max_len _req = return (Just (fromIntegral i)) lim = setOnLengthExceeded too_big $ setMaxLengthForRequest max_len defaultRequestSizeLimitSettings