haskoin-store-data 0.38.1 → 0.40.0
raw patch · 5 files changed
+2579/−1452 lines, 5 filesdep +unordered-containersdep ~haskoin-coredep ~haskoin-store-datadep ~textPVP ok
version bump matches the API change (PVP)
Dependencies added: unordered-containers
Dependency ranges changed: haskoin-core, haskoin-store-data, text
API changes (from Hackage documentation)
+ Haskoin.Store.Data: BinfoAddr :: !Address -> BinfoAddr
+ Haskoin.Store.Data: BinfoAddress :: !Address -> !Word64 -> !Word64 -> !Word64 -> !Word64 -> BinfoAddress
+ Haskoin.Store.Data: BinfoBlockInfo :: !BlockHash -> !BlockHeight -> !Word32 -> !BlockHeight -> BinfoBlockInfo
+ Haskoin.Store.Data: BinfoInfo :: !Word32 -> !Double -> !BinfoSymbol -> !BinfoSymbol -> !BinfoBlockInfo -> BinfoInfo
+ Haskoin.Store.Data: BinfoMultiAddr :: ![BinfoAddress] -> !BinfoWallet -> ![BinfoTx] -> !BinfoInfo -> !Bool -> !Bool -> BinfoMultiAddr
+ Haskoin.Store.Data: BinfoSpender :: !BinfoTxId -> !Word32 -> BinfoSpender
+ Haskoin.Store.Data: BinfoSymbol :: !Text -> !Text -> !Text -> !Double -> !Bool -> !Bool -> BinfoSymbol
+ Haskoin.Store.Data: BinfoTicker :: !Text -> !Double -> !Double -> !Double -> BinfoTicker
+ Haskoin.Store.Data: BinfoTx :: !TxHash -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word64 -> !ByteString -> !Word32 -> !BinfoTxId -> !Bool -> !Maybe (Int64, Int64) -> !Word64 -> !Maybe Word32 -> !Maybe Word32 -> [BinfoTxInput] -> [BinfoTxOutput] -> BinfoTx
+ Haskoin.Store.Data: BinfoTxIdHash :: !TxHash -> BinfoTxId
+ Haskoin.Store.Data: BinfoTxIdIndex :: !BinfoTxIndex -> BinfoTxId
+ Haskoin.Store.Data: BinfoTxInput :: !Word32 -> !ByteString -> !ByteString -> !Word32 -> !Maybe BinfoTxOutput -> BinfoTxInput
+ Haskoin.Store.Data: BinfoTxOutput :: !Int -> !Bool -> !Word64 -> !Word32 -> !BinfoTxId -> !ByteString -> ![BinfoSpender] -> !Maybe Address -> !Maybe BinfoXPubPath -> BinfoTxOutput
+ Haskoin.Store.Data: BinfoWallet :: !Word64 -> !Word64 -> !Word64 -> !Word64 -> !Word64 -> BinfoWallet
+ Haskoin.Store.Data: BinfoXPubKey :: !XPubKey -> !Word64 -> !Word64 -> !Word64 -> !Word64 -> !Word32 -> !Word32 -> BinfoAddress
+ Haskoin.Store.Data: BinfoXPubPath :: !XPubKey -> !SoftPath -> BinfoXPubPath
+ Haskoin.Store.Data: BinfoXpub :: !XPubKey -> BinfoAddr
+ Haskoin.Store.Data: [binfoTickerLastPrice] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [binfoTickerPrice24h] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [binfoTickerSymbol] :: BinfoTicker -> !Text
+ Haskoin.Store.Data: [binfoTickerVol24h] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [getBinfoAddrBalance] :: BinfoAddress -> !Word64
+ Haskoin.Store.Data: [getBinfoAddrReceived] :: BinfoAddress -> !Word64
+ Haskoin.Store.Data: [getBinfoAddrSent] :: BinfoAddress -> !Word64
+ Haskoin.Store.Data: [getBinfoAddrTxCount] :: BinfoAddress -> !Word64
+ Haskoin.Store.Data: [getBinfoAddress] :: BinfoAddress -> !Address
+ Haskoin.Store.Data: [getBinfoBTC] :: BinfoInfo -> !BinfoSymbol
+ Haskoin.Store.Data: [getBinfoBlockInfoHash] :: BinfoBlockInfo -> !BlockHash
+ Haskoin.Store.Data: [getBinfoBlockInfoHeight] :: BinfoBlockInfo -> !BlockHeight
+ Haskoin.Store.Data: [getBinfoBlockInfoIndex] :: BinfoBlockInfo -> !BlockHeight
+ Haskoin.Store.Data: [getBinfoBlockInfoTime] :: BinfoBlockInfo -> !Word32
+ Haskoin.Store.Data: [getBinfoConnected] :: BinfoInfo -> !Word32
+ Haskoin.Store.Data: [getBinfoConversion] :: BinfoInfo -> !Double
+ Haskoin.Store.Data: [getBinfoLatestBlock] :: BinfoInfo -> !BinfoBlockInfo
+ Haskoin.Store.Data: [getBinfoLocal] :: BinfoInfo -> !BinfoSymbol
+ Haskoin.Store.Data: [getBinfoMultiAddrAddresses] :: BinfoMultiAddr -> ![BinfoAddress]
+ Haskoin.Store.Data: [getBinfoMultiAddrCashAddr] :: BinfoMultiAddr -> !Bool
+ Haskoin.Store.Data: [getBinfoMultiAddrInfo] :: BinfoMultiAddr -> !BinfoInfo
+ Haskoin.Store.Data: [getBinfoMultiAddrRecommendFee] :: BinfoMultiAddr -> !Bool
+ Haskoin.Store.Data: [getBinfoMultiAddrTxs] :: BinfoMultiAddr -> ![BinfoTx]
+ Haskoin.Store.Data: [getBinfoMultiAddrWallet] :: BinfoMultiAddr -> !BinfoWallet
+ Haskoin.Store.Data: [getBinfoSpenderIndex] :: BinfoSpender -> !Word32
+ Haskoin.Store.Data: [getBinfoSpenderTxIndex] :: BinfoSpender -> !BinfoTxId
+ Haskoin.Store.Data: [getBinfoSymbolAfter] :: BinfoSymbol -> !Bool
+ Haskoin.Store.Data: [getBinfoSymbolCode] :: BinfoSymbol -> !Text
+ Haskoin.Store.Data: [getBinfoSymbolConversion] :: BinfoSymbol -> !Double
+ Haskoin.Store.Data: [getBinfoSymbolLocal] :: BinfoSymbol -> !Bool
+ Haskoin.Store.Data: [getBinfoSymbolName] :: BinfoSymbol -> !Text
+ Haskoin.Store.Data: [getBinfoSymbolString] :: BinfoSymbol -> !Text
+ Haskoin.Store.Data: [getBinfoTxBlockHeight] :: BinfoTx -> !Maybe Word32
+ Haskoin.Store.Data: [getBinfoTxBlockIndex] :: BinfoTx -> !Maybe Word32
+ Haskoin.Store.Data: [getBinfoTxDoubleSpend] :: BinfoTx -> !Bool
+ Haskoin.Store.Data: [getBinfoTxFee] :: BinfoTx -> !Word64
+ Haskoin.Store.Data: [getBinfoTxHash] :: BinfoTx -> !TxHash
+ Haskoin.Store.Data: [getBinfoTxIndex] :: BinfoTx -> !BinfoTxId
+ Haskoin.Store.Data: [getBinfoTxInputIndex] :: BinfoTxInput -> !Word32
+ Haskoin.Store.Data: [getBinfoTxInputPrevOut] :: BinfoTxInput -> !Maybe BinfoTxOutput
+ Haskoin.Store.Data: [getBinfoTxInputScript] :: BinfoTxInput -> !ByteString
+ Haskoin.Store.Data: [getBinfoTxInputSeq] :: BinfoTxInput -> !Word32
+ Haskoin.Store.Data: [getBinfoTxInputWitness] :: BinfoTxInput -> !ByteString
+ Haskoin.Store.Data: [getBinfoTxInputs] :: BinfoTx -> [BinfoTxInput]
+ Haskoin.Store.Data: [getBinfoTxLockTime] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [getBinfoTxOutputAddress] :: BinfoTxOutput -> !Maybe Address
+ Haskoin.Store.Data: [getBinfoTxOutputIndex] :: BinfoTxOutput -> !Word32
+ Haskoin.Store.Data: [getBinfoTxOutputScript] :: BinfoTxOutput -> !ByteString
+ Haskoin.Store.Data: [getBinfoTxOutputSpenders] :: BinfoTxOutput -> ![BinfoSpender]
+ Haskoin.Store.Data: [getBinfoTxOutputSpent] :: BinfoTxOutput -> !Bool
+ Haskoin.Store.Data: [getBinfoTxOutputTxIndex] :: BinfoTxOutput -> !BinfoTxId
+ Haskoin.Store.Data: [getBinfoTxOutputType] :: BinfoTxOutput -> !Int
+ Haskoin.Store.Data: [getBinfoTxOutputValue] :: BinfoTxOutput -> !Word64
+ Haskoin.Store.Data: [getBinfoTxOutputXPub] :: BinfoTxOutput -> !Maybe BinfoXPubPath
+ Haskoin.Store.Data: [getBinfoTxOutputs] :: BinfoTx -> [BinfoTxOutput]
+ Haskoin.Store.Data: [getBinfoTxRelayedBy] :: BinfoTx -> !ByteString
+ Haskoin.Store.Data: [getBinfoTxResultBal] :: BinfoTx -> !Maybe (Int64, Int64)
+ Haskoin.Store.Data: [getBinfoTxSize] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [getBinfoTxTime] :: BinfoTx -> !Word64
+ Haskoin.Store.Data: [getBinfoTxVer] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [getBinfoTxVinSz] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [getBinfoTxVoutSz] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [getBinfoTxWeight] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [getBinfoWalletBalance] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [getBinfoWalletFilteredCount] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [getBinfoWalletTotalReceived] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [getBinfoWalletTotalSent] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [getBinfoWalletTxCount] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [getBinfoXPubAccountIndex] :: BinfoAddress -> !Word32
+ Haskoin.Store.Data: [getBinfoXPubChangeIndex] :: BinfoAddress -> !Word32
+ Haskoin.Store.Data: [getBinfoXPubKey] :: BinfoAddress -> !XPubKey
+ Haskoin.Store.Data: [getBinfoXPubPathDeriv] :: BinfoXPubPath -> !SoftPath
+ Haskoin.Store.Data: [getBinfoXPubPathKey] :: BinfoXPubPath -> !XPubKey
+ Haskoin.Store.Data: binfoAddressParseJSON :: Network -> Value -> Parser BinfoAddress
+ Haskoin.Store.Data: binfoAddressToEncoding :: Network -> BinfoAddress -> Encoding
+ Haskoin.Store.Data: binfoAddressToJSON :: Network -> BinfoAddress -> Value
+ Haskoin.Store.Data: binfoMultiAddrParseJSON :: Network -> Value -> Parser BinfoMultiAddr
+ Haskoin.Store.Data: binfoMultiAddrToEncoding :: Network -> BinfoMultiAddr -> Encoding
+ Haskoin.Store.Data: binfoMultiAddrToJSON :: Network -> BinfoMultiAddr -> Value
+ Haskoin.Store.Data: binfoTransactionIndex :: Bool -> Transaction -> BinfoTxId
+ Haskoin.Store.Data: binfoTxIndexBlock :: BinfoTxIndex -> Maybe (BlockHeight, Word32)
+ Haskoin.Store.Data: binfoTxInputParseJSON :: Network -> Value -> Parser BinfoTxInput
+ Haskoin.Store.Data: binfoTxInputToEncoding :: Network -> BinfoTxInput -> Encoding
+ Haskoin.Store.Data: binfoTxInputToJSON :: Network -> BinfoTxInput -> Value
+ Haskoin.Store.Data: binfoTxOutputParseJSON :: Network -> Value -> Parser BinfoTxOutput
+ Haskoin.Store.Data: binfoTxOutputToEncoding :: Network -> BinfoTxOutput -> Encoding
+ Haskoin.Store.Data: binfoTxOutputToJSON :: Network -> BinfoTxOutput -> Value
+ Haskoin.Store.Data: binfoTxParseJSON :: Network -> Value -> Parser BinfoTx
+ Haskoin.Store.Data: binfoTxToEncoding :: Network -> BinfoTx -> Encoding
+ Haskoin.Store.Data: binfoTxToJSON :: Network -> BinfoTx -> Value
+ Haskoin.Store.Data: binfoXPubPathParseJSON :: Network -> Value -> Parser BinfoXPubPath
+ Haskoin.Store.Data: binfoXPubPathToEncoding :: Network -> BinfoXPubPath -> Encoding
+ Haskoin.Store.Data: binfoXPubPathToJSON :: Network -> BinfoXPubPath -> Value
+ Haskoin.Store.Data: blockToBinfoTxIndex :: BlockHeight -> Word32 -> BinfoTxIndex
+ Haskoin.Store.Data: data BinfoAddr
+ Haskoin.Store.Data: data BinfoAddress
+ Haskoin.Store.Data: data BinfoBlockInfo
+ Haskoin.Store.Data: data BinfoInfo
+ Haskoin.Store.Data: data BinfoMultiAddr
+ Haskoin.Store.Data: data BinfoSpender
+ Haskoin.Store.Data: data BinfoSymbol
+ Haskoin.Store.Data: data BinfoTicker
+ Haskoin.Store.Data: data BinfoTx
+ Haskoin.Store.Data: data BinfoTxId
+ Haskoin.Store.Data: data BinfoTxInput
+ Haskoin.Store.Data: data BinfoTxOutput
+ Haskoin.Store.Data: data BinfoWallet
+ Haskoin.Store.Data: data BinfoXPubPath
+ Haskoin.Store.Data: encodeBinfoTxIndexHash :: BinfoTxIndex -> Maybe ByteString
+ Haskoin.Store.Data: hashToBinfoTxIndex :: TxHash -> BinfoTxIndex
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoAddr
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoAddress
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoInfo
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoMultiAddr
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoSymbol
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoTx
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoTxInput
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoTxOutput
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoXPubPath
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoInfo
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoSymbol
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoInfo
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoSymbol
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance Data.Default.Class.Default Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance Data.Hashable.Class.Hashable Haskoin.Store.Data.BinfoAddr
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoAddr
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoAddress
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoTx
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoTxInput
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoTxOutput
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.BinfoXPubPath
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoAddr
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoAddress
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoInfo
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoMultiAddr
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoSymbol
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoTx
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoTxInput
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoTxOutput
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoXPubPath
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoAddr
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoAddress
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoInfo
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoMultiAddr
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoSymbol
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoTx
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoTxInput
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoTxOutput
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoXPubPath
+ Haskoin.Store.Data: instance GHC.Read.Read Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoAddr
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoAddress
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoBlockInfo
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoInfo
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoMultiAddr
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoSpender
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoSymbol
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoTicker
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoTx
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoTxInput
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoTxOutput
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoWallet
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoXPubPath
+ Haskoin.Store.Data: instance Web.Scotty.Action.Parsable Haskoin.Store.Data.BinfoTxId
+ Haskoin.Store.Data: isBinfoTxIndexBlock :: BinfoTxIndex -> Bool
+ Haskoin.Store.Data: isBinfoTxIndexHash :: BinfoTxIndex -> Bool
+ Haskoin.Store.Data: isBinfoTxIndexNull :: BinfoTxIndex -> Bool
+ Haskoin.Store.Data: matchBinfoTxHash :: Int64 -> TxHash -> Bool
+ Haskoin.Store.Data: parseBinfoAddr :: Network -> Text -> Maybe [BinfoAddr]
+ Haskoin.Store.Data: relevantTxs :: HashSet Address -> Bool -> Transaction -> HashSet TxHash
+ Haskoin.Store.Data: toBinfoAddrs :: HashMap Address Balance -> HashMap XPubKey [XPubBal] -> HashMap XPubKey Int -> [BinfoAddress]
+ Haskoin.Store.Data: toBinfoTx :: Maybe (HashMap TxHash Transaction) -> HashMap Address (Maybe BinfoXPubPath) -> HashSet Address -> Bool -> Int64 -> Transaction -> BinfoTx
+ Haskoin.Store.Data: toBinfoTxSimple :: Maybe (HashMap TxHash Transaction) -> Transaction -> BinfoTx
+ Haskoin.Store.Data: type BinfoTxIndex = Int64
- Haskoin.Store.Data: StoreCoinbase :: !OutPoint -> !Word32 -> !ByteString -> !Maybe WitnessStack -> StoreInput
+ Haskoin.Store.Data: StoreCoinbase :: !OutPoint -> !Word32 -> !ByteString -> !WitnessStack -> StoreInput
- Haskoin.Store.Data: StoreInput :: !OutPoint -> !Word32 -> !ByteString -> !ByteString -> !Word64 -> !Maybe WitnessStack -> !Maybe Address -> StoreInput
+ Haskoin.Store.Data: StoreInput :: !OutPoint -> !Word32 -> !ByteString -> !ByteString -> !Word64 -> !WitnessStack -> !Maybe Address -> StoreInput
- Haskoin.Store.Data: [inputWitness] :: StoreInput -> !Maybe WitnessStack
+ Haskoin.Store.Data: [inputWitness] :: StoreInput -> !WitnessStack
Files
- haskoin-store-data.cabal +9/−7
- src/Haskoin/Store/Data.hs +2346/−1417
- src/Haskoin/Store/WebCommon.hs +26/−19
- test/Haskoin/Store/DataSpec.hs +195/−7
- test/Haskoin/Store/WebCommonSpec.hs +3/−2
haskoin-store-data.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: d47cb9ae7ff346dd501859f21e3e9a0d8294ce49157710e2cd91e085c13665cd+-- hash: 84464e988a1908c7ad47ff0186701bdffc077925760e73de350b8aab941faee3 name: haskoin-store-data-version: 0.38.1+version: 0.40.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@@ -41,7 +41,7 @@ , data-default >=0.7.1.1 , deepseq >=1.4.4.0 , hashable >=1.3.0.0- , haskoin-core >=0.17.0+ , haskoin-core >=0.19.0 , http-client >=0.6.4.1 , http-types >=0.12.3 , lens >=4.18.1@@ -49,7 +49,8 @@ , network >=3.1.1.1 , scotty >=0.11.5 , string-conversions >=0.4.0.1- , text >=1.2.4.0+ , text >=1.2.3.0+ , unordered-containers >=0.2.10 , wreq >=0.5.3.2 default-language: Haskell2010 @@ -73,8 +74,8 @@ , data-default >=0.7.1.1 , deepseq >=1.4.4.0 , hashable >=1.3.0.0- , haskoin-core >=0.17.0- , haskoin-store-data ==0.38.1+ , haskoin-core >=0.19.0+ , haskoin-store-data , hspec >=2.7.1 , http-client >=0.6.4.1 , http-types >=0.12.3@@ -83,7 +84,8 @@ , network >=3.1.1.1 , scotty >=0.11.5 , string-conversions >=0.4.0.1- , text >=1.2.4.0+ , text >=1.2.3.0+ , unordered-containers >=0.2.10 , wreq >=0.5.3.2 default-language: Haskell2010 build-tool-depends: hspec-discover:hspec-discover
src/Haskoin/Store/Data.hs view
@@ -80,1420 +80,2349 @@ , HealthCheck(..) , Event(..) , Except(..)- )--where--import Control.Applicative ((<|>))-import Control.DeepSeq (NFData)-import Control.Exception (Exception)-import Control.Monad (guard, join, mzero, (<=<))-import Data.Aeson (Encoding, FromJSON (..), ToJSON (..),- Value (..), object, pairs, (.!=),- (.:), (.:?), (.=))-import qualified Data.Aeson as A-import Data.Aeson.Encoding (list, null_, pair, text,- unsafeToEncoding)-import Data.Aeson.Types (Parser)-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import Data.ByteString.Builder (char7, lazyByteStringHex)-import Data.ByteString.Short (ShortByteString)-import qualified Data.ByteString.Short as BSS-import Data.Default (Default (..))-import Data.Foldable (toList)-import Data.Hashable (Hashable (..))-import qualified Data.IntMap as I-import Data.IntMap.Strict (IntMap)-import Data.Maybe (catMaybes, fromMaybe, isJust,- mapMaybe)-import Data.Serialize (Get, Put, Serialize (..), getWord32be,- getWord64be, getWord8, putWord32be,- putWord64be, putWord8)-import qualified Data.Serialize as S-import Data.String.Conversions (cs)-import Data.Text (Text)-import qualified Data.Text.Lazy as TL-import Data.Word (Word32, Word64)-import GHC.Generics (Generic)-import Haskoin (Address, BlockHash, BlockHeader (..),- BlockHeight, BlockWork, Coin (..),- KeyIndex, Network (..), OutPoint (..),- PubKeyI (..), Tx (..), TxHash (..),- TxIn (..), TxOut (..), WitnessStack,- XPubKey (..), addrFromJSON,- addrToEncoding, addrToJSON,- blockHashToHex, decodeHex,- eitherToMaybe, encodeHex, headerHash,- scriptToAddressBS, txHash,- txHashToHex, wrapPubKey)-import Web.Scotty.Trans (ScottyError (..))--data DeriveType = DeriveNormal- | DeriveP2SH- | DeriveP2WPKH- deriving (Show, Eq, Generic, NFData, Serialize)--instance Default DeriveType where- def = DeriveNormal--data XPubSpec =- XPubSpec- { xPubSpecKey :: !XPubKey- , xPubDeriveType :: !DeriveType- }- deriving (Show, Eq, Generic, NFData)--instance Hashable XPubSpec where- hashWithSalt i XPubSpec {xPubSpecKey = XPubKey {xPubKey = pubkey}} =- hashWithSalt i pubkey--instance Serialize XPubSpec where- put XPubSpec {xPubSpecKey = k, xPubDeriveType = t} = do- put (xPubDepth k)- put (xPubParent k)- put (xPubIndex k)- put (xPubChain k)- put (wrapPubKey True (xPubKey k))- put t- get = do- d <- get- p <- get- i <- get- c <- get- k <- get- t <- get- let x =- XPubKey- { xPubDepth = d- , xPubParent = p- , xPubIndex = i- , xPubChain = c- , xPubKey = pubKeyPoint k- }- return XPubSpec {xPubSpecKey = x, xPubDeriveType = t}--type UnixTime = Word64-type BlockPos = Word32---- | Serialize such that ordering is inverted.-putUnixTime :: Word64 -> Put-putUnixTime w = putWord64be $ maxBound - w--getUnixTime :: Get Word64-getUnixTime = (maxBound -) <$> getWord64be---- | Reference to a block where a transaction is stored.-data BlockRef- = BlockRef- { blockRefHeight :: !BlockHeight- -- ^ block height in the chain- , blockRefPos :: !Word32- -- ^ position of transaction within the block- }- | MemRef- { memRefTime :: !UnixTime- }- deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)---- | Serialized entities will sort in reverse order.-instance Serialize BlockRef where- put MemRef {memRefTime = t} = do- putWord8 0x00- putUnixTime t- put BlockRef {blockRefHeight = h, blockRefPos = p} = do- putWord8 0x01- putWord32be (maxBound - h)- putWord32be (maxBound - p)- get = getmemref <|> getblockref- where- getmemref = do- guard . (== 0x00) =<< getWord8- MemRef <$> getUnixTime- getblockref = do- guard . (== 0x01) =<< getWord8- h <- (maxBound -) <$> getWord32be- p <- (maxBound -) <$> getWord32be- return BlockRef {blockRefHeight = h, blockRefPos = p}--confirmed :: BlockRef -> Bool-confirmed BlockRef {} = True-confirmed MemRef {} = False--instance ToJSON BlockRef where- toJSON BlockRef {blockRefHeight = h, blockRefPos = p} =- object ["height" .= h, "position" .= p]- toJSON MemRef {memRefTime = t} = object ["mempool" .= t]- toEncoding BlockRef {blockRefHeight = h, blockRefPos = p} =- pairs ("height" .= h <> "position" .= p)- toEncoding MemRef {memRefTime = t} = pairs ("mempool" .= t)--instance FromJSON BlockRef where- parseJSON = A.withObject "blockref" $ \o -> b o <|> m o- where- b o = do- height <- o .: "height"- position <- o .: "position"- return BlockRef {blockRefHeight = height, blockRefPos = position}- m o = do- mempool <- o .: "mempool"- return MemRef {memRefTime = mempool}---- | Transaction in relation to an address.-data TxRef =- TxRef- { txRefBlock :: !BlockRef- -- ^ block information- , txRefHash :: !TxHash- -- ^ transaction hash- }- deriving (Show, Eq, Ord, Generic, Serialize, Hashable, NFData)--instance ToJSON TxRef where- toJSON btx = object ["txid" .= txRefHash btx, "block" .= txRefBlock btx]- toEncoding btx =- pairs- ( "txid" .= txRefHash btx- <> "block" .= txRefBlock btx- )--instance FromJSON TxRef where- parseJSON =- A.withObject "blocktx" $ \o -> do- txid <- o .: "txid"- block <- o .: "block"- return TxRef {txRefBlock = block, txRefHash = txid}---- | Address balance information.-data Balance =- Balance- { balanceAddress :: !Address- -- ^ address balance- , balanceAmount :: !Word64- -- ^ confirmed balance- , balanceZero :: !Word64- -- ^ unconfirmed balance- , balanceUnspentCount :: !Word64- -- ^ number of unspent outputs- , balanceTxCount :: !Word64- -- ^ number of transactions- , balanceTotalReceived :: !Word64- -- ^ total amount from all outputs in this address- }- deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)--zeroBalance :: Address -> Balance-zeroBalance a =- Balance- { balanceAddress = a- , balanceAmount = 0- , balanceUnspentCount = 0- , balanceZero = 0- , balanceTxCount = 0- , balanceTotalReceived = 0- }--nullBalance :: Balance -> Bool-nullBalance Balance { balanceAmount = 0- , balanceUnspentCount = 0- , balanceZero = 0- , balanceTxCount = 0- , balanceTotalReceived = 0- } = True-nullBalance _ = False--balanceToJSON :: Network -> Balance -> Value-balanceToJSON net b =- object- [ "address" .= addrToJSON net (balanceAddress b)- , "confirmed" .= balanceAmount b- , "unconfirmed" .= balanceZero b- , "utxo" .= balanceUnspentCount b- , "txs" .= balanceTxCount b- , "received" .= balanceTotalReceived b- ]--balanceToEncoding :: Network -> Balance -> Encoding-balanceToEncoding net b =- pairs- ( "address" `pair` addrToEncoding net (balanceAddress b)- <> "confirmed" .= balanceAmount b- <> "unconfirmed" .= balanceZero b- <> "utxo" .= balanceUnspentCount b- <> "txs" .= balanceTxCount b- <> "received" .= balanceTotalReceived b- )--balanceParseJSON :: Network -> Value -> Parser Balance-balanceParseJSON net =- A.withObject "balance" $ \o -> do- amount <- o .: "confirmed"- unconfirmed <- o .: "unconfirmed"- utxo <- o .: "utxo"- txs <- o .: "txs"- received <- o .: "received"- address <- addrFromJSON net =<< o .: "address"- return- Balance- { balanceAddress = address- , balanceAmount = amount- , balanceUnspentCount = utxo- , balanceZero = unconfirmed- , balanceTxCount = txs- , balanceTotalReceived = received- }---- | Unspent output.-data Unspent =- Unspent- { unspentBlock :: !BlockRef- , unspentPoint :: !OutPoint- , unspentAmount :: !Word64- , unspentScript :: !ShortByteString- , unspentAddress :: !(Maybe Address)- }- deriving (Show, Eq, Ord, Generic, Hashable, Serialize, NFData)--instance Coin Unspent where- coinValue = unspentAmount--unspentToJSON :: Network -> Unspent -> Value-unspentToJSON net u =- object- [ "address" .= (addrToJSON net <$> unspentAddress u)- , "block" .= unspentBlock u- , "txid" .= outPointHash (unspentPoint u)- , "index" .= outPointIndex (unspentPoint u)- , "pkscript" .= script- , "value" .= unspentAmount u- ]- where- bsscript = BSS.fromShort (unspentScript u)- script = encodeHex bsscript--unspentToEncoding :: Network -> Unspent -> Encoding-unspentToEncoding net u =- pairs- ( "address" `pair` maybe null_ (addrToEncoding net) (unspentAddress u)- <> "block" .= unspentBlock u- <> "txid" .= outPointHash (unspentPoint u)- <> "index" .= outPointIndex (unspentPoint u)- <> "pkscript" `pair` text script- <> "value" .= unspentAmount u- )- where- bsscript = BSS.fromShort (unspentScript u)- script = encodeHex bsscript--unspentParseJSON :: Network -> Value -> Parser Unspent-unspentParseJSON net =- A.withObject "unspent" $ \o -> do- block <- o .: "block"- txid <- o .: "txid"- index <- o .: "index"- value <- o .: "value"- script <- BSS.toShort <$> (o .: "pkscript" >>= jsonHex)- addr <- o .: "address" >>= \case- Nothing -> return Nothing- Just a -> Just <$> addrFromJSON net a <|> return Nothing- return- Unspent- { unspentBlock = block- , unspentPoint = OutPoint txid index- , unspentAmount = value- , unspentScript = script- , unspentAddress = addr- }---- | Database value for a block entry.-data BlockData =- BlockData- { blockDataHeight :: !BlockHeight- -- ^ height of the block in the chain- , blockDataMainChain :: !Bool- -- ^ is this block in the main chain?- , blockDataWork :: !BlockWork- -- ^ accumulated work in that block- , blockDataHeader :: !BlockHeader- -- ^ block header- , blockDataSize :: !Word32- -- ^ size of the block including witnesses- , blockDataWeight :: !Word32- -- ^ weight of this block (for segwit networks)- , blockDataTxs :: ![TxHash]- -- ^ block transactions- , blockDataOutputs :: !Word64- -- ^ sum of all transaction outputs- , blockDataFees :: !Word64- -- ^ sum of all transaction fees- , blockDataSubsidy :: !Word64- -- ^ block subsidy- }- deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)--blockDataToJSON :: Network -> BlockData -> Value-blockDataToJSON net bv =- object $- [ "hash" .= headerHash (blockDataHeader bv)- , "height" .= blockDataHeight bv- , "mainchain" .= blockDataMainChain bv- , "previous" .= prevBlock (blockDataHeader bv)- , "time" .= blockTimestamp (blockDataHeader bv)- , "version" .= blockVersion (blockDataHeader bv)- , "bits" .= blockBits (blockDataHeader bv)- , "nonce" .= bhNonce (blockDataHeader bv)- , "size" .= blockDataSize bv- , "tx" .= blockDataTxs bv- , "merkle" .= TxHash (merkleRoot (blockDataHeader bv))- , "subsidy" .= blockDataSubsidy bv- , "fees" .= blockDataFees bv- , "outputs" .= blockDataOutputs bv- , "work" .= blockDataWork bv- ] <>- ["weight" .= blockDataWeight bv | getSegWit net]--blockDataToEncoding :: Network -> BlockData -> Encoding-blockDataToEncoding net bv =- pairs- ( "hash" `pair` text (blockHashToHex (headerHash (blockDataHeader bv)))- <> "height" .= blockDataHeight bv- <> "mainchain" .= blockDataMainChain bv- <> "previous" .= prevBlock (blockDataHeader bv)- <> "time" .= blockTimestamp (blockDataHeader bv)- <> "version" .= blockVersion (blockDataHeader bv)- <> "bits" .= blockBits (blockDataHeader bv)- <> "nonce" .= bhNonce (blockDataHeader bv)- <> "size" .= blockDataSize bv- <> "tx" .= blockDataTxs bv- <> "merkle" `pair` text (txHashToHex (TxHash (merkleRoot (blockDataHeader bv))))- <> "subsidy" .= blockDataSubsidy bv- <> "fees" .= blockDataFees bv- <> "outputs" .= blockDataOutputs bv- <> "work" .= blockDataWork bv- <> (if getSegWit net then "weight" .= blockDataWeight bv else mempty)- )--instance FromJSON BlockData where- parseJSON =- A.withObject "blockdata" $ \o -> do- height <- o .: "height"- mainchain <- o .: "mainchain"- previous <- o .: "previous"- time <- o .: "time"- version <- o .: "version"- bits <- o .: "bits"- nonce <- o .: "nonce"- size <- o .: "size"- tx <- o .: "tx"- TxHash merkle <- o .: "merkle"- subsidy <- o .: "subsidy"- fees <- o .: "fees"- outputs <- o .: "outputs"- work <- o .: "work"- weight <- o .:? "weight" .!= 0- return- BlockData- { blockDataHeader =- BlockHeader- { prevBlock = previous- , blockTimestamp = time- , blockVersion = version- , blockBits = bits- , bhNonce = nonce- , merkleRoot = merkle- }- , blockDataMainChain = mainchain- , blockDataWork = work- , blockDataSize = size- , blockDataWeight = weight- , blockDataTxs = tx- , blockDataOutputs = outputs- , blockDataFees = fees- , blockDataHeight = height- , blockDataSubsidy = subsidy- }--data StoreInput- = StoreCoinbase- { inputPoint :: !OutPoint- , inputSequence :: !Word32- , inputSigScript :: !ByteString- , inputWitness :: !(Maybe WitnessStack)- }- | StoreInput- { inputPoint :: !OutPoint- , inputSequence :: !Word32- , inputSigScript :: !ByteString- , inputPkScript :: !ByteString- , inputAmount :: !Word64- , inputWitness :: !(Maybe WitnessStack)- , inputAddress :: !(Maybe Address)- }- deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)--isCoinbase :: StoreInput -> Bool-isCoinbase StoreCoinbase {} = True-isCoinbase StoreInput {} = False--storeInputToJSON :: Network -> StoreInput -> Value-storeInputToJSON net StoreInput { inputPoint = OutPoint oph opi- , inputSequence = sq- , inputSigScript = ss- , inputPkScript = ps- , inputAmount = val- , inputWitness = wit- , inputAddress = a- } =- object $- [ "coinbase" .= False- , "txid" .= oph- , "output" .= opi- , "sigscript" .= String (encodeHex ss)- , "sequence" .= sq- , "pkscript" .= String (encodeHex ps)- , "value" .= val- , "address" .= (addrToJSON net <$> a)- ] <>- ["witness" .= fmap (map encodeHex) wit | getSegWit net]-storeInputToJSON net StoreCoinbase { inputPoint = OutPoint oph opi- , inputSequence = sq- , inputSigScript = ss- , inputWitness = wit- } =- object $- [ "coinbase" .= True- , "txid" .= oph- , "output" .= opi- , "sigscript" .= String (encodeHex ss)- , "sequence" .= sq- , "pkscript" .= Null- , "value" .= Null- , "address" .= Null- ] <>- ["witness" .= fmap (map encodeHex) wit | getSegWit net]--storeInputToEncoding :: Network -> StoreInput -> Encoding-storeInputToEncoding net StoreInput { inputPoint = OutPoint oph opi- , inputSequence = sq- , inputSigScript = ss- , inputPkScript = ps- , inputAmount = val- , inputWitness = wit- , inputAddress = a- } =- pairs- ( "coinbase" .= False- <> "txid" .= oph- <> "output" .= opi- <> "sigscript" `pair` text (encodeHex ss)- <> "sequence" .= sq- <> "pkscript" `pair` text (encodeHex ps)- <> "value" .= val- <> "address" `pair` maybe null_ (addrToEncoding net) a- <> (if getSegWit net- then "witness" .= fmap (map encodeHex) wit- else mempty)- )-storeInputToEncoding net StoreCoinbase { inputPoint = OutPoint oph opi- , inputSequence = sq- , inputSigScript = ss- , inputWitness = wit- } =- pairs- ( "coinbase" .= True- <> "txid" `pair` text (txHashToHex oph)- <> "output" .= opi- <> "sigscript" `pair` text (encodeHex ss)- <> "sequence" .= sq- <> "pkscript" `pair` null_- <> "value" `pair` null_- <> "address" `pair` null_- <> (if getSegWit net- then "witness" .= fmap (map encodeHex) wit- else mempty)- )--storeInputParseJSON :: Network -> Value -> Parser StoreInput-storeInputParseJSON net =- A.withObject "storeinput" $ \o -> do- coinbase <- o .: "coinbase"- outpoint <- OutPoint <$> o .: "txid" <*> o .: "output"- sequ <- o .: "sequence"- witness <-- o .:? "witness" >>= \mmxs ->- case join mmxs of- Nothing -> return Nothing- Just xs -> Just <$> mapM jsonHex xs- sigscript <- o .: "sigscript" >>= jsonHex- if coinbase- then return- StoreCoinbase- { inputPoint = outpoint- , inputSequence = sequ- , inputSigScript = sigscript- , inputWitness = witness- }- else do- pkscript <- o .: "pkscript" >>= jsonHex- value <- o .: "value"- addr <- o .: "address" >>= \case- Nothing -> return Nothing- Just a -> Just <$> addrFromJSON net a <|> return Nothing- return- StoreInput- { inputPoint = outpoint- , inputSequence = sequ- , inputSigScript = sigscript- , inputPkScript = pkscript- , inputAmount = value- , inputWitness = witness- , inputAddress = addr- }--jsonHex :: Text -> Parser ByteString-jsonHex s =- case decodeHex s of- Nothing -> fail "Could not decode hex"- Just b -> return b---- | Information about input spending output.-data Spender =- Spender- { spenderHash :: !TxHash- -- ^ input transaction hash- , spenderIndex :: !Word32- -- ^ input position in transaction- }- deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)--instance ToJSON Spender where- toJSON n = object ["txid" .= txHashToHex (spenderHash n), "input" .= spenderIndex n]- toEncoding n = pairs ("txid" .= txHashToHex (spenderHash n) <> "input" .= spenderIndex n)--instance FromJSON Spender where- parseJSON =- A.withObject "spender" $ \o -> Spender <$> o .: "txid" <*> o .: "input"---- | Output information.-data StoreOutput =- StoreOutput- { outputAmount :: !Word64- , outputScript :: !ByteString- , outputSpender :: !(Maybe Spender)- , outputAddress :: !(Maybe Address)- }- deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)--storeOutputToJSON :: Network -> StoreOutput -> Value-storeOutputToJSON net d =- object- [ "address" .= (addrToJSON net <$> outputAddress d)- , "pkscript" .= encodeHex (outputScript d)- , "value" .= outputAmount d- , "spent" .= isJust (outputSpender d)- , "spender" .= outputSpender d- ]--storeOutputToEncoding :: Network -> StoreOutput -> Encoding-storeOutputToEncoding net d =- pairs- ( "address" `pair` maybe null_ (addrToEncoding net) (outputAddress d)- <> "pkscript" `pair` text (encodeHex (outputScript d))- <> "value" .= outputAmount d- <> "spent" .= isJust (outputSpender d)- <> "spender" .= outputSpender d- )--storeOutputParseJSON :: Network -> Value -> Parser StoreOutput-storeOutputParseJSON net =- A.withObject "storeoutput" $ \o -> do- value <- o .: "value"- pkscript <- o .: "pkscript" >>= jsonHex- spender <- o .: "spender"- addr <- o .: "address" >>= \case- Nothing -> return Nothing- Just a -> Just <$> addrFromJSON net a <|> return Nothing- return- StoreOutput- { outputAmount = value- , outputScript = pkscript- , outputSpender = spender- , outputAddress = addr- }--data Prev =- Prev- { prevScript :: !ByteString- , prevAmount :: !Word64- }- deriving (Show, Eq, Ord, Generic, Hashable, Serialize, NFData)--toInput :: TxIn -> Maybe Prev -> Maybe WitnessStack -> StoreInput-toInput i Nothing w =- StoreCoinbase- { inputPoint = prevOutput i- , inputSequence = txInSequence i- , inputSigScript = scriptInput i- , inputWitness = w- }-toInput i (Just p) w =- StoreInput- { inputPoint = prevOutput i- , inputSequence = txInSequence i- , inputSigScript = scriptInput i- , inputPkScript = prevScript p- , inputAmount = prevAmount p- , inputWitness = w- , inputAddress = eitherToMaybe (scriptToAddressBS (prevScript p))- }--toOutput :: TxOut -> Maybe Spender -> StoreOutput-toOutput o s =- StoreOutput- { outputAmount = outValue o- , outputScript = scriptOutput o- , outputSpender = s- , outputAddress = eitherToMaybe (scriptToAddressBS (scriptOutput o))- }--data TxData =- TxData- { txDataBlock :: !BlockRef- , txData :: !Tx- , txDataPrevs :: !(IntMap Prev)- , txDataDeleted :: !Bool- , txDataRBF :: !Bool- , txDataTime :: !Word64- }- deriving (Show, Eq, Ord, Generic, Serialize, NFData)--toTransaction :: TxData -> IntMap Spender -> Transaction-toTransaction t sm =- Transaction- { transactionBlock = txDataBlock t- , transactionVersion = txVersion (txData t)- , transactionLockTime = txLockTime (txData t)- , transactionInputs = ins- , transactionOutputs = outs- , transactionDeleted = txDataDeleted t- , transactionRBF = txDataRBF t- , transactionTime = txDataTime t- , transactionId = txid- , transactionSize = txsize- , transactionWeight = txweight- , transactionFees = fees- }- where- txid = txHash (txData t)- txsize = fromIntegral $ B.length (S.encode (txData t))- txweight =- let b = B.length $ S.encode (txData t) {txWitness = []}- x = B.length $ S.encode (txData t)- in fromIntegral $ b * 3 + x- inv = sum (map inputAmount ins)- outv = sum (map outputAmount outs)- fees =- if any isCoinbase ins- then 0- else inv - outv- ws =- take (length (txIn (txData t))) $- map Just (txWitness (txData t)) <> repeat Nothing- f n i = toInput i (I.lookup n (txDataPrevs t)) (ws !! n)- ins = zipWith f [0 ..] (txIn (txData t))- g n o = toOutput o (I.lookup n sm)- outs = zipWith g [0 ..] (txOut (txData t))--fromTransaction :: Transaction -> (TxData, IntMap Spender)-fromTransaction t = (d, sm)- where- d =- TxData- { txDataBlock = transactionBlock t- , txData = transactionData t- , txDataPrevs = ps- , txDataDeleted = transactionDeleted t- , txDataRBF = transactionRBF t- , txDataTime = transactionTime t- }- f _ StoreCoinbase {} = Nothing- f n StoreInput {inputPkScript = s, inputAmount = v} =- Just (n, Prev {prevScript = s, prevAmount = v})- ps = I.fromList . catMaybes $ zipWith f [0 ..] (transactionInputs t)- g _ StoreOutput {outputSpender = Nothing} = Nothing- g n StoreOutput {outputSpender = Just s} = Just (n, s)- sm = I.fromList . catMaybes $ zipWith g [0 ..] (transactionOutputs t)---- | Detailed transaction information.-data Transaction =- Transaction- { transactionBlock :: !BlockRef- -- ^ block information for this transaction- , transactionVersion :: !Word32- -- ^ transaction version- , transactionLockTime :: !Word32- -- ^ lock time- , transactionInputs :: ![StoreInput]- -- ^ transaction inputs- , transactionOutputs :: ![StoreOutput]- -- ^ transaction outputs- , transactionDeleted :: !Bool- -- ^ this transaction has been deleted and is no longer valid- , transactionRBF :: !Bool- -- ^ this transaction can be replaced in the mempool- , transactionTime :: !Word64- -- ^ time the transaction was first seen or time of block- , transactionId :: !TxHash- -- ^ transaction id- , transactionSize :: !Word32- -- ^ serialized transaction size (includes witness data)- , transactionWeight :: !Word32- -- ^ transaction weight- , transactionFees :: !Word64- -- ^ fees that this transaction pays (0 for coinbase)- }- deriving (Show, Eq, Ord, Generic, Hashable, Serialize, NFData)--transactionData :: Transaction -> Tx-transactionData t =- Tx- { txVersion = transactionVersion t- , txIn = map i (transactionInputs t)- , txOut = map o (transactionOutputs t)- , txWitness = mapMaybe inputWitness (transactionInputs t)- , txLockTime = transactionLockTime t- }- where- i StoreCoinbase {inputPoint = p, inputSequence = q, inputSigScript = s} =- TxIn {prevOutput = p, scriptInput = s, txInSequence = q}- i StoreInput {inputPoint = p, inputSequence = q, inputSigScript = s} =- TxIn {prevOutput = p, scriptInput = s, txInSequence = q}- o StoreOutput {outputAmount = v, outputScript = s} =- TxOut {outValue = v, scriptOutput = s}--transactionToJSON :: Network -> Transaction -> Value-transactionToJSON net dtx =- object $- [ "txid" .= transactionId dtx- , "size" .= transactionSize dtx- , "version" .= transactionVersion dtx- , "locktime" .= transactionLockTime dtx- , "fee" .= transactionFees dtx- , "inputs" .= map (storeInputToJSON net) (transactionInputs dtx)- , "outputs" .= map (storeOutputToJSON net) (transactionOutputs dtx)- , "block" .= transactionBlock dtx- , "deleted" .= transactionDeleted dtx- , "time" .= transactionTime dtx- ] <>- [ "rbf" .= transactionRBF dtx | getReplaceByFee net] <>- [ "weight" .= transactionWeight dtx | getSegWit net]--transactionToEncoding :: Network -> Transaction -> Encoding-transactionToEncoding net dtx =- pairs- ( "txid" .= transactionId dtx- <> "size" .= transactionSize dtx- <> "version" .= transactionVersion dtx- <> "locktime" .= transactionLockTime dtx- <> "fee" .= transactionFees dtx- <> "inputs" `pair` list (storeInputToEncoding net) (transactionInputs dtx)- <> "outputs" `pair` list (storeOutputToEncoding net) (transactionOutputs dtx)- <> "block" .= transactionBlock dtx- <> "deleted" .= transactionDeleted dtx- <> "time" .= transactionTime dtx- <> (if getReplaceByFee net- then "rbf" .= transactionRBF dtx- else mempty)- <> (if getSegWit net- then "weight" .= transactionWeight dtx- else mempty)- )--transactionParseJSON :: Network -> Value -> Parser Transaction-transactionParseJSON net =- A.withObject "transaction" $ \o -> do- version <- o .: "version"- locktime <- o .: "locktime"- inputs <- o .: "inputs" >>= mapM (storeInputParseJSON net)- outputs <- o .: "outputs" >>= mapM (storeOutputParseJSON net)- block <- o .: "block"- deleted <- o .: "deleted"- time <- o .: "time"- rbf <- o .:? "rbf" .!= False- weight <- o .:? "weight" .!= 0- size <- o .: "size"- txid <- o .: "txid"- fees <- o .: "fee"- return- Transaction- { transactionBlock = block- , transactionVersion = version- , transactionLockTime = locktime- , transactionInputs = inputs- , transactionOutputs = outputs- , transactionDeleted = deleted- , transactionTime = time- , transactionRBF = rbf- , transactionWeight = weight- , transactionSize = size- , transactionId = txid- , transactionFees = fees- }---- | Information about a connected peer.-data PeerInformation =- PeerInformation- { peerUserAgent :: !ByteString- -- ^ user agent string- , peerAddress :: !String- -- ^ network address- , peerVersion :: !Word32- -- ^ version number- , peerServices :: !Word64- -- ^ services field- , peerRelay :: !Bool- -- ^ will relay transactions- }- deriving (Show, Eq, Ord, Generic, NFData, Serialize)--instance ToJSON PeerInformation where- toJSON p = object- [ "useragent" .= String (cs (peerUserAgent p))- , "address" .= peerAddress p- , "version" .= peerVersion p- , "services" .= String (encodeHex (S.encode (peerServices p)))- , "relay" .= peerRelay p- ]- toEncoding p = pairs- ( "useragent" `pair` text (cs (peerUserAgent p))- <> "address" .= peerAddress p- <> "version" .= peerVersion p- <> "services" `pair` text (encodeHex (S.encode (peerServices p)))- <> "relay" .= peerRelay p- )--instance FromJSON PeerInformation where- parseJSON =- A.withObject "peerinformation" $ \o -> do- String useragent <- o .: "useragent"- address <- o .: "address"- version <- o .: "version"- services <-- o .: "services" >>= jsonHex >>= \b ->- case S.decode b of- Left e -> fail $ "Could not decode services: " <> e- Right s -> return s- relay <- o .: "relay"- return- PeerInformation- { peerUserAgent = cs useragent- , peerAddress = address- , peerVersion = version- , peerServices = services- , peerRelay = relay- }---- | Address balances for an extended public key.-data XPubBal =- XPubBal- { xPubBalPath :: ![KeyIndex]- , xPubBal :: !Balance- }- deriving (Show, Ord, Eq, Generic, Serialize, NFData)--xPubBalToJSON :: Network -> XPubBal -> Value-xPubBalToJSON net XPubBal {xPubBalPath = p, xPubBal = b} =- object ["path" .= p, "balance" .= balanceToJSON net b]--xPubBalToEncoding :: Network -> XPubBal -> Encoding-xPubBalToEncoding net XPubBal {xPubBalPath = p, xPubBal = b} =- pairs ("path" .= p <> "balance" `pair` balanceToEncoding net b)--xPubBalParseJSON :: Network -> Value -> Parser XPubBal-xPubBalParseJSON net =- A.withObject "xpubbal" $ \o -> do- path <- o .: "path"- balance <- balanceParseJSON net =<< o .: "balance"- return XPubBal {xPubBalPath = path, xPubBal = balance}---- | Unspent transaction for extended public key.-data XPubUnspent =- XPubUnspent- { xPubUnspentPath :: ![KeyIndex]- , xPubUnspent :: !Unspent- }- deriving (Show, Eq, Generic, Serialize, NFData)--xPubUnspentToJSON :: Network -> XPubUnspent -> Value-xPubUnspentToJSON net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} =- object ["path" .= p, "unspent" .= unspentToJSON net u]--xPubUnspentToEncoding :: Network -> XPubUnspent -> Encoding-xPubUnspentToEncoding net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} =- pairs ("path" .= p <> "unspent" `pair` unspentToEncoding net u)--xPubUnspentParseJSON :: Network -> Value -> Parser XPubUnspent-xPubUnspentParseJSON net =- A.withObject "xpubunspent" $ \o -> do- p <- o .: "path"- u <- o .: "unspent" >>= unspentParseJSON net- return XPubUnspent {xPubUnspentPath = p, xPubUnspent = u}--data XPubSummary =- XPubSummary- { xPubSummaryConfirmed :: !Word64- , xPubSummaryZero :: !Word64- , xPubSummaryReceived :: !Word64- , xPubUnspentCount :: !Word64- , xPubExternalIndex :: !Word32- , xPubChangeIndex :: !Word32- }- deriving (Eq, Show, Generic, Serialize, NFData)--instance ToJSON XPubSummary where- toJSON XPubSummary { xPubSummaryConfirmed = c- , xPubSummaryZero = z- , xPubSummaryReceived = r- , xPubUnspentCount = u- , xPubExternalIndex = ext- , xPubChangeIndex = ch- } =- object- [ "balance" .=- object- [ "confirmed" .= c- , "unconfirmed" .= z- , "received" .= r- , "utxo" .= u- ]- , "indices" .= object ["change" .= ch, "external" .= ext]- ]- toEncoding XPubSummary { xPubSummaryConfirmed = c- , xPubSummaryZero = z- , xPubSummaryReceived = r- , xPubUnspentCount = u- , xPubExternalIndex = ext- , xPubChangeIndex = ch- } =- pairs- ( "balance" `pair` pairs- ( "confirmed" .= c- <> "unconfirmed" .= z- <> "received" .= r- <> "utxo" .= u- )- <> "indices" `pair` pairs- ( "change" .= ch- <> "external" .= ext- )- )--instance FromJSON XPubSummary where- parseJSON =- A.withObject "xpubsummary" $ \o -> do- b <- o .: "balance"- i <- o .: "indices"- conf <- b .: "confirmed"- unconfirmed <- b .: "unconfirmed"- received <- b .: "received"- utxo <- b .: "utxo"- change <- i .: "change"- external <- i .: "external"- return- XPubSummary- { xPubSummaryConfirmed = conf- , xPubSummaryZero = unconfirmed- , xPubSummaryReceived = received- , xPubUnspentCount = utxo- , xPubExternalIndex = external- , xPubChangeIndex = change- }--class Healthy a where- isOK :: a -> Bool--data BlockHealth =- BlockHealth- { blockHealthHeaders :: !BlockHeight- , blockHealthBlocks :: !BlockHeight- , blockHealthMaxDiff :: !Int- }- deriving (Show, Eq, Generic, NFData)--instance Serialize BlockHealth where- put h@BlockHealth {..} = do- put (isOK h)- put blockHealthHeaders- put blockHealthBlocks- put blockHealthMaxDiff- get = do- k <- get- blockHealthHeaders <- get- blockHealthBlocks <- get- blockHealthMaxDiff <- get- let h = BlockHealth {..}- guard (k == isOK h)- return h--instance Healthy BlockHealth where- isOK BlockHealth {..} =- h - b <= blockHealthMaxDiff- where- h = fromIntegral blockHealthHeaders- b = fromIntegral blockHealthBlocks--instance ToJSON BlockHealth where- toJSON h@BlockHealth {..} =- object- [ "headers" .= blockHealthHeaders- , "blocks" .= blockHealthBlocks- , "diff" .= diff- , "max" .= blockHealthMaxDiff- , "ok" .= isOK h- ]- where- diff = blockHealthHeaders - blockHealthBlocks--instance FromJSON BlockHealth where- parseJSON =- A.withObject "BlockHealth" $ \o -> do- blockHealthHeaders <- o .: "headers"- blockHealthBlocks <- o .: "blocks"- blockHealthMaxDiff <- o .: "max"- return BlockHealth {..}--data TimeHealth =- TimeHealth- { timeHealthAge :: !Int- , timeHealthMax :: !Int- }- deriving (Show, Eq, Generic, NFData)--instance Serialize TimeHealth where- put h@TimeHealth {..} = do- put (isOK h)- put timeHealthAge- put timeHealthMax- get = do- k <- get- timeHealthAge <- get- timeHealthMax <- get- let t = TimeHealth {..}- guard (k == isOK t)- return t--instance Healthy TimeHealth where- isOK TimeHealth {..} =- timeHealthAge <= timeHealthMax--instance ToJSON TimeHealth where- toJSON h@TimeHealth {..} =- object- [ "age" .= timeHealthAge- , "max" .= timeHealthMax- , "ok" .= isOK h- ]--instance FromJSON TimeHealth where- parseJSON =- A.withObject "TimeHealth" $ \o -> do- timeHealthAge <- o .: "age"- timeHealthMax <- o .: "max"- return TimeHealth {..}--data CountHealth =- CountHealth- { countHealthNum :: !Int- , countHealthMin :: !Int- }- deriving (Show, Eq, Generic, NFData)--instance Serialize CountHealth where- put h@CountHealth {..} = do- put (isOK h)- put countHealthNum- put countHealthMin- get = do- k <- get- countHealthNum <- get- countHealthMin <- get- let c = CountHealth {..}- guard (k == isOK c)- return c--instance Healthy CountHealth where- isOK CountHealth {..} =- countHealthMin <= countHealthNum--instance ToJSON CountHealth where- toJSON h@CountHealth {..} =- object- [ "count" .= countHealthNum- , "min" .= countHealthMin- , "ok" .= isOK h- ]--instance FromJSON CountHealth where- parseJSON =- A.withObject "CountHealth" $ \o -> do- countHealthNum <- o .: "count"- countHealthMin <- o .: "min"- return CountHealth {..}--data MaxHealth =- MaxHealth- { maxHealthNum :: !Int- , maxHealthMax :: !Int- }- deriving (Show, Eq, Generic, NFData)--instance Serialize MaxHealth where- put h@MaxHealth {..} = do- put $ isOK h- put maxHealthNum- put maxHealthMax- get = do- k <- get- maxHealthNum <- get- maxHealthMax <- get- let h = MaxHealth {..}- guard (k == isOK h)- return h--instance Healthy MaxHealth where- isOK MaxHealth {..} = maxHealthNum <= maxHealthMax--instance ToJSON MaxHealth where- toJSON h@MaxHealth {..} =- object- [ "count" .= maxHealthNum- , "max" .= maxHealthMax- , "ok" .= isOK h- ]--instance FromJSON MaxHealth where- parseJSON =- A.withObject "MaxHealth" $ \o -> do- maxHealthNum <- o .: "count"- maxHealthMax <- o .: "max"- return MaxHealth {..}--data HealthCheck =- HealthCheck- { healthBlocks :: !BlockHealth- , healthLastBlock :: !TimeHealth- , healthLastTx :: !TimeHealth- , healthPendingTxs :: !MaxHealth- , healthPeers :: !CountHealth- , healthNetwork :: !String- , healthVersion :: !String- }- deriving (Show, Eq, Generic, NFData)--instance Serialize HealthCheck where- put h@HealthCheck {..} = do- put (isOK h)- put healthBlocks- put healthLastBlock- put healthLastTx- put healthPendingTxs- put healthPeers- put healthNetwork- put healthVersion- get = do- k <- get- healthBlocks <- get- healthLastBlock <- get- healthLastTx <- get- healthPendingTxs <- get- healthPeers <- get- healthNetwork <- get- healthVersion <- get- let h = HealthCheck {..}- guard (k == isOK h)- return h--instance Healthy HealthCheck where- isOK HealthCheck {..} =- isOK healthBlocks &&- isOK healthLastBlock &&- isOK healthLastTx &&- isOK healthPendingTxs &&- isOK healthPeers--instance ToJSON HealthCheck where- toJSON h@HealthCheck {..} =- object- [ "blocks" .= healthBlocks- , "last-block" .= healthLastBlock- , "last-tx" .= healthLastTx- , "pending-txs" .= healthPendingTxs- , "peers" .= healthPeers- , "net" .= healthNetwork- , "version" .= healthVersion- , "ok" .= isOK h- ]--instance FromJSON HealthCheck where- parseJSON =- A.withObject "HealthCheck" $ \o -> do- healthBlocks <- o .: "blocks"- healthLastBlock <- o .: "last-block"- healthLastTx <- o .: "last-tx"- healthPendingTxs <- o .: "pending-txs"- healthPeers <- o .: "peers"- healthNetwork <- o .: "net"- healthVersion <- o .: "version"- return HealthCheck {..}--data Event- = EventBlock !BlockHash- | EventTx !TxHash- deriving (Show, Eq, Generic, Serialize, NFData)--instance ToJSON Event where- toJSON (EventTx h) = object ["type" .= String "tx", "id" .= h]- toJSON (EventBlock h) = object ["type" .= String "block", "id" .= h]- toEncoding (EventTx h) =- pairs ("type" `pair` text "tx" <> "id" `pair` text (txHashToHex h))- toEncoding (EventBlock h) =- pairs- ("type" `pair` text "block" <> "id" `pair` text (blockHashToHex h))--instance FromJSON Event where- parseJSON =- A.withObject "event" $ \o -> do- t <- o .: "type"- case t of- "tx" -> do- i <- o .: "id"- return $ EventTx i- "block" -> do- i <- o .: "id"- return $ EventBlock i- _ -> fail $ "Could not recognize event type: " <> t--newtype GenericResult a =- GenericResult- { getResult :: a- }- deriving (Show, Eq, Generic, Serialize, NFData)--instance ToJSON a => ToJSON (GenericResult a) where- toJSON (GenericResult b) = object ["result" .= b]- toEncoding (GenericResult b) = pairs ("result" .= b)--instance FromJSON a => FromJSON (GenericResult a) where- parseJSON =- A.withObject "GenericResult" $ \o -> GenericResult <$> o .: "result"--newtype RawResult a =- RawResult- { getRawResult :: a- }- deriving (Show, Eq, Generic, Serialize, NFData)--instance S.Serialize a => ToJSON (RawResult a) where- toJSON (RawResult b) =- object [ "result" .= A.String (encodeHex $ S.encode b)]- toEncoding (RawResult b) =- pairs $ "result" `pair` unsafeToEncoding str- where- str = char7 '"' <> lazyByteStringHex (S.runPutLazy $ put b) <> char7 '"'--instance S.Serialize a => FromJSON (RawResult a) where- parseJSON =- A.withObject "RawResult" $ \o -> do- res <- o .: "result"- let valM = eitherToMaybe . S.decode =<< decodeHex res- maybe mzero (return . RawResult) valM--newtype RawResultList a =- RawResultList- { getRawResultList :: [a]- }- deriving (Show, Eq, Generic, Serialize, NFData)--instance Semigroup (RawResultList a) where- (RawResultList a) <> (RawResultList b) = RawResultList $ a <> b--instance Monoid (RawResultList a) where- mempty = RawResultList mempty--instance S.Serialize a => ToJSON (RawResultList a) where- toJSON (RawResultList xs) =- toJSON $ encodeHex . S.encode <$> xs- toEncoding (RawResultList xs) =- list (unsafeToEncoding . str) xs- where- str x =- char7 '"' <> lazyByteStringHex (S.runPutLazy (put x)) <> char7 '"'--instance S.Serialize a => FromJSON (RawResultList a) where- parseJSON =- A.withArray "RawResultList" $ \vec ->- RawResultList <$> mapM parseElem (toList vec)- where- parseElem = A.withText "RawResultListElem" $ maybe mzero return . f- f = eitherToMaybe . S.decode <=< decodeHex--newtype TxId =- TxId TxHash- deriving (Show, Eq, Generic, Serialize, NFData)--instance ToJSON TxId where- toJSON (TxId h) = object ["txid" .= h]- toEncoding (TxId h) = pairs ("txid" `pair` text (txHashToHex h))--instance FromJSON TxId where- parseJSON = A.withObject "txid" $ \o -> TxId <$> o .: "txid"--data Except- = ThingNotFound- | ServerError- | BadRequest- | UserError !String- | StringError !String- | BlockTooLarge- deriving (Show, Eq, Ord, Serialize, Generic, NFData)--instance Exception Except--instance ScottyError Except where- stringError = StringError- showError = TL.pack . show--instance ToJSON Except where- toJSON e =- object $- case e of- ThingNotFound ->- ["error" .= String "not-found"]- ServerError ->- ["error" .= String "server-error"]- BadRequest ->- ["error" .= String "bad-request"]- UserError msg ->- [ "error" .= String "user-error"- , "message" .= String (cs msg)- ]- StringError msg ->- [ "error" .= String "string-error"- , "message" .= String (cs msg)- ]- BlockTooLarge ->- ["error" .= String "block-too-large"]--instance FromJSON Except where- parseJSON =- A.withObject "Except" $ \o -> do- ctr <- o .: "error"- msg <- fromMaybe "" <$> o .:? "message"- case ctr of- String "not-found" -> return ThingNotFound- String "server-error" -> return ServerError- String "bad-request" -> return BadRequest- String "user-error" -> return $ UserError msg- String "string-error" -> return $ StringError msg- String "block-too-large" -> return BlockTooLarge- _ -> mzero++ -- * Blockchain.info API+ , BinfoTxIndex+ , isBinfoTxIndexNull+ , isBinfoTxIndexBlock+ , isBinfoTxIndexHash+ , encodeBinfoTxIndexHash+ , hashToBinfoTxIndex+ , blockToBinfoTxIndex+ , matchBinfoTxHash+ , binfoTxIndexBlock+ , binfoTransactionIndex+ , BinfoTxId(..)+ , BinfoMultiAddr(..)+ , binfoMultiAddrToJSON+ , binfoMultiAddrToEncoding+ , binfoMultiAddrParseJSON+ , BinfoAddress(..)+ , toBinfoAddrs+ , binfoAddressToJSON+ , binfoAddressToEncoding+ , binfoAddressParseJSON+ , BinfoAddr(..)+ , parseBinfoAddr+ , BinfoWallet(..)+ , BinfoTx(..)+ , relevantTxs+ , toBinfoTx+ , toBinfoTxSimple+ , binfoTxToJSON+ , binfoTxToEncoding+ , binfoTxParseJSON+ , BinfoTxInput(..)+ , binfoTxInputToJSON+ , binfoTxInputToEncoding+ , binfoTxInputParseJSON+ , BinfoTxOutput(..)+ , binfoTxOutputToJSON+ , binfoTxOutputToEncoding+ , binfoTxOutputParseJSON+ , BinfoSpender(..)+ , BinfoXPubPath(..)+ , binfoXPubPathToJSON+ , binfoXPubPathToEncoding+ , binfoXPubPathParseJSON+ , BinfoInfo(..)+ , BinfoBlockInfo(..)+ , BinfoSymbol(..)+ , BinfoTicker(..)+ )++where++import Control.Applicative ((<|>))+import Control.DeepSeq (NFData)+import Control.Exception (Exception)+import Control.Monad (guard, join, mzero, (<=<))+import Data.Aeson (Encoding, FromJSON (..),+ FromJSONKey (..), ToJSON (..),+ ToJSONKey (..), Value (..), object,+ pairs, withObject, (.!=), (.:), (.:?),+ (.=))+import qualified Data.Aeson as A+import Data.Aeson.Encoding (list, null_, pair, text,+ unsafeToEncoding)+import Data.Aeson.Types (Parser)+import Data.Bits (setBit, shift, testBit, (.&.), (.|.))+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.ByteString.Builder (char7, lazyByteStringHex)+import Data.ByteString.Short (ShortByteString)+import qualified Data.ByteString.Short as BSS+import Data.Default (Default (..))+import Data.Either (fromRight, lefts, rights)+import Data.Foldable (toList)+import Data.Function (on)+import Data.Hashable (Hashable (..))+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap+import Data.HashSet (HashSet)+import qualified Data.HashSet as HashSet+import Data.Int (Int64)+import qualified Data.IntMap as IntMap+import Data.IntMap.Strict (IntMap)+import Data.Map.Strict (Map)+import Data.Maybe (catMaybes, fromMaybe, isJust,+ isNothing, mapMaybe, maybeToList)+import Data.Serialize (Get, Put, Serialize (..), getWord32be,+ getWord64be, getWord8, putWord32be,+ putWord64be, putWord8)+import qualified Data.Serialize as S+import Data.String.Conversions (cs)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import qualified Data.Text.Lazy as TL+import Data.Word (Word32, Word64)+import GHC.Generics (Generic)+import Haskoin (Address, BlockHash, BlockHeader (..),+ BlockHeight, BlockWork, Coin (..),+ KeyIndex, Network (..), OutPoint (..),+ PubKeyI (..), SoftPath, Tx (..),+ TxHash (..), TxIn (..), TxOut (..),+ WitnessStack, XPubKey (..),+ addrFromJSON, addrToEncoding,+ addrToJSON, bch, blockHashToHex, btc,+ decodeHex, eitherToMaybe, encodeHex,+ headerHash, hexToTxHash,+ maybeToEither, parseSoft, pathToList,+ pathToStr, putVarInt,+ scriptToAddressBS, textToAddr, txHash,+ txHashToHex, wrapPubKey, xPubFromJSON,+ xPubImport, xPubToEncoding,+ xPubToJSON)+import Text.Read (readMaybe)+import Web.Scotty.Trans (Parsable (..), ScottyError (..))++data DeriveType = DeriveNormal+ | DeriveP2SH+ | DeriveP2WPKH+ deriving (Show, Eq, Generic, NFData, Serialize)++instance Default DeriveType where+ def = DeriveNormal++data XPubSpec =+ XPubSpec+ { xPubSpecKey :: !XPubKey+ , xPubDeriveType :: !DeriveType+ }+ deriving (Show, Eq, Generic, NFData)++instance Hashable XPubSpec where+ hashWithSalt i XPubSpec {xPubSpecKey = XPubKey {xPubKey = pubkey}} =+ hashWithSalt i pubkey++instance Serialize XPubSpec where+ put XPubSpec {xPubSpecKey = k, xPubDeriveType = t} = do+ put (xPubDepth k)+ put (xPubParent k)+ put (xPubIndex k)+ put (xPubChain k)+ put (wrapPubKey True (xPubKey k))+ put t+ get = do+ d <- get+ p <- get+ i <- get+ c <- get+ k <- get+ t <- get+ let x =+ XPubKey+ { xPubDepth = d+ , xPubParent = p+ , xPubIndex = i+ , xPubChain = c+ , xPubKey = pubKeyPoint k+ }+ return XPubSpec {xPubSpecKey = x, xPubDeriveType = t}++type UnixTime = Word64+type BlockPos = Word32++-- | Serialize such that ordering is inverted.+putUnixTime :: Word64 -> Put+putUnixTime w = putWord64be $ maxBound - w++getUnixTime :: Get Word64+getUnixTime = (maxBound -) <$> getWord64be++-- | Reference to a block where a transaction is stored.+data BlockRef+ = BlockRef+ { blockRefHeight :: !BlockHeight+ -- ^ block height in the chain+ , blockRefPos :: !Word32+ -- ^ position of transaction within the block+ }+ | MemRef+ { memRefTime :: !UnixTime+ }+ deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++-- | Serialized entities will sort in reverse order.+instance Serialize BlockRef where+ put MemRef {memRefTime = t} = do+ putWord8 0x00+ putUnixTime t+ put BlockRef {blockRefHeight = h, blockRefPos = p} = do+ putWord8 0x01+ putWord32be (maxBound - h)+ putWord32be (maxBound - p)+ get = getmemref <|> getblockref+ where+ getmemref = do+ guard . (== 0x00) =<< getWord8+ MemRef <$> getUnixTime+ getblockref = do+ guard . (== 0x01) =<< getWord8+ h <- (maxBound -) <$> getWord32be+ p <- (maxBound -) <$> getWord32be+ return BlockRef {blockRefHeight = h, blockRefPos = p}++confirmed :: BlockRef -> Bool+confirmed BlockRef {} = True+confirmed MemRef {} = False++instance ToJSON BlockRef where+ toJSON BlockRef {blockRefHeight = h, blockRefPos = p} =+ object ["height" .= h, "position" .= p]+ toJSON MemRef {memRefTime = t} = object ["mempool" .= t]+ toEncoding BlockRef {blockRefHeight = h, blockRefPos = p} =+ pairs ("height" .= h <> "position" .= p)+ toEncoding MemRef {memRefTime = t} = pairs ("mempool" .= t)++instance FromJSON BlockRef where+ parseJSON = A.withObject "blockref" $ \o -> b o <|> m o+ where+ b o = do+ height <- o .: "height"+ position <- o .: "position"+ return BlockRef{blockRefHeight = height, blockRefPos = position}+ m o = do+ mempool <- o .: "mempool"+ return MemRef{memRefTime = mempool}++-- | Transaction in relation to an address.+data TxRef =+ TxRef+ { txRefBlock :: !BlockRef+ -- ^ block information+ , txRefHash :: !TxHash+ -- ^ transaction hash+ }+ deriving (Show, Eq, Ord, Generic, Serialize, Hashable, NFData)++instance ToJSON TxRef where+ toJSON btx = object ["txid" .= txRefHash btx, "block" .= txRefBlock btx]+ toEncoding btx =+ pairs+ ( "txid" .= txRefHash btx+ <> "block" .= txRefBlock btx+ )++instance FromJSON TxRef where+ parseJSON =+ A.withObject "blocktx" $ \o -> do+ txid <- o .: "txid"+ block <- o .: "block"+ return TxRef {txRefBlock = block, txRefHash = txid}++-- | Address balance information.+data Balance =+ Balance+ { balanceAddress :: !Address+ -- ^ address balance+ , balanceAmount :: !Word64+ -- ^ confirmed balance+ , balanceZero :: !Word64+ -- ^ unconfirmed balance+ , balanceUnspentCount :: !Word64+ -- ^ number of unspent outputs+ , balanceTxCount :: !Word64+ -- ^ number of transactions+ , balanceTotalReceived :: !Word64+ -- ^ total amount from all outputs in this address+ }+ deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)++zeroBalance :: Address -> Balance+zeroBalance a =+ Balance+ { balanceAddress = a+ , balanceAmount = 0+ , balanceUnspentCount = 0+ , balanceZero = 0+ , balanceTxCount = 0+ , balanceTotalReceived = 0+ }++nullBalance :: Balance -> Bool+nullBalance Balance { balanceAmount = 0+ , balanceUnspentCount = 0+ , balanceZero = 0+ , balanceTxCount = 0+ , balanceTotalReceived = 0+ } = True+nullBalance _ = False++balanceToJSON :: Network -> Balance -> Value+balanceToJSON net b =+ object+ [ "address" .= addrToJSON net (balanceAddress b)+ , "confirmed" .= balanceAmount b+ , "unconfirmed" .= balanceZero b+ , "utxo" .= balanceUnspentCount b+ , "txs" .= balanceTxCount b+ , "received" .= balanceTotalReceived b+ ]++balanceToEncoding :: Network -> Balance -> Encoding+balanceToEncoding net b =+ pairs+ ( "address" `pair` addrToEncoding net (balanceAddress b)+ <> "confirmed" .= balanceAmount b+ <> "unconfirmed" .= balanceZero b+ <> "utxo" .= balanceUnspentCount b+ <> "txs" .= balanceTxCount b+ <> "received" .= balanceTotalReceived b+ )++balanceParseJSON :: Network -> Value -> Parser Balance+balanceParseJSON net =+ A.withObject "balance" $ \o -> do+ amount <- o .: "confirmed"+ unconfirmed <- o .: "unconfirmed"+ utxo <- o .: "utxo"+ txs <- o .: "txs"+ received <- o .: "received"+ address <- addrFromJSON net =<< o .: "address"+ return+ Balance+ { balanceAddress = address+ , balanceAmount = amount+ , balanceUnspentCount = utxo+ , balanceZero = unconfirmed+ , balanceTxCount = txs+ , balanceTotalReceived = received+ }++-- | Unspent output.+data Unspent =+ Unspent+ { unspentBlock :: !BlockRef+ , unspentPoint :: !OutPoint+ , unspentAmount :: !Word64+ , unspentScript :: !ShortByteString+ , unspentAddress :: !(Maybe Address)+ }+ deriving (Show, Eq, Ord, Generic, Hashable, Serialize, NFData)++instance Coin Unspent where+ coinValue = unspentAmount++unspentToJSON :: Network -> Unspent -> Value+unspentToJSON net u =+ object+ [ "address" .= (addrToJSON net <$> unspentAddress u)+ , "block" .= unspentBlock u+ , "txid" .= outPointHash (unspentPoint u)+ , "index" .= outPointIndex (unspentPoint u)+ , "pkscript" .= script+ , "value" .= unspentAmount u+ ]+ where+ bsscript = BSS.fromShort (unspentScript u)+ script = encodeHex bsscript++unspentToEncoding :: Network -> Unspent -> Encoding+unspentToEncoding net u =+ pairs+ ( "address" `pair` maybe null_ (addrToEncoding net) (unspentAddress u)+ <> "block" .= unspentBlock u+ <> "txid" .= outPointHash (unspentPoint u)+ <> "index" .= outPointIndex (unspentPoint u)+ <> "pkscript" `pair` text script+ <> "value" .= unspentAmount u+ )+ where+ bsscript = BSS.fromShort (unspentScript u)+ script = encodeHex bsscript++unspentParseJSON :: Network -> Value -> Parser Unspent+unspentParseJSON net =+ A.withObject "unspent" $ \o -> do+ block <- o .: "block"+ txid <- o .: "txid"+ index <- o .: "index"+ value <- o .: "value"+ script <- BSS.toShort <$> (o .: "pkscript" >>= jsonHex)+ addr <- o .: "address" >>= \case+ Nothing -> return Nothing+ Just a -> Just <$> addrFromJSON net a <|> return Nothing+ return+ Unspent+ { unspentBlock = block+ , unspentPoint = OutPoint txid index+ , unspentAmount = value+ , unspentScript = script+ , unspentAddress = addr+ }++-- | Database value for a block entry.+data BlockData =+ BlockData+ { blockDataHeight :: !BlockHeight+ -- ^ height of the block in the chain+ , blockDataMainChain :: !Bool+ -- ^ is this block in the main chain?+ , blockDataWork :: !BlockWork+ -- ^ accumulated work in that block+ , blockDataHeader :: !BlockHeader+ -- ^ block header+ , blockDataSize :: !Word32+ -- ^ size of the block including witnesses+ , blockDataWeight :: !Word32+ -- ^ weight of this block (for segwit networks)+ , blockDataTxs :: ![TxHash]+ -- ^ block transactions+ , blockDataOutputs :: !Word64+ -- ^ sum of all transaction outputs+ , blockDataFees :: !Word64+ -- ^ sum of all transaction fees+ , blockDataSubsidy :: !Word64+ -- ^ block subsidy+ }+ deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)++blockDataToJSON :: Network -> BlockData -> Value+blockDataToJSON net bv =+ object $+ [ "hash" .= headerHash (blockDataHeader bv)+ , "height" .= blockDataHeight bv+ , "mainchain" .= blockDataMainChain bv+ , "previous" .= prevBlock (blockDataHeader bv)+ , "time" .= blockTimestamp (blockDataHeader bv)+ , "version" .= blockVersion (blockDataHeader bv)+ , "bits" .= blockBits (blockDataHeader bv)+ , "nonce" .= bhNonce (blockDataHeader bv)+ , "size" .= blockDataSize bv+ , "tx" .= blockDataTxs bv+ , "merkle" .= TxHash (merkleRoot (blockDataHeader bv))+ , "subsidy" .= blockDataSubsidy bv+ , "fees" .= blockDataFees bv+ , "outputs" .= blockDataOutputs bv+ , "work" .= blockDataWork bv+ ] <>+ ["weight" .= blockDataWeight bv | getSegWit net]++blockDataToEncoding :: Network -> BlockData -> Encoding+blockDataToEncoding net bv =+ pairs+ ( "hash" `pair` text (blockHashToHex (headerHash (blockDataHeader bv)))+ <> "height" .= blockDataHeight bv+ <> "mainchain" .= blockDataMainChain bv+ <> "previous" .= prevBlock (blockDataHeader bv)+ <> "time" .= blockTimestamp (blockDataHeader bv)+ <> "version" .= blockVersion (blockDataHeader bv)+ <> "bits" .= blockBits (blockDataHeader bv)+ <> "nonce" .= bhNonce (blockDataHeader bv)+ <> "size" .= blockDataSize bv+ <> "tx" .= blockDataTxs bv+ <> "merkle" `pair` text (txHashToHex (TxHash (merkleRoot (blockDataHeader bv))))+ <> "subsidy" .= blockDataSubsidy bv+ <> "fees" .= blockDataFees bv+ <> "outputs" .= blockDataOutputs bv+ <> "work" .= blockDataWork bv+ <> (if getSegWit net then "weight" .= blockDataWeight bv else mempty)+ )++instance FromJSON BlockData where+ parseJSON =+ A.withObject "blockdata" $ \o -> do+ height <- o .: "height"+ mainchain <- o .: "mainchain"+ previous <- o .: "previous"+ time <- o .: "time"+ version <- o .: "version"+ bits <- o .: "bits"+ nonce <- o .: "nonce"+ size <- o .: "size"+ tx <- o .: "tx"+ TxHash merkle <- o .: "merkle"+ subsidy <- o .: "subsidy"+ fees <- o .: "fees"+ outputs <- o .: "outputs"+ work <- o .: "work"+ weight <- o .:? "weight" .!= 0+ return+ BlockData+ { blockDataHeader =+ BlockHeader+ { prevBlock = previous+ , blockTimestamp = time+ , blockVersion = version+ , blockBits = bits+ , bhNonce = nonce+ , merkleRoot = merkle+ }+ , blockDataMainChain = mainchain+ , blockDataWork = work+ , blockDataSize = size+ , blockDataWeight = weight+ , blockDataTxs = tx+ , blockDataOutputs = outputs+ , blockDataFees = fees+ , blockDataHeight = height+ , blockDataSubsidy = subsidy+ }++data StoreInput+ = StoreCoinbase+ { inputPoint :: !OutPoint+ , inputSequence :: !Word32+ , inputSigScript :: !ByteString+ , inputWitness :: !WitnessStack+ }+ | StoreInput+ { inputPoint :: !OutPoint+ , inputSequence :: !Word32+ , inputSigScript :: !ByteString+ , inputPkScript :: !ByteString+ , inputAmount :: !Word64+ , inputWitness :: !WitnessStack+ , inputAddress :: !(Maybe Address)+ }+ deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)++isCoinbase :: StoreInput -> Bool+isCoinbase StoreCoinbase {} = True+isCoinbase StoreInput {} = False++storeInputToJSON :: Network -> StoreInput -> Value+storeInputToJSON net StoreInput { inputPoint = OutPoint oph opi+ , inputSequence = sq+ , inputSigScript = ss+ , inputPkScript = ps+ , inputAmount = val+ , inputWitness = wit+ , inputAddress = a+ } =+ object $+ [ "coinbase" .= False+ , "txid" .= oph+ , "output" .= opi+ , "sigscript" .= String (encodeHex ss)+ , "sequence" .= sq+ , "pkscript" .= String (encodeHex ps)+ , "value" .= val+ , "address" .= (addrToJSON net <$> a)+ , "witness" .= map encodeHex wit+ ]+storeInputToJSON net StoreCoinbase { inputPoint = OutPoint oph opi+ , inputSequence = sq+ , inputSigScript = ss+ , inputWitness = wit+ } =+ object $+ [ "coinbase" .= True+ , "txid" .= oph+ , "output" .= opi+ , "sigscript" .= String (encodeHex ss)+ , "sequence" .= sq+ , "pkscript" .= Null+ , "value" .= Null+ , "address" .= Null+ , "witness" .= map encodeHex wit+ ]++storeInputToEncoding :: Network -> StoreInput -> Encoding+storeInputToEncoding net StoreInput { inputPoint = OutPoint oph opi+ , inputSequence = sq+ , inputSigScript = ss+ , inputPkScript = ps+ , inputAmount = val+ , inputWitness = wit+ , inputAddress = a+ } =+ pairs+ ( "coinbase" .= False+ <> "txid" .= oph+ <> "output" .= opi+ <> "sigscript" `pair` text (encodeHex ss)+ <> "sequence" .= sq+ <> "pkscript" `pair` text (encodeHex ps)+ <> "value" .= val+ <> "address" `pair` maybe null_ (addrToEncoding net) a+ <> "witness" .= map encodeHex wit+ )+storeInputToEncoding net StoreCoinbase { inputPoint = OutPoint oph opi+ , inputSequence = sq+ , inputSigScript = ss+ , inputWitness = wit+ } =+ pairs+ ( "coinbase" .= True+ <> "txid" `pair` text (txHashToHex oph)+ <> "output" .= opi+ <> "sigscript" `pair` text (encodeHex ss)+ <> "sequence" .= sq+ <> "pkscript" `pair` null_+ <> "value" `pair` null_+ <> "address" `pair` null_+ <> "witness" .= map encodeHex wit+ )++storeInputParseJSON :: Network -> Value -> Parser StoreInput+storeInputParseJSON net =+ A.withObject "storeinput" $ \o -> do+ coinbase <- o .: "coinbase"+ outpoint <- OutPoint <$> o .: "txid" <*> o .: "output"+ sequ <- o .: "sequence"+ witness <- mapM jsonHex =<< o .:? "witness" .!= []+ sigscript <- o .: "sigscript" >>= jsonHex+ if coinbase+ then return+ StoreCoinbase+ { inputPoint = outpoint+ , inputSequence = sequ+ , inputSigScript = sigscript+ , inputWitness = witness+ }+ else do+ pkscript <- o .: "pkscript" >>= jsonHex+ value <- o .: "value"+ addr <- o .: "address" >>= \case+ Nothing -> return Nothing+ Just a -> Just <$> addrFromJSON net a <|> return Nothing+ return+ StoreInput+ { inputPoint = outpoint+ , inputSequence = sequ+ , inputSigScript = sigscript+ , inputPkScript = pkscript+ , inputAmount = value+ , inputWitness = witness+ , inputAddress = addr+ }++jsonHex :: Text -> Parser ByteString+jsonHex s =+ case decodeHex s of+ Nothing -> fail "Could not decode hex"+ Just b -> return b++-- | Information about input spending output.+data Spender =+ Spender+ { spenderHash :: !TxHash+ -- ^ input transaction hash+ , spenderIndex :: !Word32+ -- ^ input position in transaction+ }+ deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)++instance ToJSON Spender where+ toJSON n =+ object+ [ "txid" .= txHashToHex (spenderHash n)+ , "input" .= spenderIndex n+ ]+ toEncoding n =+ pairs $+ "txid" .= txHashToHex (spenderHash n) <>+ "input" .= spenderIndex n++instance FromJSON Spender where+ parseJSON =+ A.withObject "spender" $ \o -> Spender <$> o .: "txid" <*> o .: "input"++-- | Output information.+data StoreOutput =+ StoreOutput+ { outputAmount :: !Word64+ , outputScript :: !ByteString+ , outputSpender :: !(Maybe Spender)+ , outputAddress :: !(Maybe Address)+ }+ deriving (Show, Read, Eq, Ord, Generic, Serialize, Hashable, NFData)++storeOutputToJSON :: Network -> StoreOutput -> Value+storeOutputToJSON net d =+ object+ [ "address" .= (addrToJSON net <$> outputAddress d)+ , "pkscript" .= encodeHex (outputScript d)+ , "value" .= outputAmount d+ , "spent" .= isJust (outputSpender d)+ , "spender" .= outputSpender d+ ]++storeOutputToEncoding :: Network -> StoreOutput -> Encoding+storeOutputToEncoding net d =+ pairs+ ( "address" `pair` maybe null_ (addrToEncoding net) (outputAddress d)+ <> "pkscript" `pair` text (encodeHex (outputScript d))+ <> "value" .= outputAmount d+ <> "spent" .= isJust (outputSpender d)+ <> "spender" .= outputSpender d+ )++storeOutputParseJSON :: Network -> Value -> Parser StoreOutput+storeOutputParseJSON net =+ A.withObject "storeoutput" $ \o -> do+ value <- o .: "value"+ pkscript <- o .: "pkscript" >>= jsonHex+ spender <- o .: "spender"+ addr <- o .: "address" >>= \case+ Nothing -> return Nothing+ Just a -> Just <$> addrFromJSON net a <|> return Nothing+ return+ StoreOutput+ { outputAmount = value+ , outputScript = pkscript+ , outputSpender = spender+ , outputAddress = addr+ }++data Prev =+ Prev+ { prevScript :: !ByteString+ , prevAmount :: !Word64+ }+ deriving (Show, Eq, Ord, Generic, Hashable, Serialize, NFData)++toInput :: TxIn -> Maybe Prev -> WitnessStack -> StoreInput+toInput i Nothing w =+ StoreCoinbase+ { inputPoint = prevOutput i+ , inputSequence = txInSequence i+ , inputSigScript = scriptInput i+ , inputWitness = w+ }+toInput i (Just p) w =+ StoreInput+ { inputPoint = prevOutput i+ , inputSequence = txInSequence i+ , inputSigScript = scriptInput i+ , inputPkScript = prevScript p+ , inputAmount = prevAmount p+ , inputWitness = w+ , inputAddress = eitherToMaybe (scriptToAddressBS (prevScript p))+ }++toOutput :: TxOut -> Maybe Spender -> StoreOutput+toOutput o s =+ StoreOutput+ { outputAmount = outValue o+ , outputScript = scriptOutput o+ , outputSpender = s+ , outputAddress = eitherToMaybe (scriptToAddressBS (scriptOutput o))+ }++data TxData =+ TxData+ { txDataBlock :: !BlockRef+ , txData :: !Tx+ , txDataPrevs :: !(IntMap Prev)+ , txDataDeleted :: !Bool+ , txDataRBF :: !Bool+ , txDataTime :: !Word64+ }+ deriving (Show, Eq, Ord, Generic, Serialize, NFData)++toTransaction :: TxData -> IntMap Spender -> Transaction+toTransaction t sm =+ Transaction+ { transactionBlock = txDataBlock t+ , transactionVersion = txVersion (txData t)+ , transactionLockTime = txLockTime (txData t)+ , transactionInputs = ins+ , transactionOutputs = outs+ , transactionDeleted = txDataDeleted t+ , transactionRBF = txDataRBF t+ , transactionTime = txDataTime t+ , transactionId = txid+ , transactionSize = txsize+ , transactionWeight = txweight+ , transactionFees = fees+ }+ where+ txid = txHash (txData t)+ txsize = fromIntegral $ B.length (S.encode (txData t))+ txweight =+ let b = B.length $ S.encode (txData t) {txWitness = []}+ x = B.length $ S.encode (txData t)+ in fromIntegral $ b * 3 + x+ inv = sum (map inputAmount ins)+ outv = sum (map outputAmount outs)+ fees = if any isCoinbase ins then 0 else inv - outv+ ws = take (length (txIn (txData t))) $ txWitness (txData t) <> repeat []+ f n i = toInput i (IntMap.lookup n (txDataPrevs t)) (ws !! n)+ ins = zipWith f [0 ..] (txIn (txData t))+ g n o = toOutput o (IntMap.lookup n sm)+ outs = zipWith g [0 ..] (txOut (txData t))++fromTransaction :: Transaction -> (TxData, IntMap Spender)+fromTransaction t = (d, sm)+ where+ d =+ TxData+ { txDataBlock = transactionBlock t+ , txData = transactionData t+ , txDataPrevs = ps+ , txDataDeleted = transactionDeleted t+ , txDataRBF = transactionRBF t+ , txDataTime = transactionTime t+ }+ f _ StoreCoinbase {} = Nothing+ f n StoreInput {inputPkScript = s, inputAmount = v} =+ Just (n, Prev {prevScript = s, prevAmount = v})+ ps = IntMap.fromList . catMaybes $ zipWith f [0 ..] (transactionInputs t)+ g _ StoreOutput {outputSpender = Nothing} = Nothing+ g n StoreOutput {outputSpender = Just s} = Just (n, s)+ sm = IntMap.fromList . catMaybes $ zipWith g [0 ..] (transactionOutputs t)++-- | Detailed transaction information.+data Transaction =+ Transaction+ { transactionBlock :: !BlockRef+ -- ^ block information for this transaction+ , transactionVersion :: !Word32+ -- ^ transaction version+ , transactionLockTime :: !Word32+ -- ^ lock time+ , transactionInputs :: ![StoreInput]+ -- ^ transaction inputs+ , transactionOutputs :: ![StoreOutput]+ -- ^ transaction outputs+ , transactionDeleted :: !Bool+ -- ^ this transaction has been deleted and is no longer valid+ , transactionRBF :: !Bool+ -- ^ this transaction can be replaced in the mempool+ , transactionTime :: !Word64+ -- ^ time the transaction was first seen or time of block+ , transactionId :: !TxHash+ -- ^ transaction id+ , transactionSize :: !Word32+ -- ^ serialized transaction size (includes witness data)+ , transactionWeight :: !Word32+ -- ^ transaction weight+ , transactionFees :: !Word64+ -- ^ fees that this transaction pays (0 for coinbase)+ }+ deriving (Show, Eq, Ord, Generic, Hashable, Serialize, NFData)++transactionData :: Transaction -> Tx+transactionData t =+ Tx+ { txVersion = transactionVersion t+ , txIn = map i (transactionInputs t)+ , txOut = map o (transactionOutputs t)+ , txWitness = w $ map inputWitness (transactionInputs t)+ , txLockTime = transactionLockTime t+ }+ where+ i StoreCoinbase {inputPoint = p, inputSequence = q, inputSigScript = s} =+ TxIn {prevOutput = p, scriptInput = s, txInSequence = q}+ i StoreInput {inputPoint = p, inputSequence = q, inputSigScript = s} =+ TxIn {prevOutput = p, scriptInput = s, txInSequence = q}+ o StoreOutput {outputAmount = v, outputScript = s} =+ TxOut {outValue = v, scriptOutput = s}+ w xs | all null xs = []+ | otherwise = xs++transactionToJSON :: Network -> Transaction -> Value+transactionToJSON net dtx =+ object $+ [ "txid" .= transactionId dtx+ , "size" .= transactionSize dtx+ , "version" .= transactionVersion dtx+ , "locktime" .= transactionLockTime dtx+ , "fee" .= transactionFees dtx+ , "inputs" .= map (storeInputToJSON net) (transactionInputs dtx)+ , "outputs" .= map (storeOutputToJSON net) (transactionOutputs dtx)+ , "block" .= transactionBlock dtx+ , "deleted" .= transactionDeleted dtx+ , "time" .= transactionTime dtx+ , "rbf" .= transactionRBF dtx+ , "weight" .= transactionWeight dtx+ ]++transactionToEncoding :: Network -> Transaction -> Encoding+transactionToEncoding net dtx =+ pairs+ ( "txid" .= transactionId dtx+ <> "size" .= transactionSize dtx+ <> "version" .= transactionVersion dtx+ <> "locktime" .= transactionLockTime dtx+ <> "fee" .= transactionFees dtx+ <> "inputs" `pair` list (storeInputToEncoding net) (transactionInputs dtx)+ <> "outputs" `pair` list (storeOutputToEncoding net) (transactionOutputs dtx)+ <> "block" .= transactionBlock dtx+ <> "deleted" .= transactionDeleted dtx+ <> "time" .= transactionTime dtx+ <> "rbf" .= transactionRBF dtx+ <> "weight" .= transactionWeight dtx+ )++transactionParseJSON :: Network -> Value -> Parser Transaction+transactionParseJSON net =+ A.withObject "transaction" $ \o -> do+ version <- o .: "version"+ locktime <- o .: "locktime"+ inputs <- o .: "inputs" >>= mapM (storeInputParseJSON net)+ outputs <- o .: "outputs" >>= mapM (storeOutputParseJSON net)+ block <- o .: "block"+ deleted <- o .: "deleted"+ time <- o .: "time"+ rbf <- o .:? "rbf" .!= False+ weight <- o .:? "weight" .!= 0+ size <- o .: "size"+ txid <- o .: "txid"+ fees <- o .: "fee"+ return+ Transaction+ { transactionBlock = block+ , transactionVersion = version+ , transactionLockTime = locktime+ , transactionInputs = inputs+ , transactionOutputs = outputs+ , transactionDeleted = deleted+ , transactionTime = time+ , transactionRBF = rbf+ , transactionWeight = weight+ , transactionSize = size+ , transactionId = txid+ , transactionFees = fees+ }++-- | Information about a connected peer.+data PeerInformation =+ PeerInformation+ { peerUserAgent :: !ByteString+ -- ^ user agent string+ , peerAddress :: !String+ -- ^ network address+ , peerVersion :: !Word32+ -- ^ version number+ , peerServices :: !Word64+ -- ^ services field+ , peerRelay :: !Bool+ -- ^ will relay transactions+ }+ deriving (Show, Eq, Ord, Generic, NFData, Serialize)++instance ToJSON PeerInformation where+ toJSON p = object+ [ "useragent" .= String (cs (peerUserAgent p))+ , "address" .= peerAddress p+ , "version" .= peerVersion p+ , "services" .= String (encodeHex (S.encode (peerServices p)))+ , "relay" .= peerRelay p+ ]+ toEncoding p = pairs+ ( "useragent" `pair` text (cs (peerUserAgent p))+ <> "address" .= peerAddress p+ <> "version" .= peerVersion p+ <> "services" `pair` text (encodeHex (S.encode (peerServices p)))+ <> "relay" .= peerRelay p+ )++instance FromJSON PeerInformation where+ parseJSON =+ A.withObject "peerinformation" $ \o -> do+ String useragent <- o .: "useragent"+ address <- o .: "address"+ version <- o .: "version"+ services <-+ o .: "services" >>= jsonHex >>= \b ->+ case S.decode b of+ Left e -> fail $ "Could not decode services: " <> e+ Right s -> return s+ relay <- o .: "relay"+ return+ PeerInformation+ { peerUserAgent = cs useragent+ , peerAddress = address+ , peerVersion = version+ , peerServices = services+ , peerRelay = relay+ }++-- | Address balances for an extended public key.+data XPubBal =+ XPubBal+ { xPubBalPath :: ![KeyIndex]+ , xPubBal :: !Balance+ }+ deriving (Show, Ord, Eq, Generic, Serialize, NFData)++xPubBalToJSON :: Network -> XPubBal -> Value+xPubBalToJSON net XPubBal {xPubBalPath = p, xPubBal = b} =+ object ["path" .= p, "balance" .= balanceToJSON net b]++xPubBalToEncoding :: Network -> XPubBal -> Encoding+xPubBalToEncoding net XPubBal {xPubBalPath = p, xPubBal = b} =+ pairs ("path" .= p <> "balance" `pair` balanceToEncoding net b)++xPubBalParseJSON :: Network -> Value -> Parser XPubBal+xPubBalParseJSON net =+ A.withObject "xpubbal" $ \o -> do+ path <- o .: "path"+ balance <- balanceParseJSON net =<< o .: "balance"+ return XPubBal {xPubBalPath = path, xPubBal = balance}++-- | Unspent transaction for extended public key.+data XPubUnspent =+ XPubUnspent+ { xPubUnspentPath :: ![KeyIndex]+ , xPubUnspent :: !Unspent+ }+ deriving (Show, Eq, Generic, Serialize, NFData)++xPubUnspentToJSON :: Network -> XPubUnspent -> Value+xPubUnspentToJSON net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} =+ object ["path" .= p, "unspent" .= unspentToJSON net u]++xPubUnspentToEncoding :: Network -> XPubUnspent -> Encoding+xPubUnspentToEncoding net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} =+ pairs ("path" .= p <> "unspent" `pair` unspentToEncoding net u)++xPubUnspentParseJSON :: Network -> Value -> Parser XPubUnspent+xPubUnspentParseJSON net =+ A.withObject "xpubunspent" $ \o -> do+ p <- o .: "path"+ u <- o .: "unspent" >>= unspentParseJSON net+ return XPubUnspent {xPubUnspentPath = p, xPubUnspent = u}++data XPubSummary =+ XPubSummary+ { xPubSummaryConfirmed :: !Word64+ , xPubSummaryZero :: !Word64+ , xPubSummaryReceived :: !Word64+ , xPubUnspentCount :: !Word64+ , xPubExternalIndex :: !Word32+ , xPubChangeIndex :: !Word32+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++instance ToJSON XPubSummary where+ toJSON XPubSummary { xPubSummaryConfirmed = c+ , xPubSummaryZero = z+ , xPubSummaryReceived = r+ , xPubUnspentCount = u+ , xPubExternalIndex = ext+ , xPubChangeIndex = ch+ } =+ object+ [ "balance" .=+ object+ [ "confirmed" .= c+ , "unconfirmed" .= z+ , "received" .= r+ , "utxo" .= u+ ]+ , "indices" .= object ["change" .= ch, "external" .= ext]+ ]+ toEncoding XPubSummary { xPubSummaryConfirmed = c+ , xPubSummaryZero = z+ , xPubSummaryReceived = r+ , xPubUnspentCount = u+ , xPubExternalIndex = ext+ , xPubChangeIndex = ch+ } =+ pairs+ ( "balance" `pair` pairs+ ( "confirmed" .= c+ <> "unconfirmed" .= z+ <> "received" .= r+ <> "utxo" .= u+ )+ <> "indices" `pair` pairs+ ( "change" .= ch+ <> "external" .= ext+ )+ )++instance FromJSON XPubSummary where+ parseJSON =+ A.withObject "xpubsummary" $ \o -> do+ b <- o .: "balance"+ i <- o .: "indices"+ conf <- b .: "confirmed"+ unconfirmed <- b .: "unconfirmed"+ received <- b .: "received"+ utxo <- b .: "utxo"+ change <- i .: "change"+ external <- i .: "external"+ return+ XPubSummary+ { xPubSummaryConfirmed = conf+ , xPubSummaryZero = unconfirmed+ , xPubSummaryReceived = received+ , xPubUnspentCount = utxo+ , xPubExternalIndex = external+ , xPubChangeIndex = change+ }++class Healthy a where+ isOK :: a -> Bool++data BlockHealth =+ BlockHealth+ { blockHealthHeaders :: !BlockHeight+ , blockHealthBlocks :: !BlockHeight+ , blockHealthMaxDiff :: !Int+ }+ deriving (Show, Eq, Generic, NFData)++instance Serialize BlockHealth where+ put h@BlockHealth {..} = do+ put (isOK h)+ put blockHealthHeaders+ put blockHealthBlocks+ put blockHealthMaxDiff+ get = do+ k <- get+ blockHealthHeaders <- get+ blockHealthBlocks <- get+ blockHealthMaxDiff <- get+ let h = BlockHealth {..}+ guard (k == isOK h)+ return h++instance Healthy BlockHealth where+ isOK BlockHealth {..} =+ h - b <= blockHealthMaxDiff+ where+ h = fromIntegral blockHealthHeaders+ b = fromIntegral blockHealthBlocks++instance ToJSON BlockHealth where+ toJSON h@BlockHealth {..} =+ object+ [ "headers" .= blockHealthHeaders+ , "blocks" .= blockHealthBlocks+ , "diff" .= diff+ , "max" .= blockHealthMaxDiff+ , "ok" .= isOK h+ ]+ where+ diff = blockHealthHeaders - blockHealthBlocks++instance FromJSON BlockHealth where+ parseJSON =+ A.withObject "BlockHealth" $ \o -> do+ blockHealthHeaders <- o .: "headers"+ blockHealthBlocks <- o .: "blocks"+ blockHealthMaxDiff <- o .: "max"+ return BlockHealth {..}++data TimeHealth =+ TimeHealth+ { timeHealthAge :: !Int+ , timeHealthMax :: !Int+ }+ deriving (Show, Eq, Generic, NFData)++instance Serialize TimeHealth where+ put h@TimeHealth {..} = do+ put (isOK h)+ put timeHealthAge+ put timeHealthMax+ get = do+ k <- get+ timeHealthAge <- get+ timeHealthMax <- get+ let t = TimeHealth {..}+ guard (k == isOK t)+ return t++instance Healthy TimeHealth where+ isOK TimeHealth {..} =+ timeHealthAge <= timeHealthMax++instance ToJSON TimeHealth where+ toJSON h@TimeHealth {..} =+ object+ [ "age" .= timeHealthAge+ , "max" .= timeHealthMax+ , "ok" .= isOK h+ ]++instance FromJSON TimeHealth where+ parseJSON =+ A.withObject "TimeHealth" $ \o -> do+ timeHealthAge <- o .: "age"+ timeHealthMax <- o .: "max"+ return TimeHealth {..}++data CountHealth =+ CountHealth+ { countHealthNum :: !Int+ , countHealthMin :: !Int+ }+ deriving (Show, Eq, Generic, NFData)++instance Serialize CountHealth where+ put h@CountHealth {..} = do+ put (isOK h)+ put countHealthNum+ put countHealthMin+ get = do+ k <- get+ countHealthNum <- get+ countHealthMin <- get+ let c = CountHealth {..}+ guard (k == isOK c)+ return c++instance Healthy CountHealth where+ isOK CountHealth {..} =+ countHealthMin <= countHealthNum++instance ToJSON CountHealth where+ toJSON h@CountHealth {..} =+ object+ [ "count" .= countHealthNum+ , "min" .= countHealthMin+ , "ok" .= isOK h+ ]++instance FromJSON CountHealth where+ parseJSON =+ A.withObject "CountHealth" $ \o -> do+ countHealthNum <- o .: "count"+ countHealthMin <- o .: "min"+ return CountHealth {..}++data MaxHealth =+ MaxHealth+ { maxHealthNum :: !Int+ , maxHealthMax :: !Int+ }+ deriving (Show, Eq, Generic, NFData)++instance Serialize MaxHealth where+ put h@MaxHealth {..} = do+ put $ isOK h+ put maxHealthNum+ put maxHealthMax+ get = do+ k <- get+ maxHealthNum <- get+ maxHealthMax <- get+ let h = MaxHealth {..}+ guard (k == isOK h)+ return h++instance Healthy MaxHealth where+ isOK MaxHealth {..} = maxHealthNum <= maxHealthMax++instance ToJSON MaxHealth where+ toJSON h@MaxHealth {..} =+ object+ [ "count" .= maxHealthNum+ , "max" .= maxHealthMax+ , "ok" .= isOK h+ ]++instance FromJSON MaxHealth where+ parseJSON =+ A.withObject "MaxHealth" $ \o -> do+ maxHealthNum <- o .: "count"+ maxHealthMax <- o .: "max"+ return MaxHealth {..}++data HealthCheck =+ HealthCheck+ { healthBlocks :: !BlockHealth+ , healthLastBlock :: !TimeHealth+ , healthLastTx :: !TimeHealth+ , healthPendingTxs :: !MaxHealth+ , healthPeers :: !CountHealth+ , healthNetwork :: !String+ , healthVersion :: !String+ }+ deriving (Show, Eq, Generic, NFData)++instance Serialize HealthCheck where+ put h@HealthCheck {..} = do+ put (isOK h)+ put healthBlocks+ put healthLastBlock+ put healthLastTx+ put healthPendingTxs+ put healthPeers+ put healthNetwork+ put healthVersion+ get = do+ k <- get+ healthBlocks <- get+ healthLastBlock <- get+ healthLastTx <- get+ healthPendingTxs <- get+ healthPeers <- get+ healthNetwork <- get+ healthVersion <- get+ let h = HealthCheck {..}+ guard (k == isOK h)+ return h++instance Healthy HealthCheck where+ isOK HealthCheck {..} =+ isOK healthBlocks &&+ isOK healthLastBlock &&+ isOK healthLastTx &&+ isOK healthPendingTxs &&+ isOK healthPeers++instance ToJSON HealthCheck where+ toJSON h@HealthCheck {..} =+ object+ [ "blocks" .= healthBlocks+ , "last-block" .= healthLastBlock+ , "last-tx" .= healthLastTx+ , "pending-txs" .= healthPendingTxs+ , "peers" .= healthPeers+ , "net" .= healthNetwork+ , "version" .= healthVersion+ , "ok" .= isOK h+ ]++instance FromJSON HealthCheck where+ parseJSON =+ A.withObject "HealthCheck" $ \o -> do+ healthBlocks <- o .: "blocks"+ healthLastBlock <- o .: "last-block"+ healthLastTx <- o .: "last-tx"+ healthPendingTxs <- o .: "pending-txs"+ healthPeers <- o .: "peers"+ healthNetwork <- o .: "net"+ healthVersion <- o .: "version"+ return HealthCheck {..}++data Event+ = EventBlock !BlockHash+ | EventTx !TxHash+ deriving (Show, Eq, Generic, Serialize, NFData)++instance ToJSON Event where+ toJSON (EventTx h) = object ["type" .= String "tx", "id" .= h]+ toJSON (EventBlock h) = object ["type" .= String "block", "id" .= h]+ toEncoding (EventTx h) =+ pairs ("type" `pair` text "tx" <> "id" `pair` text (txHashToHex h))+ toEncoding (EventBlock h) =+ pairs+ ("type" `pair` text "block" <> "id" `pair` text (blockHashToHex h))++instance FromJSON Event where+ parseJSON =+ A.withObject "event" $ \o -> do+ t <- o .: "type"+ case t of+ "tx" -> do+ i <- o .: "id"+ return $ EventTx i+ "block" -> do+ i <- o .: "id"+ return $ EventBlock i+ _ -> fail $ "Could not recognize event type: " <> t++newtype GenericResult a =+ GenericResult+ { getResult :: a+ }+ deriving (Show, Eq, Generic, Serialize, NFData)++instance ToJSON a => ToJSON (GenericResult a) where+ toJSON (GenericResult b) = object ["result" .= b]+ toEncoding (GenericResult b) = pairs ("result" .= b)++instance FromJSON a => FromJSON (GenericResult a) where+ parseJSON =+ A.withObject "GenericResult" $ \o -> GenericResult <$> o .: "result"++newtype RawResult a =+ RawResult+ { getRawResult :: a+ }+ deriving (Show, Eq, Generic, Serialize, NFData)++instance S.Serialize a => ToJSON (RawResult a) where+ toJSON (RawResult b) =+ object [ "result" .= A.String (encodeHex $ S.encode b)]+ toEncoding (RawResult b) =+ pairs $ "result" `pair` unsafeToEncoding str+ where+ str = char7 '"' <> lazyByteStringHex (S.runPutLazy $ put b) <> char7 '"'++instance S.Serialize a => FromJSON (RawResult a) where+ parseJSON =+ A.withObject "RawResult" $ \o -> do+ res <- o .: "result"+ let valM = eitherToMaybe . S.decode =<< decodeHex res+ maybe mzero (return . RawResult) valM++newtype RawResultList a =+ RawResultList+ { getRawResultList :: [a]+ }+ deriving (Show, Eq, Generic, Serialize, NFData)++instance Semigroup (RawResultList a) where+ (RawResultList a) <> (RawResultList b) = RawResultList $ a <> b++instance Monoid (RawResultList a) where+ mempty = RawResultList mempty++instance S.Serialize a => ToJSON (RawResultList a) where+ toJSON (RawResultList xs) =+ toJSON $ encodeHex . S.encode <$> xs+ toEncoding (RawResultList xs) =+ list (unsafeToEncoding . str) xs+ where+ str x =+ char7 '"' <> lazyByteStringHex (S.runPutLazy (put x)) <> char7 '"'++instance S.Serialize a => FromJSON (RawResultList a) where+ parseJSON =+ A.withArray "RawResultList" $ \vec ->+ RawResultList <$> mapM parseElem (toList vec)+ where+ parseElem = A.withText "RawResultListElem" $ maybe mzero return . f+ f = eitherToMaybe . S.decode <=< decodeHex++newtype TxId =+ TxId TxHash+ deriving (Show, Eq, Generic, Serialize, NFData)++instance ToJSON TxId where+ toJSON (TxId h) = object ["txid" .= h]+ toEncoding (TxId h) = pairs ("txid" `pair` text (txHashToHex h))++instance FromJSON TxId where+ parseJSON = A.withObject "txid" $ \o -> TxId <$> o .: "txid"++data Except+ = ThingNotFound+ | ServerError+ | BadRequest+ | UserError !String+ | StringError !String+ | BlockTooLarge+ deriving (Show, Eq, Ord, Serialize, Generic, NFData)++instance Exception Except++instance ScottyError Except where+ stringError = StringError+ showError = TL.pack . show++instance ToJSON Except where+ toJSON e =+ object $+ case e of+ ThingNotFound ->+ ["error" .= String "not-found"]+ ServerError ->+ ["error" .= String "server-error"]+ BadRequest ->+ ["error" .= String "bad-request"]+ UserError msg ->+ [ "error" .= String "user-error"+ , "message" .= String (cs msg)+ ]+ StringError msg ->+ [ "error" .= String "string-error"+ , "message" .= String (cs msg)+ ]+ BlockTooLarge ->+ ["error" .= String "block-too-large"]++instance FromJSON Except where+ parseJSON =+ A.withObject "Except" $ \o -> do+ ctr <- o .: "error"+ msg <- fromMaybe "" <$> o .:? "message"+ case ctr of+ String "not-found" -> return ThingNotFound+ String "server-error" -> return ServerError+ String "bad-request" -> return BadRequest+ String "user-error" -> return $ UserError msg+ String "string-error" -> return $ StringError msg+ String "block-too-large" -> return BlockTooLarge+ _ -> mzero+++---------------------------------------+-- Blockchain.info API Compatibility --+---------------------------------------++type BinfoTxIndex = Int64++isBinfoTxIndexNull :: BinfoTxIndex -> Bool+isBinfoTxIndexNull = (0 >)++isBinfoTxIndexBlock :: BinfoTxIndex -> Bool+isBinfoTxIndexBlock n = 0 <= n && not (testBit n 48)++isBinfoTxIndexHash :: BinfoTxIndex -> Bool+isBinfoTxIndexHash n = 0 <= n && testBit n 48++encodeBinfoTxIndexHash :: BinfoTxIndex -> Maybe ByteString+encodeBinfoTxIndexHash n =+ if isBinfoTxIndexHash n+ then Just . B.drop 2 $ S.encode n+ else Nothing++hashToBinfoTxIndex :: TxHash -> BinfoTxIndex+hashToBinfoTxIndex h =+ fromRight (error "weird monkeys dancing") . S.decode $+ 0x00 `B.cons` 0x01 `B.cons` B.take 6 (S.encode h)++blockToBinfoTxIndex :: BlockHeight -> Word32 -> BinfoTxIndex+blockToBinfoTxIndex h p =+ let norm = (.&.) (2 ^ 24 - 1) . fromIntegral+ in norm h `shift` 24 .|. norm p++matchBinfoTxHash :: Int64 -> TxHash -> Bool+matchBinfoTxHash n h =+ let bn = B.drop 2 $ S.encode n+ bh = B.take 6 $ S.encode h+ in isBinfoTxIndexHash n && bn == bh++binfoTxIndexBlock :: BinfoTxIndex -> Maybe (BlockHeight, Word32)+binfoTxIndexBlock n =+ let norm = (.&.) (2 ^ 24 - 1) . fromIntegral+ height = norm $ n `shift` (-24)+ pos = norm n+ in if isBinfoTxIndexBlock n+ then Just (height, pos)+ else Nothing++binfoTransactionIndex :: Bool -> Transaction -> BinfoTxId+binfoTransactionIndex True t = BinfoTxIdHash (txHash (transactionData t))+binfoTransactionIndex False Transaction{transactionDeleted = True} =+ BinfoTxIdIndex (-1)+binfoTransactionIndex False t@Transaction{transactionBlock = MemRef _} =+ BinfoTxIdIndex (hashToBinfoTxIndex (txHash (transactionData t)))+binfoTransactionIndex False Transaction{transactionBlock = BlockRef h p} =+ BinfoTxIdIndex (blockToBinfoTxIndex h p)++data BinfoTxId+ = BinfoTxIdHash !TxHash+ | BinfoTxIdIndex !BinfoTxIndex+ deriving (Eq, Show, Read, Generic, Serialize, NFData)++instance Parsable BinfoTxId where+ parseParam t =+ case hexToTxHash (TL.toStrict t) of+ Nothing -> BinfoTxIdIndex <$> parseParam t+ Just h -> Right (BinfoTxIdHash h)++getBinfoTxId :: Maybe (HashMap TxHash Transaction) -> TxHash -> BinfoTxId+getBinfoTxId Nothing h = BinfoTxIdHash h+getBinfoTxId (Just m) h =+ case HashMap.lookup h m of+ Nothing -> BinfoTxIdIndex (-1)+ Just t -> binfoTransactionIndex False t++instance ToJSON BinfoTxId where+ toJSON (BinfoTxIdHash h) = toJSON h+ toJSON (BinfoTxIdIndex i) = toJSON i+ toEncoding (BinfoTxIdHash h) = toEncoding h+ toEncoding (BinfoTxIdIndex i) = toEncoding i++instance FromJSON BinfoTxId where+ parseJSON v = BinfoTxIdHash <$> parseJSON v <|>+ BinfoTxIdIndex <$> parseJSON v++data BinfoMultiAddr+ = BinfoMultiAddr+ { getBinfoMultiAddrAddresses :: ![BinfoAddress]+ , getBinfoMultiAddrWallet :: !BinfoWallet+ , getBinfoMultiAddrTxs :: ![BinfoTx]+ , getBinfoMultiAddrInfo :: !BinfoInfo+ , getBinfoMultiAddrRecommendFee :: !Bool+ , getBinfoMultiAddrCashAddr :: !Bool+ }+ deriving (Eq, Show, Generic, NFData)++binfoMultiAddrToJSON :: Network -> BinfoMultiAddr -> Value+binfoMultiAddrToJSON net' BinfoMultiAddr {..} =+ object $+ [ "addresses" .= map (binfoAddressToJSON net) getBinfoMultiAddrAddresses+ , "wallet" .= getBinfoMultiAddrWallet+ , "txs" .= map (binfoTxToJSON net) getBinfoMultiAddrTxs+ , "info" .= getBinfoMultiAddrInfo+ , "recommend_include_fee" .= getBinfoMultiAddrRecommendFee+ ] +++ [ "cash_addr" .= True | getBinfoMultiAddrCashAddr ]+ where+ net = if not getBinfoMultiAddrCashAddr && net' == bch then btc else net'++binfoMultiAddrParseJSON :: Network -> Value -> Parser BinfoMultiAddr+binfoMultiAddrParseJSON net = withObject "multiaddr" $ \o -> do+ getBinfoMultiAddrAddresses <-+ mapM (binfoAddressParseJSON net) =<< o .: "addresses"+ getBinfoMultiAddrWallet <- o .: "wallet"+ getBinfoMultiAddrTxs <-+ mapM (binfoTxParseJSON net) =<< o .: "txs"+ getBinfoMultiAddrInfo <- o .: "info"+ getBinfoMultiAddrRecommendFee <- o .: "recommend_include_fee"+ getBinfoMultiAddrCashAddr <- o .:? "cash_addr" .!= False+ return BinfoMultiAddr {..}++binfoMultiAddrToEncoding :: Network -> BinfoMultiAddr -> Encoding+binfoMultiAddrToEncoding net' BinfoMultiAddr {..} =+ pairs+ ( "addresses" `pair` as+ <> "wallet" .= getBinfoMultiAddrWallet+ <> "txs" `pair` ts+ <> "info" .= getBinfoMultiAddrInfo+ <> "recommend_include_fee" .= getBinfoMultiAddrRecommendFee+ <> if getBinfoMultiAddrCashAddr then "cash_addr" .= True else mempty+ )+ where+ as = list (binfoAddressToEncoding net) getBinfoMultiAddrAddresses+ ts = list (binfoTxToEncoding net) getBinfoMultiAddrTxs+ net = if not getBinfoMultiAddrCashAddr && net' == bch then btc else net'++data BinfoAddress+ = BinfoAddress+ { getBinfoAddress :: !Address+ , getBinfoAddrTxCount :: !Word64+ , getBinfoAddrReceived :: !Word64+ , getBinfoAddrSent :: !Word64+ , getBinfoAddrBalance :: !Word64+ }+ | BinfoXPubKey+ { getBinfoXPubKey :: !XPubKey+ , getBinfoAddrTxCount :: !Word64+ , getBinfoAddrReceived :: !Word64+ , getBinfoAddrSent :: !Word64+ , getBinfoAddrBalance :: !Word64+ , getBinfoXPubAccountIndex :: !Word32+ , getBinfoXPubChangeIndex :: !Word32+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++binfoAddressToJSON :: Network -> BinfoAddress -> Value+binfoAddressToJSON net BinfoAddress {..} =+ object+ [ "address" .= addrToJSON net getBinfoAddress+ , "final_balance" .= getBinfoAddrBalance+ , "n_tx" .= getBinfoAddrTxCount+ , "total_received" .= getBinfoAddrReceived+ , "total_sent" .= getBinfoAddrSent+ ]+binfoAddressToJSON net BinfoXPubKey {..} =+ object+ [ "address" .= xPubToJSON net getBinfoXPubKey+ , "change_index" .= getBinfoXPubChangeIndex+ , "account_index" .= getBinfoXPubAccountIndex+ , "final_balance" .= getBinfoAddrBalance+ , "n_tx" .= getBinfoAddrTxCount+ , "total_received" .= getBinfoAddrReceived+ , "total_sent" .= getBinfoAddrSent+ ]++binfoAddressParseJSON :: Network -> Value -> Parser BinfoAddress+binfoAddressParseJSON net = withObject "address" $ \o -> x o <|> a o+ where+ x o = do+ getBinfoXPubKey <- xPubFromJSON net =<< o .: "address"+ getBinfoXPubChangeIndex <- o .: "change_index"+ getBinfoXPubAccountIndex <- o .: "account_index"+ getBinfoAddrBalance <- o .: "final_balance"+ getBinfoAddrTxCount <- o .: "n_tx"+ getBinfoAddrReceived <- o .: "total_received"+ getBinfoAddrSent <- o .: "total_sent"+ return BinfoXPubKey{..}+ a o = do+ getBinfoAddress <- addrFromJSON net =<< o .: "address"+ getBinfoAddrBalance <- o .: "final_balance"+ getBinfoAddrTxCount <- o .: "n_tx"+ getBinfoAddrReceived <- o .: "total_received"+ getBinfoAddrSent <- o .: "total_sent"+ return BinfoAddress{..}++binfoAddressToEncoding :: Network -> BinfoAddress -> Encoding+binfoAddressToEncoding net BinfoAddress {..} =+ pairs+ ( "address" `pair` addrToEncoding net getBinfoAddress+ <> "final_balance" .= getBinfoAddrBalance+ <> "n_tx" .= getBinfoAddrTxCount+ <> "total_received" .= getBinfoAddrReceived+ <> "total_sent" .= getBinfoAddrSent+ )+binfoAddressToEncoding net BinfoXPubKey {..} =+ pairs+ ( "address" `pair` xPubToEncoding net getBinfoXPubKey+ <> "change_index" .= getBinfoXPubChangeIndex+ <> "account_index" .= getBinfoXPubAccountIndex+ <> "final_balance" .= getBinfoAddrBalance+ <> "n_tx" .= getBinfoAddrTxCount+ <> "total_received" .= getBinfoAddrReceived+ <> "total_sent" .= getBinfoAddrSent+ )++data BinfoWallet+ = BinfoWallet+ { getBinfoWalletBalance :: !Word64+ , getBinfoWalletTxCount :: !Word64+ , getBinfoWalletFilteredCount :: !Word64+ , getBinfoWalletTotalReceived :: !Word64+ , getBinfoWalletTotalSent :: !Word64+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++instance ToJSON BinfoWallet where+ toJSON BinfoWallet {..} =+ object+ [ "final_balance" .= getBinfoWalletBalance+ , "n_tx" .= getBinfoWalletTxCount+ , "n_tx_filtered" .= getBinfoWalletFilteredCount+ , "total_received" .= getBinfoWalletTotalReceived+ , "total_sent" .= getBinfoWalletTotalSent+ ]+ toEncoding BinfoWallet {..} =+ pairs+ ( "final_balance" .= getBinfoWalletBalance+ <> "n_tx" .= getBinfoWalletTxCount+ <> "n_tx_filtered" .= getBinfoWalletFilteredCount+ <> "total_received" .= getBinfoWalletTotalReceived+ <> "total_sent" .= getBinfoWalletTotalSent+ )++instance FromJSON BinfoWallet where+ parseJSON = withObject "wallet" $ \o -> do+ getBinfoWalletBalance <- o .: "final_balance"+ getBinfoWalletTxCount <- o .: "n_tx"+ getBinfoWalletFilteredCount <- o .: "n_tx_filtered"+ getBinfoWalletTotalReceived <- o .: "total_received"+ getBinfoWalletTotalSent <- o .: "total_sent"+ return BinfoWallet {..}++data BinfoTx+ = BinfoTx+ { getBinfoTxHash :: !TxHash+ , getBinfoTxVer :: !Word32+ , getBinfoTxVinSz :: !Word32+ , getBinfoTxVoutSz :: !Word32+ , getBinfoTxSize :: !Word32+ , getBinfoTxWeight :: !Word32+ , getBinfoTxFee :: !Word64+ , getBinfoTxRelayedBy :: !ByteString+ , getBinfoTxLockTime :: !Word32+ , getBinfoTxIndex :: !BinfoTxId+ , getBinfoTxDoubleSpend :: !Bool+ , getBinfoTxResultBal :: !(Maybe (Int64, Int64))+ , getBinfoTxTime :: !Word64+ , getBinfoTxBlockIndex :: !(Maybe Word32)+ , getBinfoTxBlockHeight :: !(Maybe Word32)+ , getBinfoTxInputs :: [BinfoTxInput]+ , getBinfoTxOutputs :: [BinfoTxOutput]+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++binfoTxToJSON :: Network -> BinfoTx -> Value+binfoTxToJSON net BinfoTx {..} =+ object $+ [ "hash" .= getBinfoTxHash+ , "ver" .= getBinfoTxVer+ , "vin_sz" .= getBinfoTxVinSz+ , "vout_sz" .= getBinfoTxVoutSz+ , "size" .= getBinfoTxSize+ , "weight" .= getBinfoTxWeight+ , "fee" .= getBinfoTxFee+ , "relayed_by" .= decodeUtf8 getBinfoTxRelayedBy+ , "lock_time" .= getBinfoTxLockTime+ , "tx_index" .= getBinfoTxIndex+ , "double_spend" .= getBinfoTxDoubleSpend+ , "time" .= getBinfoTxTime+ , "block_index" .= getBinfoTxBlockIndex+ , "block_height" .= getBinfoTxBlockHeight+ , "inputs" .= map (binfoTxInputToJSON net) getBinfoTxInputs+ , "out" .= map (binfoTxOutputToJSON net) getBinfoTxOutputs+ ] +++ case getBinfoTxResultBal of+ Nothing -> []+ Just (res, bal) -> ["result" .= res, "balance" .= bal]++binfoTxToEncoding :: Network -> BinfoTx -> Encoding+binfoTxToEncoding net BinfoTx {..} =+ pairs $+ "hash" .= getBinfoTxHash <>+ "ver" .= getBinfoTxVer <>+ "vin_sz" .= getBinfoTxVinSz <>+ "vout_sz" .= getBinfoTxVoutSz <>+ "size" .= getBinfoTxSize <>+ "weight" .= getBinfoTxWeight <>+ "fee" .= getBinfoTxFee <>+ "relayed_by" .= decodeUtf8 getBinfoTxRelayedBy <>+ "lock_time" .= getBinfoTxLockTime <>+ "tx_index" .= getBinfoTxIndex <>+ "double_spend" .= getBinfoTxDoubleSpend <>+ "time" .= getBinfoTxTime <>+ "block_index" .= getBinfoTxBlockIndex <>+ "block_height" .= getBinfoTxBlockHeight <>+ "inputs" `pair` list (binfoTxInputToEncoding net) getBinfoTxInputs <>+ "out" `pair` list (binfoTxOutputToEncoding net) getBinfoTxOutputs <>+ case getBinfoTxResultBal of+ Nothing -> mempty+ Just (res, bal) -> "result" .= res <> "balance" .= bal++binfoTxParseJSON :: Network -> Value -> Parser BinfoTx+binfoTxParseJSON net = withObject "tx" $ \o -> do+ getBinfoTxHash <- o .: "hash"+ getBinfoTxVer <- o .: "ver"+ getBinfoTxVinSz <- o .: "vin_sz"+ getBinfoTxVoutSz <- o .: "vout_sz"+ getBinfoTxSize <- o .: "size"+ getBinfoTxWeight <- o .: "weight"+ getBinfoTxFee <- o .: "fee"+ getBinfoTxRelayedBy <- encodeUtf8 <$> o .: "relayed_by"+ getBinfoTxLockTime <- o .: "lock_time"+ getBinfoTxIndex <- o .: "tx_index"+ getBinfoTxDoubleSpend <- o .: "double_spend"+ getBinfoTxTime <- o .: "time"+ getBinfoTxBlockIndex <- o .: "block_index"+ getBinfoTxBlockHeight <- o .: "block_height"+ getBinfoTxInputs <- o .: "inputs" >>= mapM (binfoTxInputParseJSON net)+ getBinfoTxOutputs <- o .: "out" >>= mapM (binfoTxOutputParseJSON net)+ res <- o .:? "result"+ bal <- o .:? "balance"+ let getBinfoTxResultBal = (,) <$> res <*> bal+ return BinfoTx {..}++data BinfoTxInput+ = BinfoTxInput+ { getBinfoTxInputSeq :: !Word32+ , getBinfoTxInputWitness :: !ByteString+ , getBinfoTxInputScript :: !ByteString+ , getBinfoTxInputIndex :: !Word32+ , getBinfoTxInputPrevOut :: !(Maybe BinfoTxOutput)+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++binfoTxInputToJSON :: Network -> BinfoTxInput -> Value+binfoTxInputToJSON net BinfoTxInput {..} =+ object+ [ "sequence" .= getBinfoTxInputSeq+ , "witness" .= encodeHex getBinfoTxInputWitness+ , "script" .= encodeHex getBinfoTxInputScript+ , "index" .= getBinfoTxInputIndex+ , "prev_out" .= (binfoTxOutputToJSON net <$> getBinfoTxInputPrevOut)+ ]++binfoTxInputToEncoding :: Network -> BinfoTxInput -> Encoding+binfoTxInputToEncoding net BinfoTxInput {..} =+ pairs+ ( "sequence" .= getBinfoTxInputSeq+ <> "witness" .= encodeHex getBinfoTxInputWitness+ <> "script" .= encodeHex getBinfoTxInputScript+ <> "index" .= getBinfoTxInputIndex+ <> "prev_out" .= (binfoTxOutputToJSON net <$> getBinfoTxInputPrevOut)+ )++binfoTxInputParseJSON :: Network -> Value -> Parser BinfoTxInput+binfoTxInputParseJSON net = withObject "txin" $ \o -> do+ getBinfoTxInputSeq <- o .: "sequence"+ getBinfoTxInputWitness <- maybe mzero return . decodeHex =<<+ o .: "witness"+ getBinfoTxInputScript <- maybe mzero return . decodeHex =<<+ o .: "script"+ getBinfoTxInputIndex <- o .: "index"+ getBinfoTxInputPrevOut <- o .:? "prev_out" >>=+ mapM (binfoTxOutputParseJSON net)+ return BinfoTxInput {..}++data BinfoTxOutput+ = BinfoTxOutput+ { getBinfoTxOutputType :: !Int+ , getBinfoTxOutputSpent :: !Bool+ , getBinfoTxOutputValue :: !Word64+ , getBinfoTxOutputIndex :: !Word32+ , getBinfoTxOutputTxIndex :: !BinfoTxId+ , getBinfoTxOutputScript :: !ByteString+ , getBinfoTxOutputSpenders :: ![BinfoSpender]+ , getBinfoTxOutputAddress :: !(Maybe Address)+ , getBinfoTxOutputXPub :: !(Maybe BinfoXPubPath)+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++binfoTxOutputToJSON :: Network -> BinfoTxOutput -> Value+binfoTxOutputToJSON net BinfoTxOutput {..} =+ object $+ [ "type" .= getBinfoTxOutputType+ , "spent" .= getBinfoTxOutputSpent+ , "value" .= getBinfoTxOutputValue+ , "spending_outpoints" .= getBinfoTxOutputSpenders+ , "n" .= getBinfoTxOutputIndex+ , "tx_index" .= getBinfoTxOutputTxIndex+ , "script" .= encodeHex getBinfoTxOutputScript+ ] <>+ [ "addr" .= addrToJSON net a+ | a <- maybeToList getBinfoTxOutputAddress+ ] <>+ [ "xpub" .= binfoXPubPathToJSON net x+ | x <- maybeToList getBinfoTxOutputXPub+ ]++binfoTxOutputToEncoding :: Network -> BinfoTxOutput -> Encoding+binfoTxOutputToEncoding net BinfoTxOutput {..} =+ pairs $ mconcat $+ [ "type" .= getBinfoTxOutputType+ , "spent" .= getBinfoTxOutputSpent+ , "value" .= getBinfoTxOutputValue+ , "spending_outpoints" .= getBinfoTxOutputSpenders+ , "n" .= getBinfoTxOutputIndex+ , "tx_index" .= getBinfoTxOutputTxIndex+ , "script" .= encodeHex getBinfoTxOutputScript+ ] <>+ [ "addr" .= addrToJSON net a+ | a <- maybeToList getBinfoTxOutputAddress+ ] <>+ [ "xpub" .= binfoXPubPathToJSON net x+ | x <- maybeToList getBinfoTxOutputXPub+ ]++binfoTxOutputParseJSON :: Network -> Value -> Parser BinfoTxOutput+binfoTxOutputParseJSON net = withObject "txout" $ \o -> do+ getBinfoTxOutputType <- o .: "type"+ getBinfoTxOutputSpent <- o .: "spent"+ getBinfoTxOutputValue <- o .: "value"+ getBinfoTxOutputSpenders <- o .: "spending_outpoints"+ getBinfoTxOutputIndex <- o .: "n"+ getBinfoTxOutputTxIndex <- o .: "tx_index"+ getBinfoTxOutputScript <- maybe mzero return . decodeHex =<< o .: "script"+ getBinfoTxOutputAddress <- o .:? "addr" >>= mapM (addrFromJSON net)+ getBinfoTxOutputXPub <- o .:? "xpub" >>= mapM (binfoXPubPathParseJSON net)+ return BinfoTxOutput {..}++data BinfoSpender+ = BinfoSpender+ { getBinfoSpenderTxIndex :: !BinfoTxId+ , getBinfoSpenderIndex :: !Word32+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++instance ToJSON BinfoSpender where+ toJSON BinfoSpender {..} =+ object+ [ "tx_index" .= getBinfoSpenderTxIndex+ , "n" .= getBinfoSpenderIndex+ ]+ toEncoding BinfoSpender {..} =+ pairs+ ( "tx_index" .= getBinfoSpenderTxIndex+ <> "n" .= getBinfoSpenderIndex+ )++instance FromJSON BinfoSpender where+ parseJSON = withObject "spender" $ \o -> do+ getBinfoSpenderTxIndex <- o .: "tx_index"+ getBinfoSpenderIndex <- o .: "n"+ return BinfoSpender {..}++data BinfoXPubPath+ = BinfoXPubPath+ { getBinfoXPubPathKey :: !XPubKey+ , getBinfoXPubPathDeriv :: !SoftPath+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++binfoXPubPathToJSON :: Network -> BinfoXPubPath -> Value+binfoXPubPathToJSON net BinfoXPubPath {..} =+ object+ [ "m" .= xPubToJSON net getBinfoXPubPathKey+ , "path" .= ("M" ++ pathToStr getBinfoXPubPathDeriv)+ ]++binfoXPubPathToEncoding :: Network -> BinfoXPubPath -> Encoding+binfoXPubPathToEncoding net BinfoXPubPath {..} =+ pairs $+ "m" `pair` xPubToEncoding net getBinfoXPubPathKey <>+ "path" .= ("M" ++ pathToStr getBinfoXPubPathDeriv)++binfoXPubPathParseJSON :: Network -> Value -> Parser BinfoXPubPath+binfoXPubPathParseJSON net = withObject "xpub" $ \o -> do+ getBinfoXPubPathKey <- o .: "m" >>= xPubFromJSON net+ getBinfoXPubPathDeriv <-+ fromMaybe "bad xpub path" . parseSoft <$> o .: "path"+ return BinfoXPubPath {..}++data BinfoInfo+ = BinfoInfo+ { getBinfoConnected :: !Word32+ , getBinfoConversion :: !Double+ , getBinfoLocal :: !BinfoSymbol+ , getBinfoBTC :: !BinfoSymbol+ , getBinfoLatestBlock :: !BinfoBlockInfo+ }+ deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoInfo where+ toJSON BinfoInfo {..} =+ object+ [ "nconnected" .= getBinfoConnected+ , "conversion" .= getBinfoConversion+ , "symbol_local" .= getBinfoLocal+ , "symbol_btc" .= getBinfoBTC+ , "latest_block" .= getBinfoLatestBlock+ ]+ toEncoding BinfoInfo {..} =+ pairs+ ( "nconnected" .= getBinfoConnected+ <> "conversion" .= getBinfoConversion+ <> "symbol_local" .= getBinfoLocal+ <> "symbol_btc" .= getBinfoBTC+ <> "latest_block" .= getBinfoLatestBlock+ )++instance FromJSON BinfoInfo where+ parseJSON = withObject "info" $ \o -> do+ getBinfoConnected <- o .: "nconnected"+ getBinfoConversion <- o .: "conversion"+ getBinfoLocal <- o .: "symbol_local"+ getBinfoBTC <- o .: "symbol_btc"+ getBinfoLatestBlock <- o .: "latest_block"+ return BinfoInfo {..}++data BinfoBlockInfo+ = BinfoBlockInfo+ { getBinfoBlockInfoHash :: !BlockHash+ , getBinfoBlockInfoHeight :: !BlockHeight+ , getBinfoBlockInfoTime :: !Word32+ , getBinfoBlockInfoIndex :: !BlockHeight+ }+ deriving (Eq, Show, Generic, Serialize, NFData)++instance ToJSON BinfoBlockInfo where+ toJSON BinfoBlockInfo {..} =+ object+ [ "hash" .= getBinfoBlockInfoHash+ , "height" .= getBinfoBlockInfoHeight+ , "time" .= getBinfoBlockInfoTime+ , "block_index" .= getBinfoBlockInfoIndex+ ]+ toEncoding BinfoBlockInfo {..} =+ pairs+ ( "hash" .= getBinfoBlockInfoHash+ <> "height" .= getBinfoBlockInfoHeight+ <> "time" .= getBinfoBlockInfoTime+ <> "block_index" .= getBinfoBlockInfoIndex+ )++instance FromJSON BinfoBlockInfo where+ parseJSON = withObject "block_info" $ \o -> do+ getBinfoBlockInfoHash <- o .: "hash"+ getBinfoBlockInfoHeight <- o .: "height"+ getBinfoBlockInfoTime <- o .: "time"+ getBinfoBlockInfoIndex <- o .: "block_index"+ return BinfoBlockInfo {..}++data BinfoTicker+ = BinfoTicker+ { binfoTickerSymbol :: !Text+ , binfoTickerPrice24h :: !Double+ , binfoTickerVol24h :: !Double+ , binfoTickerLastPrice :: !Double+ }+ deriving (Eq, Show, Generic, NFData)++instance Default BinfoTicker where+ def = BinfoTicker{ binfoTickerSymbol = "XXX"+ , binfoTickerPrice24h = 0.0+ , binfoTickerVol24h = 0.0+ , binfoTickerLastPrice = 0.0+ }++instance ToJSON BinfoTicker where+ toJSON BinfoTicker{..} =+ object+ [ "symbol" .= binfoTickerSymbol+ , "price_24h" .= binfoTickerPrice24h+ , "volume_24h" .= binfoTickerVol24h+ , "last_trade_price" .= binfoTickerLastPrice+ ]++instance FromJSON BinfoTicker where+ parseJSON = withObject "ticker" $ \o -> do+ binfoTickerSymbol <- o .: "symbol"+ binfoTickerPrice24h <- o .: "price_24h"+ binfoTickerVol24h <- o .: "volume_24h"+ binfoTickerLastPrice <- o .: "last_trade_price"+ return BinfoTicker{..}++data BinfoSymbol+ = BinfoSymbol+ { getBinfoSymbolCode :: !Text+ , getBinfoSymbolString :: !Text+ , getBinfoSymbolName :: !Text+ , getBinfoSymbolConversion :: !Double+ , getBinfoSymbolAfter :: !Bool+ , getBinfoSymbolLocal :: !Bool+ }+ deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoSymbol where+ toJSON BinfoSymbol {..} =+ object+ [ "code" .= getBinfoSymbolCode+ , "symbol" .= getBinfoSymbolString+ , "name" .= getBinfoSymbolName+ , "conversion" .= getBinfoSymbolConversion+ , "symbolAppearsAfter" .= getBinfoSymbolAfter+ , "local" .= getBinfoSymbolLocal+ ]+ toEncoding BinfoSymbol {..} =+ pairs+ ( "code" .= getBinfoSymbolCode+ <> "symbol" .= getBinfoSymbolString+ <> "name" .= getBinfoSymbolName+ <> "conversion" .= getBinfoSymbolConversion+ <> "symbolAppearsAfter" .= getBinfoSymbolAfter+ <> "local" .= getBinfoSymbolLocal+ )++instance FromJSON BinfoSymbol where+ parseJSON = withObject "symbol" $ \o -> do+ getBinfoSymbolCode <- o .: "code"+ getBinfoSymbolString <- o .: "symbol"+ getBinfoSymbolName <- o .: "name"+ getBinfoSymbolConversion <- o .: "conversion"+ getBinfoSymbolAfter <- o .: "symbolAppearsAfter"+ getBinfoSymbolLocal <- o .: "local"+ return BinfoSymbol {..}++relevantTxs :: HashSet Address+ -> Bool+ -> Transaction+ -> HashSet TxHash+relevantTxs addrs prune t@Transaction{..} =+ let p a = prune && getTxResult addrs t > 0 && not (HashSet.member a addrs)+ f StoreOutput{..} =+ case outputSpender of+ Nothing -> Nothing+ Just Spender{..} ->+ case outputAddress of+ Nothing -> Nothing+ Just a | p a -> Nothing+ | otherwise -> Just spenderHash+ outs = mapMaybe f transactionOutputs+ g StoreCoinbase{} = Nothing+ g StoreInput{inputPoint = OutPoint{..}} = Just outPointHash+ ins = mapMaybe g transactionInputs+ in HashSet.fromList $ ins <> outs++toBinfoAddrs :: HashMap Address Balance+ -> HashMap XPubKey [XPubBal]+ -> HashMap XPubKey Int+ -> [BinfoAddress]+toBinfoAddrs only_addrs only_xpubs xpub_txs =+ xpub_bals <> addr_bals+ where+ xpub_bal k xs =+ let f x = case xPubBalPath x of+ [0, _] -> balanceTotalReceived (xPubBal x)+ _ -> 0+ g x = balanceAmount (xPubBal x) + balanceZero (xPubBal x)+ i m x = case xPubBalPath x of+ [m', n] | m == m' -> n + 1+ _ -> 0+ 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+ ax = foldl max 0 (map (i 0) xs)+ cx = foldl max 0 (map (i 1) xs)+ in BinfoXPubKey{ getBinfoXPubKey = k+ , getBinfoAddrTxCount = count+ , getBinfoAddrReceived = received+ , getBinfoAddrSent = sent+ , getBinfoAddrBalance = bal+ , getBinfoXPubAccountIndex = ax+ , getBinfoXPubChangeIndex = cx+ }+ xpub_bals = map (uncurry xpub_bal) (HashMap.toList only_xpubs)+ addr_bals =+ let f Balance{..} =+ let addr = balanceAddress+ sent = recv - bal+ recv = balanceTotalReceived+ tx_count = balanceTxCount+ bal = balanceAmount + balanceZero+ in BinfoAddress{ getBinfoAddress = addr+ , getBinfoAddrTxCount = tx_count+ , getBinfoAddrReceived = recv+ , getBinfoAddrSent = sent+ , getBinfoAddrBalance = bal+ }+ in map f $ HashMap.elems only_addrs++toBinfoTxSimple :: Maybe (HashMap TxHash Transaction)+ -> Transaction+ -> BinfoTx+toBinfoTxSimple etxs =+ toBinfoTx etxs HashMap.empty HashSet.empty False 0++toBinfoTxInputs :: Maybe (HashMap TxHash Transaction)+ -> HashMap Address (Maybe BinfoXPubPath)+ -> Transaction+ -> [BinfoTxInput]+toBinfoTxInputs etxs abook t =+ zipWith f [0..] (transactionInputs t)+ where+ f n i = BinfoTxInput{ getBinfoTxInputIndex = n+ , getBinfoTxInputSeq = inputSequence i+ , getBinfoTxInputScript = inputSigScript i+ , getBinfoTxInputWitness = wit i+ , getBinfoTxInputPrevOut = prev n i+ }+ wit i =+ case inputWitness i of+ [] -> B.empty+ ws -> S.runPut (put_witness ws)+ prev = inputToBinfoTxOutput etxs abook t+ put_witness ws = do+ putVarInt (length ws)+ mapM_ put_item ws+ put_item bs = do+ putVarInt (B.length bs)+ S.putByteString bs++toBinfoBlockIndex :: Transaction -> Maybe BlockHeight+toBinfoBlockIndex Transaction{transactionDeleted = True} = Nothing+toBinfoBlockIndex Transaction{transactionBlock = MemRef _} = Nothing+toBinfoBlockIndex Transaction{transactionBlock = BlockRef h _} = Just h++toBinfoTx :: Maybe (HashMap TxHash Transaction)+ -> HashMap Address (Maybe BinfoXPubPath)+ -> HashSet Address+ -> Bool+ -> Int64+ -> Transaction+ -> BinfoTx+toBinfoTx etxs abook saddrs prune bal t@Transaction{..} =+ BinfoTx{ getBinfoTxHash = txHash (transactionData t)+ , getBinfoTxVer = transactionVersion+ , getBinfoTxVinSz = fromIntegral (length transactionInputs)+ , getBinfoTxVoutSz = fromIntegral (length transactionOutputs)+ , getBinfoTxSize = transactionSize+ , getBinfoTxWeight = transactionWeight+ , getBinfoTxFee = transactionFees+ , getBinfoTxRelayedBy = "0.0.0.0"+ , getBinfoTxLockTime = transactionLockTime+ , getBinfoTxIndex = binfoTransactionIndex (isNothing etxs) t+ , getBinfoTxDoubleSpend = transactionRBF+ , getBinfoTxTime = transactionTime+ , getBinfoTxBlockIndex = toBinfoBlockIndex t+ , getBinfoTxBlockHeight = toBinfoBlockIndex t+ , getBinfoTxInputs = toBinfoTxInputs etxs abook t+ , getBinfoTxOutputs = outs+ , getBinfoTxResultBal = resbal+ }+ where+ simple = HashSet.null saddrs && HashMap.null abook && bal == 0+ resbal = if simple then Nothing else Just (getTxResult saddrs t, bal)+ outs =+ let p = prune && getTxResult saddrs t > 0+ f = toBinfoTxOutput etxs abook p t+ in catMaybes $ zipWith f [0..] transactionOutputs++getTxResult :: HashSet Address -> Transaction -> Int64+getTxResult saddrs Transaction{..} =+ let input_sum = sum $ map input_value transactionInputs+ input_value StoreCoinbase{} = 0+ input_value StoreInput{..} =+ case inputAddress of+ Nothing -> 0+ Just a ->+ if test_addr a+ then negate $ fromIntegral inputAmount+ else 0+ test_addr a = HashSet.member a saddrs+ output_sum = sum $ map out_value transactionOutputs+ out_value StoreOutput{..} =+ case outputAddress of+ Nothing -> 0+ Just a ->+ if test_addr a+ then fromIntegral outputAmount+ else 0+ in input_sum + output_sum++toBinfoTxOutput :: Maybe (HashMap TxHash Transaction)+ -> HashMap Address (Maybe BinfoXPubPath)+ -> Bool+ -> Transaction+ -> Word32+ -> StoreOutput+ -> Maybe BinfoTxOutput+toBinfoTxOutput etxs abook prune t n StoreOutput{..} =+ let getBinfoTxOutputType = 0+ getBinfoTxOutputSpent = isJust outputSpender+ getBinfoTxOutputValue = outputAmount+ getBinfoTxOutputIndex = n+ getBinfoTxOutputTxIndex = binfoTransactionIndex (isNothing etxs) t+ getBinfoTxOutputScript = outputScript+ getBinfoTxOutputSpenders =+ maybeToList $ toBinfoSpender etxs <$> outputSpender+ getBinfoTxOutputAddress = outputAddress+ getBinfoTxOutputXPub =+ outputAddress >>= join . (`HashMap.lookup` abook)+ in if prune && isNothing (outputAddress >>= (`HashMap.lookup` abook))+ then Nothing+ else Just BinfoTxOutput{..}++toBinfoSpender :: Maybe (HashMap TxHash Transaction)+ -> Spender+ -> BinfoSpender+toBinfoSpender etxs Spender{..} =+ let getBinfoSpenderTxIndex = getBinfoTxId etxs spenderHash+ getBinfoSpenderIndex = spenderIndex+ in BinfoSpender{..}++inputToBinfoTxOutput :: Maybe (HashMap TxHash Transaction)+ -> HashMap Address (Maybe BinfoXPubPath)+ -> Transaction+ -> Word32+ -> StoreInput+ -> Maybe BinfoTxOutput+inputToBinfoTxOutput _ _ _ _ StoreCoinbase{} = Nothing+inputToBinfoTxOutput etxs abook t n StoreInput{..} =+ Just BinfoTxOutput{ getBinfoTxOutputIndex = out_index+ , getBinfoTxOutputType = 0+ , getBinfoTxOutputSpent = True+ , getBinfoTxOutputValue = inputAmount+ , getBinfoTxOutputTxIndex = getBinfoTxId etxs out_hash+ , getBinfoTxOutputScript = inputPkScript+ , getBinfoTxOutputSpenders = [spender]+ , getBinfoTxOutputAddress = inputAddress+ , getBinfoTxOutputXPub = xpub+ }+ where+ OutPoint out_hash out_index = inputPoint+ spender = BinfoSpender (binfoTransactionIndex (isNothing etxs) t) n+ xpub = inputAddress >>= join . (`HashMap.lookup` abook)++data BinfoAddr+ = BinfoAddr !Address+ | BinfoXpub !XPubKey+ deriving (Eq, Show, Generic, Serialize, Hashable, NFData)++parseBinfoAddr :: Network -> Text -> Maybe [BinfoAddr]+parseBinfoAddr _ "" = Just []+parseBinfoAddr net s =+ mapM f $ T.splitOn "|" s+ where+ f x = BinfoAddr <$> textToAddr net x <|> BinfoXpub <$> xPubImport net x
src/Haskoin/Store/WebCommon.hs view
@@ -4,32 +4,35 @@ {-# 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.Default (Default, def)-import Data.Proxy (Proxy(..))-import qualified Data.Serialize as S-import Data.String (IsString (..))-import Data.String.Conversions (cs)-import Data.Text (Text)-import qualified Data.Text as Text+import Control.Applicative ((<|>))+import Control.Monad (guard)+import Data.Default (Default, def)+import Data.Proxy (Proxy (..))+import qualified Data.Serialize as S+import Data.String (IsString (..))+import Data.String.Conversions (cs)+import Data.Text (Text)+import qualified Data.Text as T import Haskoin.Address-import Haskoin.Block (Block, BlockHash, blockHashToHex,- hexToBlockHash)+import Haskoin.Block (Block, BlockHash, blockHashToHex,+ hexToBlockHash) import Haskoin.Constants-import Haskoin.Crypto (Hash256)+import Haskoin.Crypto (Hash256) import Haskoin.Keys-import qualified Haskoin.Store.Data as Store+import qualified Haskoin.Store.Data as Store import Haskoin.Transaction-import Network.HTTP.Types (StdMethod (..))-import Numeric.Natural (Natural)-import Text.Read (readMaybe)-import qualified Web.Scotty.Trans as Scotty+import Network.HTTP.Types (StdMethod (..))+import Numeric.Natural (Natural)+import Text.Read (readMaybe)+import qualified Web.Scotty.Trans as Scotty ------------------- -- API Resources --@@ -336,7 +339,7 @@ noMaybeBox :: (Param p, Eq p) => Maybe p -> [ParamBox] noMaybeBox (Just p) = [ParamBox p]-noMaybeBox _ = []+noMaybeBox _ = [] asProxy :: a -> Proxy a asProxy = const Proxy@@ -405,7 +408,7 @@ parseParam _ [s] = parseHash <|> parseHeight <|> parseUnix where parseHash = do- guard (Text.length s == 32 * 2)+ guard (T.length s == 32 * 2) TxHash x <- hexToTxHash s return $ StartParamHash x parseHeight = do@@ -558,3 +561,7 @@ encodeParam _ ts = Just $ txHashToHex <$> ts parseParam _ = mapM hexToTxHash ++---------------------------------------+-- Blockchain.info API Compatibility --+---------------------------------------
test/Haskoin/Store/DataSpec.hs view
@@ -2,19 +2,24 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Haskoin.Store.DataSpec ( spec ) where -import Control.Monad (forM_)+import Control.Monad (forM_, replicateM) 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)+import Test.Hspec (Spec, describe, it)+import Test.Hspec.QuickCheck (prop) import Test.QuickCheck serialVals :: [SerialBox]@@ -43,6 +48,15 @@ , SerialBox (arbitrary :: Gen (RawResult BlockData)) , SerialBox (arbitrary :: Gen (RawResultList BlockData)) , SerialBox (arbitrary :: Gen Except)+ , SerialBox (arbitrary :: Gen BinfoWallet)+ , SerialBox (arbitrary :: Gen BinfoAddress)+ , SerialBox (arbitrary :: Gen BinfoBlockInfo)+ , SerialBox (arbitrary :: Gen BinfoXPubPath)+ , SerialBox (arbitrary :: Gen BinfoSpender)+ , SerialBox (arbitrary :: Gen BinfoTxOutput)+ , SerialBox (arbitrary :: Gen BinfoTxInput)+ , SerialBox (arbitrary :: Gen BinfoTx)+ , SerialBox (arbitrary :: Gen BinfoTxId) ] jsonVals :: [JsonBox]@@ -59,6 +73,13 @@ , JsonBox (arbitrary :: Gen (RawResult BlockData)) , JsonBox (arbitrary :: Gen (RawResultList BlockData)) , JsonBox (arbitrary :: Gen Except)+ , JsonBox (arbitrary :: Gen BinfoWallet)+ , JsonBox (arbitrary :: Gen BinfoSymbol)+ , JsonBox (arbitrary :: Gen BinfoBlockInfo)+ , JsonBox (arbitrary :: Gen BinfoInfo)+ , JsonBox (arbitrary :: Gen BinfoSpender)+ , JsonBox (arbitrary :: Gen BinfoTicker)+ , JsonBox (arbitrary :: Gen BinfoTxId) ] netVals :: [NetBox]@@ -94,7 +115,31 @@ , NetBox ( transactionToJSON , transactionToEncoding , transactionParseJSON- , arbitraryTransactionNet)+ , arbitraryNetData)+ , NetBox ( binfoMultiAddrToJSON+ , binfoMultiAddrToEncoding+ , binfoMultiAddrParseJSON+ , arbitraryNetData)+ , NetBox ( binfoAddressToJSON+ , binfoAddressToEncoding+ , binfoAddressParseJSON+ , arbitraryNetData)+ , NetBox ( binfoTxToJSON+ , binfoTxToEncoding+ , binfoTxParseJSON+ , arbitraryNetData)+ , NetBox ( binfoTxInputToJSON+ , binfoTxInputToEncoding+ , binfoTxInputParseJSON+ , arbitraryNetData)+ , NetBox ( binfoTxOutputToJSON+ , binfoTxOutputToEncoding+ , binfoTxOutputParseJSON+ , arbitraryNetData)+ , NetBox ( binfoXPubPathToJSON+ , binfoXPubPathToEncoding+ , binfoXPubPathParseJSON+ , arbitraryNetData) ] spec :: Spec@@ -105,7 +150,18 @@ forM_ jsonVals $ \(JsonBox g) -> testJson g describe "Data.Aeson Encoding with Network" $ forM_ netVals $ \(NetBox (j,e,p,g)) -> testNetJson j e p g+ describe "Blockchain.info API" $ do+ it "compresses txids correctly" $+ forAll arbitraryTxHash $ \h ->+ matchBinfoTxHash (hashToBinfoTxIndex h) h+ prop "compresses blockchain locations correctly" $+ let x = choose (0, 2 ^ 24 - 1)+ in forAll ((,) <$> x <*> x) $ \(b, p) ->+ let i = blockToBinfoTxIndex b p+ Just (b', p') = binfoTxIndexBlock i+ in b == b' && p == p' + instance Arbitrary BlockRef where arbitrary = oneof [BlockRef <$> arbitrary <*> arbitrary, MemRef <$> arbitrary]@@ -130,14 +186,14 @@ <$> arbitraryOutPoint <*> arbitrary <*> arbitraryBS1- <*> arbitraryMaybe (listOf arbitraryBS1)+ <*> listOf arbitraryBS1 , StoreInput <$> arbitraryOutPoint <*> arbitrary <*> arbitraryBS1 <*> arbitraryBS1 <*> arbitrary- <*> arbitraryMaybe (listOf arbitraryBS1)+ <*> listOf arbitraryBS1 <*> arbitraryMaybe arbitraryAddress ] @@ -146,7 +202,7 @@ net <- arbitraryNetwork store <- arbitrary let res | getSegWit net = store- | otherwise = store{ inputWitness = Nothing }+ | otherwise = store{ inputWitness = [] } return (net, res) instance Arbitrary Spender where@@ -188,7 +244,7 @@ | otherwise = val1{ transactionRBF = False } return (net, res) where- f i = i {inputWitness = Nothing}+ f i = i {inputWitness = []} instance Arbitrary PeerInformation where arbitrary =@@ -343,3 +399,135 @@ , StringError <$> arbitrary , return BlockTooLarge ]++---------------------------------------+-- Blockchain.info API Compatibility --+---------------------------------------++instance Arbitrary BinfoTxId where+ arbitrary = oneof+ [ BinfoTxIdHash <$> arbitraryTxHash+ , BinfoTxIdIndex <$> arbitrary+ ]++instance Arbitrary BinfoMultiAddr where+ arbitrary = do+ getBinfoMultiAddrAddresses <- arbitrary+ getBinfoMultiAddrWallet <- arbitrary+ getBinfoMultiAddrTxs <- arbitrary+ getBinfoMultiAddrInfo <- arbitrary+ getBinfoMultiAddrRecommendFee <- arbitrary+ getBinfoMultiAddrCashAddr <- arbitrary+ return BinfoMultiAddr {..}++instance Arbitrary BinfoAddress where+ arbitrary = do+ getBinfoAddress <- arbitraryAddress+ getBinfoAddrTxCount <- arbitrary+ getBinfoAddrReceived <- arbitrary+ getBinfoAddrSent <- arbitrary+ getBinfoAddrBalance <- arbitrary+ getBinfoXPubKey <- snd <$> arbitraryXPubKey+ getBinfoXPubAccountIndex <- arbitrary+ getBinfoXPubChangeIndex <- arbitrary+ elements [BinfoAddress {..}, BinfoXPubKey {..}]++instance Arbitrary BinfoWallet where+ arbitrary = do+ getBinfoWalletBalance <- arbitrary+ getBinfoWalletTxCount <- arbitrary+ getBinfoWalletFilteredCount <- arbitrary+ getBinfoWalletTotalReceived <- arbitrary+ getBinfoWalletTotalSent <- arbitrary+ return BinfoWallet {..}++instance Arbitrary BinfoTx where+ arbitrary = do+ getBinfoTxHash <- arbitraryTxHash+ getBinfoTxVer <- arbitrary+ getBinfoTxInputs <- resize 10 $ listOf1 arbitrary+ getBinfoTxOutputs <- resize 10 $ listOf1 arbitrary+ let getBinfoTxVinSz = fromIntegral (length getBinfoTxInputs)+ getBinfoTxVoutSz = fromIntegral (length getBinfoTxOutputs)+ getBinfoTxSize <- arbitrary+ getBinfoTxWeight <- arbitrary+ getBinfoTxFee <- arbitrary+ getBinfoTxRelayedBy <- cs <$> listOf arbitraryUnicodeChar+ getBinfoTxLockTime <- arbitrary+ getBinfoTxIndex <- arbitrary+ getBinfoTxDoubleSpend <- arbitrary+ getBinfoTxTime <- arbitrary+ getBinfoTxBlockIndex <- arbitrary+ getBinfoTxBlockHeight <- arbitrary+ getBinfoTxResultBal <- arbitrary+ return BinfoTx {..}++instance Arbitrary BinfoTxInput where+ arbitrary = do+ getBinfoTxInputSeq <- arbitrary+ getBinfoTxInputWitness <- B.pack <$> listOf arbitrary+ getBinfoTxInputScript <- B.pack <$> listOf arbitrary+ getBinfoTxInputIndex <- arbitrary+ getBinfoTxInputPrevOut <- arbitrary+ return BinfoTxInput {..}++instance Arbitrary BinfoTxOutput where+ arbitrary = do+ getBinfoTxOutputType <- arbitrary+ getBinfoTxOutputSpent <- arbitrary+ getBinfoTxOutputValue <- arbitrary+ getBinfoTxOutputIndex <- arbitrary+ getBinfoTxOutputTxIndex <- arbitrary+ getBinfoTxOutputScript <- B.pack <$> listOf arbitrary+ getBinfoTxOutputSpenders <- arbitrary+ getBinfoTxOutputAddress <-+ oneof [return Nothing, Just <$> arbitraryAddress]+ getBinfoTxOutputXPub <- arbitrary+ return BinfoTxOutput {..}++instance Arbitrary BinfoSpender where+ arbitrary = do+ getBinfoSpenderTxIndex <- arbitrary+ getBinfoSpenderIndex <- arbitrary+ return BinfoSpender {..}++instance Arbitrary BinfoXPubPath where+ arbitrary = do+ getBinfoXPubPathKey <- snd <$> arbitraryXPubKey+ getBinfoXPubPathDeriv <- arbitrarySoftPath+ return BinfoXPubPath {..}++instance Arbitrary BinfoInfo where+ arbitrary = do+ getBinfoConnected <- arbitrary+ getBinfoConversion <- arbitrary+ getBinfoLocal <- arbitrary+ getBinfoBTC <- arbitrary+ getBinfoLatestBlock <- arbitrary+ return BinfoInfo {..}++instance Arbitrary BinfoBlockInfo where+ arbitrary = do+ getBinfoBlockInfoHash <- arbitraryBlockHash+ getBinfoBlockInfoHeight <- arbitrary+ getBinfoBlockInfoTime <- arbitrary+ getBinfoBlockInfoIndex <- arbitrary+ return BinfoBlockInfo {..}++instance Arbitrary BinfoSymbol where+ arbitrary = do+ getBinfoSymbolCode <- cs <$> listOf1 arbitraryUnicodeChar+ getBinfoSymbolString <- cs <$> listOf1 arbitraryUnicodeChar+ getBinfoSymbolName <- cs <$> listOf1 arbitraryUnicodeChar+ getBinfoSymbolConversion <- arbitrary+ getBinfoSymbolAfter <- arbitrary+ getBinfoSymbolLocal <- arbitrary+ return BinfoSymbol {..}++instance Arbitrary BinfoTicker where+ arbitrary = do+ binfoTickerSymbol <- cs <$> listOf1 arbitraryUnicodeChar+ binfoTickerPrice24h <- arbitrary+ binfoTickerVol24h <- arbitrary+ binfoTickerLastPrice <- arbitrary+ return BinfoTicker{..}
test/Haskoin/Store/WebCommonSpec.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Haskoin.Store.WebCommonSpec ( spec@@ -10,7 +11,7 @@ import Control.Monad (forM_) import Data.Proxy import Data.String.Conversions (cs)-import Data.Word (Word64)+import Data.Word import Haskoin import Haskoin.Store.Data import Haskoin.Store.DataSpec ()@@ -53,7 +54,7 @@ forAll pGen $ \p -> case encodeParam btc p of Just txts -> parseParam btc txts `shouldBe` Just p- _ -> expectationFailure "Param encoding failed"+ _ -> expectationFailure "Param encoding failed" where name = cs $ proxyLabel $ proxy pGen proxy :: Gen a -> Proxy a