packages feed

haskoin-store-data 0.52.13 → 0.53.0

raw patch · 7 files changed

+144/−122 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Haskoin.Store.Data: [getBinfoRawAddrBalance] :: BinfoRawAddr -> !Balance
- Haskoin.Store.Data: [getBinfoRawAddrTxs] :: BinfoRawAddr -> ![BinfoTx]
+ Haskoin.Store.Data: [binfoRawAddr] :: BinfoRawAddr -> !BinfoAddr
+ Haskoin.Store.Data: [binfoRawBalance] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [binfoRawReceived] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [binfoRawSent] :: BinfoRawAddr -> !Int64
+ Haskoin.Store.Data: [binfoRawTxCount] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [binfoRawTxs] :: BinfoRawAddr -> ![BinfoTx]
+ Haskoin.Store.Data: [binfoRawUnredeemed] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: deriveTypeToText :: DeriveType -> Text
+ Haskoin.Store.Data: instance Web.Scotty.Action.Parsable Haskoin.Store.Data.DeriveType
+ Haskoin.Store.Data: textToDeriveType :: Text -> Maybe DeriveType
- Haskoin.Store.Data: BinfoRawAddr :: !Balance -> ![BinfoTx] -> BinfoRawAddr
+ Haskoin.Store.Data: BinfoRawAddr :: !BinfoAddr -> !Word64 -> !Word64 -> !Word64 -> !Word64 -> !Int64 -> ![BinfoTx] -> BinfoRawAddr

Files

CHANGELOG.md view
@@ -4,6 +4,27 @@ 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.0+### Added+- Blockchain.info endpoint: `q/addresstohash/:addr`.+- Blockchain.info endpoint: `q/addrpubkey/:pubkey`.+- Blockchain.info endpoint: `q/hashpubkey/:pubkey`.+- Blockchain.info endpoint: `q/getblockcount`.+- Blockchain.info endpoint: `q/latesthash`.+- Blockchain.info endpoint: `q/bcperblock`.+- Blockchain.info endpoint: `q/txtotalbtcoutput/:txid`.+- Blockchain.info endpoint: `q/txtotalbtcinput/:txid`.+- Blockchain.info endpoint: `q/txfee/:txid`.+- Blockchain.info endpoint: `q/txresult/:txhash/:addr`.+- Blockchain.info endpoint: `q/getreceivedbyaddress/:addr`.+- Blockchain.info endpoint: `q/addressbalance/:addr`.+- Blockchain.info endpoint: `q/addressfirstseen/:addr`.+- Support xpubs in `blockchain/rawaddr` endpoint.++### Fixed+- Various latest blocks endpoint fixes.+- Allow empty parameters in Blockchain.info POST requests.+ ## 0.52.13 ### Fixed - Upstream fixes for unknown inv types.
README.md view
@@ -44,4 +44,4 @@  ## API Documentation -* [Swagger API Documentation](https://api.haskoin.com/).+* [Swagger API Documentation](https://api.haskoin.com/)
haskoin-store-data.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: af8f78beeb32434af141e0db55de82fa118f20234aa90dbc9e9e34fc1771215d+-- hash: 350002bf6bde26ced5867d69ad29f5e9b105651275decac058ef220ebea8e66e  name:           haskoin-store-data-version:        0.52.13+version:        0.53.0 synopsis:       Data for Haskoin Store description:    Please see the README on GitHub at <https://github.com/haskoin/haskoin-store#readme> category:       Bitcoin, Finance, Network
src/Haskoin/Store/Data.hs view
@@ -66,6 +66,8 @@     , xPubUnspentParseJSON     , XPubSummary(..)     , DeriveType(..)+    , textToDeriveType+    , deriveTypeToText        -- * Other Data     , TxId(..)@@ -164,12 +166,9 @@ import           Control.Applicative     ((<|>)) import           Control.DeepSeq         (NFData) import           Control.Exception       (Exception)-import           Control.Monad           (join, mzero, replicateM, unless,-                                          (<=<))-import           Data.Aeson              (Encoding, FromJSON (..),-                                          FromJSONKey (..), ToJSON (..),-                                          ToJSONKey (..), Value (..), (.!=),-                                          (.:), (.:?), (.=))+import           Control.Monad           (join, mzero, unless, (<=<))+import           Data.Aeson              (Encoding, FromJSON (..), ToJSON (..),+                                          Value (..), (.!=), (.:), (.:?), (.=)) import qualified Data.Aeson              as A import qualified Data.Aeson.Encoding     as AE import           Data.Aeson.Types        (Parser)@@ -181,11 +180,8 @@ import           Data.Bytes.Get import qualified Data.Bytes.Get          as Bytes.Get import           Data.Bytes.Put-import qualified Data.Bytes.Put          as Bytes.Put import           Data.Bytes.Serial-import           Data.Bytes.Serial       (Serial (..)) import           Data.Default            (Default (..))-import           Data.Either             (fromRight, lefts, rights) import           Data.Foldable           (toList) import           Data.Function           (on) import           Data.HashMap.Strict     (HashMap)@@ -196,8 +192,6 @@ import           Data.Int                (Int32, Int64) import qualified Data.IntMap             as IntMap import           Data.IntMap.Strict      (IntMap)-import           Data.List               (unfoldr)-import           Data.Map.Strict         (Map) import           Data.Maybe              (catMaybes, fromMaybe, isJust,                                           isNothing, mapMaybe, maybeToList) import           Data.Serialize          (Serialize (..))@@ -211,12 +205,9 @@                                           utcTimeToPOSIXSeconds) import           Data.Time.Format        (defaultTimeLocale, formatTime,                                           parseTimeM)-import           Data.Word               (Word32, Word64, Word8)+import           Data.Word               (Word32, Word64) import           GHC.Generics            (Generic)-import           Haskoin-import           Numeric.Natural         (Natural)-import           Text.Printf             (printf)-import           Text.Read               (readMaybe)+import           Haskoin                 hiding (inputAddress) import           Web.Scotty.Trans        (Parsable (..), ScottyError (..))  data DeriveType@@ -225,6 +216,17 @@     | DeriveP2WPKH     deriving (Show, Eq, Generic, NFData) +textToDeriveType :: Text -> Maybe DeriveType+textToDeriveType "normal" = Just DeriveNormal+textToDeriveType "compat" = Just DeriveP2SH+textToDeriveType "segwit" = Just DeriveP2WPKH+textToDeriveType _        = Nothing++deriveTypeToText :: DeriveType -> Text+deriveTypeToText DeriveNormal = "normal"+deriveTypeToText DeriveP2SH   = "compat"+deriveTypeToText DeriveP2WPKH = "segwit"+ instance Serial DeriveType where     serialize DeriveNormal = putWord8 0x00     serialize DeriveP2SH   = putWord8 0x01@@ -234,6 +236,7 @@         0x00 -> return DeriveNormal         0x01 -> return DeriveP2SH         0x02 -> return DeriveP2WPKH+        _    -> return DeriveNormal  instance Binary DeriveType where     put = serialize@@ -246,6 +249,12 @@ instance Default DeriveType where     def = DeriveNormal +instance Parsable DeriveType where+    parseParam txt =+        case textToDeriveType (TL.toStrict txt) of+            Nothing -> Left "invalid derivation type"+            Just x  -> Right x+ data XPubSpec =     XPubSpec         { xPubSpecKey    :: !XPubKey@@ -799,6 +808,7 @@             inputWitness <- getList getLengthBytes             inputAddress <- getMaybe deserialize             return StoreInput{..}+        x -> fail $ "Unknown input id: " <> cs (show x)  instance Serialize StoreInput where     put = serialize@@ -825,7 +835,7 @@         inputWitness = wit,         inputAddress = a     } =-    A.object $+    A.object     [ "coinbase" .= False     , "txid" .= oph     , "output" .= opi@@ -838,7 +848,7 @@     ]  storeInputToJSON-    net+    _     StoreCoinbase     {         inputPoint = OutPoint oph opi,@@ -846,7 +856,7 @@         inputSigScript = ss,         inputWitness = wit     } =-    A.object $+    A.object     [ "coinbase" .= True     , "txid" .= oph     , "output" .= opi@@ -883,7 +893,7 @@     "witness" .= map encodeHex wit  storeInputToEncoding-    net+    _     StoreCoinbase     {         inputPoint = OutPoint oph opi,@@ -2130,15 +2140,15 @@             BadRequest ->                 [ "error" .= String "bad-request"                 , "message" .= String "Invalid request" ]-            UserError msg ->+            UserError msg' ->                 [ "error" .= String "user-error"-                , "message" .= String (cs msg) ]-            StringError msg ->+                , "message" .= String (cs msg') ]+            StringError msg' ->                 [ "error" .= String "string-error"-                , "message" .= String (cs msg) ]+                , "message" .= String (cs msg') ]             TxIndexConflict txids ->                 [ "error" .= String "multiple-tx-index"-                , "message" .= String "Many txs match that tx_index"+                , "message" .= String "Multiple txs match that index"                 , "txids" .= txids ]             ServerTimeout ->                 [ "error" .= String "server-timeout"@@ -2151,7 +2161,7 @@     parseJSON =         A.withObject "Except" $ \o -> do             ctr <- o .: "error"-            msg <- o .:? "message" .!= ""+            msg' <- o .:? "message" .!= ""             case ctr of                 String "not-found-or-invalid-arg" ->                     return ThingNotFound@@ -2160,9 +2170,9 @@                 String "bad-request" ->                     return BadRequest                 String "user-error" ->-                    return $ UserError msg+                    return $ UserError msg'                 String "string-error" ->-                    return $ StringError msg+                    return $ StringError msg'                 String "multiple-tx-index" -> do                     txids <- o .: "txids"                     return $ TxIndexConflict txids@@ -2299,8 +2309,13 @@  data BinfoRawAddr     = BinfoRawAddr-      { getBinfoRawAddrBalance :: !Balance-      , getBinfoRawAddrTxs     :: ![BinfoTx]+      { binfoRawAddr       :: !BinfoAddr+      , binfoRawBalance    :: !Word64+      , binfoRawTxCount    :: !Word64+      , binfoRawUnredeemed :: !Word64+      , binfoRawReceived   :: !Word64+      , binfoRawSent       :: !Int64+      , binfoRawTxs        :: ![BinfoTx]       }     deriving (Eq, Show, Generic, NFData) @@ -2308,59 +2323,67 @@ binfoRawAddrToJSON net BinfoRawAddr{..} =     A.object     [-        "hash160"        .= (encodeHex . runPutS . serialize <$> h160),-        "address"        .= addrToJSON net balanceAddress,-        "n_tx"           .= balanceTxCount,-        "n_unredeemed"   .= balanceUnspentCount,-        "total_received" .= balanceTotalReceived,-        "total_sent"     .= (balanceTotalReceived - bal),-        "final_balance"  .= bal,-        "txs"            .= map (binfoTxToJSON net) getBinfoRawAddrTxs+        "hash160"        .= h160,+        "address"        .= address,+        "n_tx"           .= binfoRawTxCount,+        "n_unredeemed"   .= binfoRawUnredeemed,+        "total_received" .= binfoRawReceived,+        "total_sent"     .= binfoRawSent,+        "final_balance"  .= binfoRawBalance,+        "txs"            .= map (binfoTxToJSON net) binfoRawTxs     ]   where-    Balance{..} = getBinfoRawAddrBalance-    bal = balanceAmount + balanceZero-    h160 = case balanceAddress of-               PubKeyAddress h        -> Just h-               ScriptAddress h        -> Just h-               WitnessPubKeyAddress h -> Just h-               _                      -> Nothing+    address = case binfoRawAddr of+        BinfoAddr a -> addrToJSON net a+        BinfoXpub x -> xPubToJSON net x+    h160 =+        encodeHex . runPutS . serialize <$>+        case binfoRawAddr of+        BinfoAddr a -> case a of+            PubKeyAddress h        -> Just h+            ScriptAddress h        -> Just h+            WitnessPubKeyAddress h -> Just h+            _                      -> Nothing+        _ -> Nothing  binfoRawAddrToEncoding :: Network -> BinfoRawAddr -> Encoding binfoRawAddrToEncoding net BinfoRawAddr{..} =     AE.pairs $-    "hash160"        .= (encodeHex . runPutS . serialize <$> h160) <>-    "address" `AE.pair` addrToEncoding net balanceAddress <>-    "n_tx"           .= balanceTxCount <>-    "n_unredeemed"   .= balanceUnspentCount <>-    "total_received" .= balanceTotalReceived <>-    "total_sent"     .= (balanceTotalReceived - bal) <>-    "final_balance"  .= bal <>-    "txs"     `AE.pair` AE.list (binfoTxToEncoding net) getBinfoRawAddrTxs+    "hash160"        .= h160 <>+    "address" `AE.pair` address <>+    "n_tx"           .= binfoRawTxCount <>+    "n_unredeemed"   .= binfoRawUnredeemed <>+    "total_received" .= binfoRawReceived <>+    "total_sent"     .= binfoRawSent <>+    "final_balance"  .= binfoRawBalance <>+    "txs"     `AE.pair` AE.list (binfoTxToEncoding net) binfoRawTxs   where-    Balance{..} = getBinfoRawAddrBalance-    bal = balanceAmount + balanceZero-    h160 = case balanceAddress of-               PubKeyAddress h        -> Just h-               ScriptAddress h        -> Just h-               WitnessPubKeyAddress h -> Just h-               _                      -> Nothing+    address = case binfoRawAddr of+        BinfoAddr a -> addrToEncoding net a+        BinfoXpub x -> xPubToEncoding net x+    h160 =+        encodeHex . runPutS . serialize <$>+        case binfoRawAddr of+        BinfoAddr a -> case a of+            PubKeyAddress h        -> Just h+            ScriptAddress h        -> Just h+            WitnessPubKeyAddress h -> Just h+            _                      -> Nothing+        _ -> Nothing  binfoRawAddrParseJSON :: Network -> Value -> Parser BinfoRawAddr binfoRawAddrParseJSON net = A.withObject "balancetxs" $ \o -> do-    balanceAddress <- addrFromJSON net =<< o .: "address"-    balanceAmount <- o .: "final_balance"-    let balanceZero = 0-    balanceUnspentCount <- o .: "n_unredeemed"-    balanceTxCount <- o .: "n_tx"-    balanceTotalReceived <- o .: "total_received"-    txs <- mapM (binfoTxParseJSON net) =<< o .: "txs"-    return-        BinfoRawAddr-        {-            getBinfoRawAddrBalance = Balance{..},-            getBinfoRawAddrTxs = txs-        }+    addr <- o .: "address"+    binfoRawAddr <-+        BinfoAddr <$> addrFromJSON net addr <|>+        BinfoXpub <$> xPubFromJSON net addr+    binfoRawBalance <- o .: "final_balance"+    binfoRawUnredeemed <- o .: "n_unredeemed"+    binfoRawTxCount <- o .: "n_tx"+    binfoRawReceived <- o .: "total_received"+    binfoRawSent <- o .: "total_sent"+    binfoRawTxs <- mapM (binfoTxParseJSON net) =<< o .: "txs"+    return BinfoRawAddr{..}  data BinfoShortBal     = BinfoShortBal@@ -2649,7 +2672,7 @@  binfoBlockToJSON :: Network -> BinfoBlock -> Value binfoBlockToJSON net BinfoBlock{..} =-    A.object $+    A.object     [ "hash" .= getBinfoBlockHash     , "ver" .= getBinfoBlockVer     , "prev_block" .= getBinfoPrevBlock@@ -2754,9 +2777,9 @@   where     bal =         case getBinfoTxResultBal of-            Nothing         -> []-            Just (res, bal) -> ["result" .= res, "balance" .= bal]-    rbf = if getBinfoTxRBF then ["rbf" .= True] else []+            Nothing          -> []+            Just (res, bal') -> ["result" .= res, "balance" .= bal']+    rbf = ["rbf" .= True | getBinfoTxRBF]  binfoTxToEncoding :: Network -> BinfoTx -> Encoding binfoTxToEncoding net BinfoTx {..} =@@ -2781,8 +2804,8 @@   where     bal =         case getBinfoTxResultBal of-            Nothing         -> mempty-            Just (res, bal) -> "result" .= res <> "balance" .= bal+            Nothing          -> mempty+            Just (res, bal') -> "result" .= res <> "balance" .= bal'     rbf = if getBinfoTxRBF then "rbf" .= True else mempty  binfoTxParseJSON :: Network -> Value -> Parser BinfoTx@@ -3291,9 +3314,7 @@             received = sum (map f xs)             bal = fromIntegral (sum (map g xs))             sent = if bal <= received then received - bal else 0-            count = case HashMap.lookup k xpub_txs of-                Nothing -> 0-                Just i  -> fromIntegral i+            count = maybe 0 fromIntegral $ HashMap.lookup k xpub_txs             ax = foldl max 0 (map (i 0) xs)             cx = foldl max 0 (map (i 1) xs)         in BinfoXPubBalance{ getBinfoXPubKey = k
src/Haskoin/Store/WebClient.hs view
@@ -248,7 +248,6 @@     code = res ^. HTTP.responseStatus . HTTP.statusCode     message = res ^. HTTP.responseStatus . HTTP.statusMessage     status = mkStatus code message-    err = unwords ["Code:", show code, "Message:", cs message]     isHealthPath = "/health" `Text.isInfixOf` cs (path req)  ---------------
src/Haskoin/Store/WebCommon.hs view
@@ -4,18 +4,13 @@ {-# LANGUAGE FunctionalDependencies     #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase                 #-}-{-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE RecordWildCards            #-}-{-# LANGUAGE TupleSections              #-} module Haskoin.Store.WebCommon  where  import           Control.Applicative     ((<|>)) import           Control.Monad           (guard)-import           Data.Bytes.Get-import           Data.Bytes.Put import           Data.Bytes.Serial import           Data.Default            (Default, def) import           Data.Proxy              (Proxy (..))@@ -490,16 +485,11 @@     proxyLabel = const "derive"     encodeParam net p = do         guard (getSegWit net || p == Store.DeriveNormal)-        case p of-            Store.DeriveNormal -> Just ["standard"]-            Store.DeriveP2SH   -> Just ["compat"]-            Store.DeriveP2WPKH -> Just ["segwit"]+        Just [Store.deriveTypeToText p]     parseParam net d = do         res <- case d of-            ["standard"] -> Just Store.DeriveNormal-            ["compat"]   -> Just Store.DeriveP2SH-            ["segwit"]   -> Just Store.DeriveP2WPKH-            _            -> Nothing+            [x] -> Store.textToDeriveType x+            _   -> Nothing         guard (getSegWit net || res == Store.DeriveNormal)         return res 
test/Haskoin/Store/DataSpec.hs view
@@ -8,18 +8,14 @@     ( spec     ) where -import           Control.Monad           (forM_, replicateM)+import           Control.Monad           (forM_) import           Data.Aeson              (FromJSON (..)) import qualified Data.ByteString         as B-import qualified Data.ByteString.Short   as BSS-import qualified Data.Map.Strict         as Map-import qualified Data.Serialize          as S import           Data.String.Conversions (cs) import           Haskoin import           Haskoin.Store.Data import           Haskoin.Util.Arbitrary-import           Test.Hspec              (Spec, describe, it)-import           Test.Hspec.QuickCheck   (prop)+import           Test.Hspec              (Spec, describe) import           Test.QuickCheck  serialVals :: [SerialBox]@@ -236,20 +232,6 @@             <*> arbitrary             <*> arbitrary -arbitraryTransactionNet :: Gen (Network, Transaction)-arbitraryTransactionNet = do-    net <- arbitraryNetwork-    val <- arbitrary-    let val1 | getSegWit net = val-             | otherwise = val{ transactionInputs = f <$> transactionInputs val-                              , transactionWeight = 0-                              }-        res | getReplaceByFee net = val1-            | otherwise = val1{ transactionRBF = False }-    return (net, res)-  where-    f i = i {inputWitness = []}- instance Arbitrary PeerInformation where     arbitrary =         PeerInformation@@ -427,9 +409,18 @@  instance Arbitrary BinfoRawAddr where     arbitrary = do-        balance <- arbitrary-        txs <- arbitrary-        return $ BinfoRawAddr balance{balanceZero = 0} txs+        binfoRawAddr <-+            oneof+            [ BinfoAddr <$> arbitraryAddress+            , BinfoXpub . snd <$> arbitraryXPubKey+            ]+        binfoRawBalance <- arbitrary+        binfoRawTxCount <- arbitrary+        binfoRawUnredeemed <- arbitrary+        binfoRawReceived <- arbitrary+        binfoRawSent <- arbitrary+        binfoRawTxs <- arbitrary+        return $ BinfoRawAddr {..}  instance Arbitrary BinfoShortBal where     arbitrary = BinfoShortBal <$> arbitrary <*> arbitrary <*> arbitrary