packages feed

haskoin-store-data 0.65.12 → 1.0.0

raw patch · 8 files changed

+5724/−4036 lines, 8 filesdep +vectordep ~haskoin-corePVP ok

version bump matches the API change (PVP)

Dependencies added: vector

Dependency ranges changed: haskoin-core

API changes (from Hackage documentation)

- Haskoin.Store.Data: BinfoBlocks :: [BinfoBlockInfo] -> BinfoBlocks
- Haskoin.Store.Data: PeerInformation :: !ByteString -> !String -> !Word32 -> !Word64 -> !Bool -> PeerInformation
- Haskoin.Store.Data: [balanceAddress] :: Balance -> !Address
- Haskoin.Store.Data: [balanceAmount] :: Balance -> !Word64
- Haskoin.Store.Data: [balanceTotalReceived] :: Balance -> !Word64
- Haskoin.Store.Data: [balanceTxCount] :: Balance -> !Word64
- Haskoin.Store.Data: [balanceUnspentCount] :: Balance -> !Word64
- Haskoin.Store.Data: [balanceZero] :: Balance -> !Word64
- Haskoin.Store.Data: [binfoBlocks] :: BinfoBlocks -> [BinfoBlockInfo]
- Haskoin.Store.Data: [binfoHeaderHash] :: BinfoHeader -> !BlockHash
- Haskoin.Store.Data: [binfoHeaderHeight] :: BinfoHeader -> !BlockHeight
- Haskoin.Store.Data: [binfoHeaderIndex] :: BinfoHeader -> !Word32
- Haskoin.Store.Data: [binfoHeaderTime] :: BinfoHeader -> !Timestamp
- Haskoin.Store.Data: [binfoHistoryAmount] :: BinfoHistory -> !Double
- Haskoin.Store.Data: [binfoHistoryDate] :: BinfoHistory -> !Text
- Haskoin.Store.Data: [binfoHistoryExchangeRateThen] :: BinfoHistory -> !Double
- Haskoin.Store.Data: [binfoHistoryFee] :: BinfoHistory -> !Double
- Haskoin.Store.Data: [binfoHistoryTime] :: BinfoHistory -> !Text
- Haskoin.Store.Data: [binfoHistoryTx] :: BinfoHistory -> !TxHash
- Haskoin.Store.Data: [binfoHistoryType] :: BinfoHistory -> !Text
- Haskoin.Store.Data: [binfoHistoryValueNow] :: BinfoHistory -> !Double
- Haskoin.Store.Data: [binfoHistoryValueThen] :: BinfoHistory -> !Double
- Haskoin.Store.Data: [binfoRatePrice] :: BinfoRate -> !Double
- Haskoin.Store.Data: [binfoRateTime] :: BinfoRate -> !Word64
- Haskoin.Store.Data: [binfoRateVol24] :: BinfoRate -> !Double
- Haskoin.Store.Data: [binfoRawAddr] :: BinfoRawAddr -> !BinfoAddr
- Haskoin.Store.Data: [binfoRawBalance] :: BinfoRawAddr -> !Word64
- Haskoin.Store.Data: [binfoRawReceived] :: BinfoRawAddr -> !Word64
- Haskoin.Store.Data: [binfoRawSent] :: BinfoRawAddr -> !Int64
- Haskoin.Store.Data: [binfoRawTxCount] :: BinfoRawAddr -> !Word64
- Haskoin.Store.Data: [binfoRawTxs] :: BinfoRawAddr -> ![BinfoTx]
- Haskoin.Store.Data: [binfoRawUnredeemed] :: BinfoRawAddr -> !Word64
- Haskoin.Store.Data: [binfoShortBalFinal] :: BinfoShortBal -> !Word64
- Haskoin.Store.Data: [binfoShortBalReceived] :: BinfoShortBal -> !Word64
- Haskoin.Store.Data: [binfoShortBalTxCount] :: BinfoShortBal -> !Word64
- Haskoin.Store.Data: [binfoTicker15m] :: BinfoTicker -> !Double
- Haskoin.Store.Data: [binfoTickerBuy] :: BinfoTicker -> !Double
- Haskoin.Store.Data: [binfoTickerLast] :: BinfoTicker -> !Double
- Haskoin.Store.Data: [binfoTickerSell] :: BinfoTicker -> !Double
- Haskoin.Store.Data: [binfoTickerSymbol] :: BinfoTicker -> !Text
- Haskoin.Store.Data: [binfoTxIndices] :: BinfoHeader -> ![BinfoTxId]
- Haskoin.Store.Data: [blockDataFees] :: BlockData -> !Word64
- Haskoin.Store.Data: [blockDataHeader] :: BlockData -> !BlockHeader
- Haskoin.Store.Data: [blockDataHeight] :: BlockData -> !BlockHeight
- Haskoin.Store.Data: [blockDataMainChain] :: BlockData -> !Bool
- Haskoin.Store.Data: [blockDataOutputs] :: BlockData -> !Word64
- Haskoin.Store.Data: [blockDataSize] :: BlockData -> !Word32
- Haskoin.Store.Data: [blockDataSubsidy] :: BlockData -> !Word64
- Haskoin.Store.Data: [blockDataTxs] :: BlockData -> ![TxHash]
- Haskoin.Store.Data: [blockDataWeight] :: BlockData -> !Word32
- Haskoin.Store.Data: [blockDataWork] :: BlockData -> !Integer
- Haskoin.Store.Data: [blockHealthBlocks] :: BlockHealth -> !BlockHeight
- Haskoin.Store.Data: [blockHealthHeaders] :: BlockHealth -> !BlockHeight
- Haskoin.Store.Data: [blockHealthMaxDiff] :: BlockHealth -> !Int32
- Haskoin.Store.Data: [blockRefHeight] :: BlockRef -> !BlockHeight
- Haskoin.Store.Data: [blockRefPos] :: BlockRef -> !Word32
- Haskoin.Store.Data: [countHealthMin] :: CountHealth -> !Int64
- Haskoin.Store.Data: [countHealthNum] :: CountHealth -> !Int64
- Haskoin.Store.Data: [getBinfoAddrBalance] :: BinfoBalance -> !Word64
- Haskoin.Store.Data: [getBinfoAddrReceived] :: BinfoBalance -> !Word64
- Haskoin.Store.Data: [getBinfoAddrSent] :: BinfoBalance -> !Word64
- Haskoin.Store.Data: [getBinfoAddrTxCount] :: BinfoBalance -> !Word64
- Haskoin.Store.Data: [getBinfoAddress] :: BinfoBalance -> !Address
- Haskoin.Store.Data: [getBinfoBTC] :: BinfoInfo -> !BinfoSymbol
- Haskoin.Store.Data: [getBinfoBlockBits] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockFee] :: BinfoBlock -> !Word64
- Haskoin.Store.Data: [getBinfoBlockHash] :: BinfoBlock -> !BlockHash
- Haskoin.Store.Data: [getBinfoBlockHeight] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockIndex] :: BinfoBlock -> !Word32
- 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: [getBinfoBlockMain] :: BinfoBlock -> !Bool
- Haskoin.Store.Data: [getBinfoBlockNonce] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockSize] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockTime] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockTxCount] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockTx] :: BinfoBlock -> ![BinfoTx]
- Haskoin.Store.Data: [getBinfoBlockVer] :: BinfoBlock -> !Word32
- Haskoin.Store.Data: [getBinfoBlockWeight] :: BinfoBlock -> !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: [getBinfoMempool] :: BinfoMempool -> [BinfoTx]
- Haskoin.Store.Data: [getBinfoMerkleRoot] :: BinfoBlock -> !Hash256
- Haskoin.Store.Data: [getBinfoMultiAddrAddresses] :: BinfoMultiAddr -> ![BinfoBalance]
- 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: [getBinfoNextBlock] :: BinfoBlock -> ![BlockHash]
- Haskoin.Store.Data: [getBinfoPrevBlock] :: BinfoBlock -> !BlockHash
- 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 -> !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: [getBinfoTxRBF] :: BinfoTx -> !Bool
- 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: [getBinfoUnspentConfirmations] :: BinfoUnspent -> !Int32
- Haskoin.Store.Data: [getBinfoUnspentHash] :: BinfoUnspent -> !TxHash
- Haskoin.Store.Data: [getBinfoUnspentOutputIndex] :: BinfoUnspent -> !Word32
- Haskoin.Store.Data: [getBinfoUnspentScript] :: BinfoUnspent -> !ByteString
- Haskoin.Store.Data: [getBinfoUnspentTxIndex] :: BinfoUnspent -> !BinfoTxId
- Haskoin.Store.Data: [getBinfoUnspentValue] :: BinfoUnspent -> !Word64
- Haskoin.Store.Data: [getBinfoUnspentXPub] :: BinfoUnspent -> !Maybe BinfoXPubPath
- 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] :: BinfoBalance -> !Word32
- Haskoin.Store.Data: [getBinfoXPubChangeIndex] :: BinfoBalance -> !Word32
- Haskoin.Store.Data: [getBinfoXPubKey] :: BinfoBalance -> !XPubKey
- Haskoin.Store.Data: [getBinfoXPubPathDeriv] :: BinfoXPubPath -> !SoftPath
- Haskoin.Store.Data: [getBinfoXPubPathKey] :: BinfoXPubPath -> !XPubKey
- Haskoin.Store.Data: [getRawResultList] :: RawResultList a -> [a]
- Haskoin.Store.Data: [getRawResult] :: RawResult a -> a
- Haskoin.Store.Data: [getResult] :: GenericResult a -> a
- Haskoin.Store.Data: [getSerialList] :: SerialList a -> [a]
- Haskoin.Store.Data: [healthBlocks] :: HealthCheck -> !BlockHealth
- Haskoin.Store.Data: [healthLastBlock] :: HealthCheck -> !TimeHealth
- Haskoin.Store.Data: [healthLastTx] :: HealthCheck -> !TimeHealth
- Haskoin.Store.Data: [healthNetwork] :: HealthCheck -> !String
- Haskoin.Store.Data: [healthPeers] :: HealthCheck -> !CountHealth
- Haskoin.Store.Data: [healthPendingTxs] :: HealthCheck -> !MaxHealth
- Haskoin.Store.Data: [healthVersion] :: HealthCheck -> !String
- Haskoin.Store.Data: [inputAddress] :: StoreInput -> !Maybe Address
- Haskoin.Store.Data: [inputAmount] :: StoreInput -> !Word64
- Haskoin.Store.Data: [inputPkScript] :: StoreInput -> !ByteString
- Haskoin.Store.Data: [inputPoint] :: StoreInput -> !OutPoint
- Haskoin.Store.Data: [inputSequence] :: StoreInput -> !Word32
- Haskoin.Store.Data: [inputSigScript] :: StoreInput -> !ByteString
- Haskoin.Store.Data: [inputWitness] :: StoreInput -> !WitnessStack
- Haskoin.Store.Data: [maxHealthMax] :: MaxHealth -> !Int64
- Haskoin.Store.Data: [maxHealthNum] :: MaxHealth -> !Int64
- Haskoin.Store.Data: [memRefTime] :: BlockRef -> !UnixTime
- Haskoin.Store.Data: [outputAddr] :: StoreOutput -> !Maybe Address
- Haskoin.Store.Data: [outputAmount] :: StoreOutput -> !Word64
- Haskoin.Store.Data: [outputScript] :: StoreOutput -> !ByteString
- Haskoin.Store.Data: [outputSpender] :: StoreOutput -> !Maybe Spender
- Haskoin.Store.Data: [peerAddress] :: PeerInformation -> !String
- Haskoin.Store.Data: [peerRelay] :: PeerInformation -> !Bool
- Haskoin.Store.Data: [peerServices] :: PeerInformation -> !Word64
- Haskoin.Store.Data: [peerUserAgent] :: PeerInformation -> !ByteString
- Haskoin.Store.Data: [peerVersion] :: PeerInformation -> !Word32
- Haskoin.Store.Data: [prevAmount] :: Prev -> !Word64
- Haskoin.Store.Data: [prevScript] :: Prev -> !ByteString
- Haskoin.Store.Data: [spenderHash] :: Spender -> !TxHash
- Haskoin.Store.Data: [spenderIndex] :: Spender -> !Word32
- Haskoin.Store.Data: [timeHealthAge] :: TimeHealth -> !Int64
- Haskoin.Store.Data: [timeHealthMax] :: TimeHealth -> !Int64
- Haskoin.Store.Data: [transactionBlock] :: Transaction -> !BlockRef
- Haskoin.Store.Data: [transactionDeleted] :: Transaction -> !Bool
- Haskoin.Store.Data: [transactionFees] :: Transaction -> !Word64
- Haskoin.Store.Data: [transactionId] :: Transaction -> !TxHash
- Haskoin.Store.Data: [transactionInputs] :: Transaction -> ![StoreInput]
- Haskoin.Store.Data: [transactionLockTime] :: Transaction -> !Word32
- Haskoin.Store.Data: [transactionOutputs] :: Transaction -> ![StoreOutput]
- Haskoin.Store.Data: [transactionRBF] :: Transaction -> !Bool
- Haskoin.Store.Data: [transactionSize] :: Transaction -> !Word32
- Haskoin.Store.Data: [transactionTime] :: Transaction -> !Word64
- Haskoin.Store.Data: [transactionVersion] :: Transaction -> !Word32
- Haskoin.Store.Data: [transactionWeight] :: Transaction -> !Word32
- Haskoin.Store.Data: [txDataBlock] :: TxData -> !BlockRef
- Haskoin.Store.Data: [txDataDeleted] :: TxData -> !Bool
- Haskoin.Store.Data: [txDataPrevs] :: TxData -> !IntMap Prev
- Haskoin.Store.Data: [txDataRBF] :: TxData -> !Bool
- Haskoin.Store.Data: [txDataSpenders] :: TxData -> !IntMap Spender
- Haskoin.Store.Data: [txDataTime] :: TxData -> !Word64
- Haskoin.Store.Data: [txData] :: TxData -> !Tx
- Haskoin.Store.Data: [txRefBlock] :: TxRef -> !BlockRef
- Haskoin.Store.Data: [txRefHash] :: TxRef -> !TxHash
- Haskoin.Store.Data: [unspentAddress] :: Unspent -> !Maybe Address
- Haskoin.Store.Data: [unspentAmount] :: Unspent -> !Word64
- Haskoin.Store.Data: [unspentBlock] :: Unspent -> !BlockRef
- Haskoin.Store.Data: [unspentPoint] :: Unspent -> !OutPoint
- Haskoin.Store.Data: [unspentScript] :: Unspent -> !ByteString
- Haskoin.Store.Data: [xPubBalPath] :: XPubBal -> ![KeyIndex]
- Haskoin.Store.Data: [xPubBal] :: XPubBal -> !Balance
- Haskoin.Store.Data: [xPubChangeIndex] :: XPubSummary -> !Word32
- Haskoin.Store.Data: [xPubDeriveType] :: XPubSpec -> !DeriveType
- Haskoin.Store.Data: [xPubExternalIndex] :: XPubSummary -> !Word32
- Haskoin.Store.Data: [xPubSpecKey] :: XPubSpec -> !XPubKey
- Haskoin.Store.Data: [xPubSummaryConfirmed] :: XPubSummary -> !Word64
- Haskoin.Store.Data: [xPubSummaryReceived] :: XPubSummary -> !Word64
- Haskoin.Store.Data: [xPubSummaryZero] :: XPubSummary -> !Word64
- Haskoin.Store.Data: [xPubUnspentCount] :: XPubSummary -> !Word64
- Haskoin.Store.Data: [xPubUnspentPath] :: XPubUnspent -> ![KeyIndex]
- Haskoin.Store.Data: [xPubUnspent] :: XPubUnspent -> !Unspent
- Haskoin.Store.Data: balanceParseJSON :: Network -> Value -> Parser Balance
- Haskoin.Store.Data: balanceToEncoding :: Network -> Balance -> Encoding
- Haskoin.Store.Data: balanceToJSON :: Network -> Balance -> Value
- Haskoin.Store.Data: binfoBalanceParseJSON :: Network -> Value -> Parser BinfoBalance
- Haskoin.Store.Data: binfoBalanceToEncoding :: Network -> BinfoBalance -> Encoding
- Haskoin.Store.Data: binfoBalanceToJSON :: Network -> BinfoBalance -> Value
- Haskoin.Store.Data: binfoBlockParseJSON :: Network -> Value -> Parser BinfoBlock
- Haskoin.Store.Data: binfoBlockToEncoding :: Network -> BinfoBlock -> Encoding
- Haskoin.Store.Data: binfoBlockToJSON :: Network -> BinfoBlock -> Value
- Haskoin.Store.Data: binfoBlocksParseJSON :: Network -> Value -> Parser [BinfoBlock]
- Haskoin.Store.Data: binfoBlocksToEncoding :: Network -> [BinfoBlock] -> Encoding
- Haskoin.Store.Data: binfoBlocksToJSON :: Network -> [BinfoBlock] -> Value
- Haskoin.Store.Data: binfoMempoolParseJSON :: Network -> Value -> Parser BinfoMempool
- Haskoin.Store.Data: binfoMempoolToEncoding :: Network -> BinfoMempool -> Encoding
- Haskoin.Store.Data: binfoMempoolToJSON :: Network -> BinfoMempool -> 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: binfoRawAddrParseJSON :: Network -> Value -> Parser BinfoRawAddr
- Haskoin.Store.Data: binfoRawAddrToEncoding :: Network -> BinfoRawAddr -> Encoding
- Haskoin.Store.Data: binfoRawAddrToJSON :: Network -> BinfoRawAddr -> Value
- 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: binfoUnspentParseJSON :: Network -> Value -> Parser BinfoUnspent
- Haskoin.Store.Data: binfoUnspentToEncoding :: Network -> BinfoUnspent -> Encoding
- Haskoin.Store.Data: binfoUnspentToJSON :: Network -> BinfoUnspent -> Value
- Haskoin.Store.Data: binfoUnspentsParseJSON :: Network -> Value -> Parser BinfoUnspents
- Haskoin.Store.Data: binfoUnspentsToEncoding :: Network -> BinfoUnspents -> Encoding
- Haskoin.Store.Data: binfoUnspentsToJSON :: Network -> BinfoUnspents -> 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: blockDataToEncoding :: Network -> BlockData -> Encoding
- Haskoin.Store.Data: blockDataToJSON :: Network -> BlockData -> Value
- Haskoin.Store.Data: data PeerInformation
- Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoBlocks
- Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoBlocks
- Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoBlocks
- Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance Data.Binary.Class.Binary Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance Data.Bytes.Serial.Serial Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoBlocks
- Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance GHC.Classes.Ord Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoBlocks
- Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoBlocks
- Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.PeerInformation
- Haskoin.Store.Data: newtype BinfoBlocks
- Haskoin.Store.Data: storeInputParseJSON :: Network -> Value -> Parser StoreInput
- Haskoin.Store.Data: storeInputToEncoding :: Network -> StoreInput -> Encoding
- Haskoin.Store.Data: storeInputToJSON :: Network -> StoreInput -> Value
- Haskoin.Store.Data: storeOutputParseJSON :: Network -> Value -> Parser StoreOutput
- Haskoin.Store.Data: storeOutputToEncoding :: Network -> StoreOutput -> Encoding
- Haskoin.Store.Data: storeOutputToJSON :: Network -> StoreOutput -> Value
- Haskoin.Store.Data: transactionParseJSON :: Network -> Value -> Parser Transaction
- Haskoin.Store.Data: transactionToEncoding :: Network -> Transaction -> Encoding
- Haskoin.Store.Data: transactionToJSON :: Network -> Transaction -> Value
- Haskoin.Store.Data: unspentParseJSON :: Network -> Value -> Parser Unspent
- Haskoin.Store.Data: unspentToEncoding :: Network -> Unspent -> Encoding
- Haskoin.Store.Data: unspentToJSON :: Network -> Unspent -> Value
- Haskoin.Store.Data: xPubBalParseJSON :: Network -> Value -> Parser XPubBal
- Haskoin.Store.Data: xPubBalToEncoding :: Network -> XPubBal -> Encoding
- Haskoin.Store.Data: xPubBalToJSON :: Network -> XPubBal -> Value
- Haskoin.Store.Data: xPubUnspentParseJSON :: Network -> Value -> Parser XPubUnspent
- Haskoin.Store.Data: xPubUnspentToEncoding :: Network -> XPubUnspent -> Encoding
- Haskoin.Store.Data: xPubUnspentToJSON :: Network -> XPubUnspent -> Value
- Haskoin.Store.WebClient: [configHost] :: ApiConfig -> !String
- Haskoin.Store.WebClient: [configNetwork] :: ApiConfig -> !Network
- Haskoin.Store.WebClient: [getHeightParam] :: HeightParam -> Natural
- Haskoin.Store.WebClient: [getHeightsParam] :: HeightsParam -> [Natural]
- Haskoin.Store.WebClient: [getLimitParam] :: LimitParam -> Natural
- Haskoin.Store.WebClient: [getNoCache] :: NoCache -> Bool
- Haskoin.Store.WebClient: [getNoTx] :: NoTx -> Bool
- Haskoin.Store.WebClient: [getOffsetParam] :: OffsetParam -> Natural
- Haskoin.Store.WebClient: [paramLimit] :: LimitsParam -> Maybe LimitParam
- Haskoin.Store.WebClient: [paramOffset] :: LimitsParam -> OffsetParam
- Haskoin.Store.WebClient: [paramStart] :: LimitsParam -> Maybe StartParam
- Haskoin.Store.WebClient: [startParamHash] :: StartParam -> Hash256
- Haskoin.Store.WebClient: [startParamHeight] :: StartParam -> Natural
- Haskoin.Store.WebClient: [startParamTime] :: StartParam -> UnixTime
- Haskoin.Store.WebCommon: [getHeightParam] :: HeightParam -> Natural
- Haskoin.Store.WebCommon: [getHeightsParam] :: HeightsParam -> [Natural]
- Haskoin.Store.WebCommon: [getLimitParam] :: LimitParam -> Natural
- Haskoin.Store.WebCommon: [getNoCache] :: NoCache -> Bool
- Haskoin.Store.WebCommon: [getNoTx] :: NoTx -> Bool
- Haskoin.Store.WebCommon: [getOffsetParam] :: OffsetParam -> Natural
- Haskoin.Store.WebCommon: [getTimeParam] :: TimeParam -> UnixTime
- Haskoin.Store.WebCommon: [paramLimit] :: LimitsParam -> Maybe LimitParam
- Haskoin.Store.WebCommon: [paramOffset] :: LimitsParam -> OffsetParam
- Haskoin.Store.WebCommon: [paramStart] :: LimitsParam -> Maybe StartParam
- Haskoin.Store.WebCommon: [startParamHash] :: StartParam -> Hash256
- Haskoin.Store.WebCommon: [startParamHeight] :: StartParam -> Natural
- Haskoin.Store.WebCommon: [startParamTime] :: StartParam -> UnixTime
- Haskoin.Store.WebCommon: instance Haskoin.Store.WebCommon.ApiResource Haskoin.Store.WebCommon.GetPeers (Haskoin.Store.Data.SerialList Haskoin.Store.Data.PeerInformation)
- Haskoin.Store.WebCommon: instance Haskoin.Store.WebCommon.Param Haskoin.Keys.Extended.XPubKey
+ Haskoin.Store.Data: BinfoBlockInfos :: [BinfoBlockInfo] -> BinfoBlockInfos
+ Haskoin.Store.Data: PeerInfo :: !ByteString -> !String -> !Word32 -> !Word64 -> !Bool -> PeerInfo
+ Haskoin.Store.Data: [$sel:address:Balance] :: Balance -> !Address
+ Haskoin.Store.Data: [$sel:address:BinfoAddrBalance] :: BinfoBalance -> !Address
+ Haskoin.Store.Data: [$sel:address:BinfoRawAddr] :: BinfoRawAddr -> !BinfoAddr
+ Haskoin.Store.Data: [$sel:address:BinfoTxOutput] :: BinfoTxOutput -> !Maybe Address
+ Haskoin.Store.Data: [$sel:address:PeerInfo] :: PeerInfo -> !String
+ Haskoin.Store.Data: [$sel:address:StoreCoinbase] :: StoreInput -> !Maybe Address
+ Haskoin.Store.Data: [$sel:address:StoreOutput] :: StoreOutput -> !Maybe Address
+ Haskoin.Store.Data: [$sel:address:Unspent] :: Unspent -> !Maybe Address
+ Haskoin.Store.Data: [$sel:addresses:BinfoMultiAddr] :: BinfoMultiAddr -> ![BinfoBalance]
+ Haskoin.Store.Data: [$sel:after:BinfoSymbol] :: BinfoSymbol -> !Bool
+ Haskoin.Store.Data: [$sel:age:TimeHealth] :: TimeHealth -> !Int64
+ Haskoin.Store.Data: [$sel:amount:BinfoHistory] :: BinfoHistory -> !Double
+ Haskoin.Store.Data: [$sel:balance:BinfoAddrBalance] :: BinfoBalance -> !Word64
+ Haskoin.Store.Data: [$sel:balance:BinfoRawAddr] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [$sel:balance:BinfoTx] :: BinfoTx -> !Maybe (Int64, Int64)
+ Haskoin.Store.Data: [$sel:balance:BinfoWallet] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [$sel:balance:XPubBal] :: XPubBal -> !Balance
+ Haskoin.Store.Data: [$sel:bits:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:block:Transaction] :: Transaction -> !BlockRef
+ Haskoin.Store.Data: [$sel:block:TxData] :: TxData -> !BlockRef
+ Haskoin.Store.Data: [$sel:block:TxRef] :: TxRef -> !BlockRef
+ Haskoin.Store.Data: [$sel:block:Unspent] :: Unspent -> !BlockRef
+ Haskoin.Store.Data: [$sel:blockHeight:BinfoTx] :: BinfoTx -> !Maybe Word32
+ Haskoin.Store.Data: [$sel:blockIndex:BinfoTx] :: BinfoTx -> !Maybe Word32
+ Haskoin.Store.Data: [$sel:blocks:BlockHealth] :: BlockHealth -> !BlockHeight
+ Haskoin.Store.Data: [$sel:blocks:HealthCheck] :: HealthCheck -> !BlockHealth
+ Haskoin.Store.Data: [$sel:buy:BinfoTicker] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [$sel:cashAddr:BinfoMultiAddr] :: BinfoMultiAddr -> !Bool
+ Haskoin.Store.Data: [$sel:change:BinfoAddrBalance] :: BinfoBalance -> !Word32
+ Haskoin.Store.Data: [$sel:change:XPubSummary] :: XPubSummary -> !Word32
+ Haskoin.Store.Data: [$sel:code:BinfoSymbol] :: BinfoSymbol -> !Text
+ Haskoin.Store.Data: [$sel:confirmations:BinfoUnspent] :: BinfoUnspent -> !Int32
+ Haskoin.Store.Data: [$sel:confirmed:Balance] :: Balance -> !Word64
+ Haskoin.Store.Data: [$sel:confirmed:XPubSummary] :: XPubSummary -> !Word64
+ Haskoin.Store.Data: [$sel:connected:BinfoInfo] :: BinfoInfo -> !Word32
+ Haskoin.Store.Data: [$sel:conversion:BinfoInfo] :: BinfoInfo -> !Double
+ Haskoin.Store.Data: [$sel:conversion:BinfoSymbol] :: BinfoSymbol -> !Double
+ Haskoin.Store.Data: [$sel:count:CountHealth] :: CountHealth -> !Int64
+ Haskoin.Store.Data: [$sel:count:MaxHealth] :: MaxHealth -> !Int64
+ Haskoin.Store.Data: [$sel:crypto:BinfoInfo] :: BinfoInfo -> !BinfoSymbol
+ Haskoin.Store.Data: [$sel:date:BinfoHistory] :: BinfoHistory -> !Text
+ Haskoin.Store.Data: [$sel:deleted:Transaction] :: Transaction -> !Bool
+ Haskoin.Store.Data: [$sel:deleted:TxData] :: TxData -> !Bool
+ Haskoin.Store.Data: [$sel:deriv:BinfoXPubPath] :: BinfoXPubPath -> !SoftPath
+ Haskoin.Store.Data: [$sel:deriv:XPubSpec] :: XPubSpec -> !DeriveType
+ Haskoin.Store.Data: [$sel:doubleSpend:BinfoTx] :: BinfoTx -> !Bool
+ Haskoin.Store.Data: [$sel:external:BinfoAddrBalance] :: BinfoBalance -> !Word32
+ Haskoin.Store.Data: [$sel:external:XPubSummary] :: XPubSummary -> !Word32
+ Haskoin.Store.Data: [$sel:fee:BinfoBlock] :: BinfoBlock -> !Word64
+ Haskoin.Store.Data: [$sel:fee:BinfoHistory] :: BinfoHistory -> !Double
+ Haskoin.Store.Data: [$sel:fee:BinfoTx] :: BinfoTx -> !Word64
+ Haskoin.Store.Data: [$sel:fee:BlockData] :: BlockData -> !Word64
+ Haskoin.Store.Data: [$sel:fee:Transaction] :: Transaction -> !Word64
+ Haskoin.Store.Data: [$sel:fiat:BinfoInfo] :: BinfoInfo -> !BinfoSymbol
+ Haskoin.Store.Data: [$sel:fifteen:BinfoTicker] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [$sel:filtered:BinfoWallet] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [$sel:final:BinfoShortBal] :: BinfoShortBal -> !Word64
+ Haskoin.Store.Data: [$sel:get:BinfoBlockInfos] :: BinfoBlockInfos -> [BinfoBlockInfo]
+ Haskoin.Store.Data: [$sel:get:BinfoMempool] :: BinfoMempool -> [BinfoTx]
+ Haskoin.Store.Data: [$sel:get:GenericResult] :: GenericResult a -> a
+ Haskoin.Store.Data: [$sel:get:RawResultList] :: RawResultList a -> [a]
+ Haskoin.Store.Data: [$sel:get:RawResult] :: RawResult a -> a
+ Haskoin.Store.Data: [$sel:get:SerialList] :: SerialList a -> [a]
+ Haskoin.Store.Data: [$sel:hash:BinfoBlockInfo] :: BinfoBlockInfo -> !BlockHash
+ Haskoin.Store.Data: [$sel:hash:BinfoBlock] :: BinfoBlock -> !BlockHash
+ Haskoin.Store.Data: [$sel:hash:BinfoHeader] :: BinfoHeader -> !BlockHash
+ Haskoin.Store.Data: [$sel:head:BinfoInfo] :: BinfoInfo -> !BinfoBlockInfo
+ Haskoin.Store.Data: [$sel:header:BlockData] :: BlockData -> !BlockHeader
+ Haskoin.Store.Data: [$sel:headers:BlockHealth] :: BlockHealth -> !BlockHeight
+ Haskoin.Store.Data: [$sel:height:BinfoBlockInfo] :: BinfoBlockInfo -> !BlockHeight
+ Haskoin.Store.Data: [$sel:height:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:height:BinfoHeader] :: BinfoHeader -> !BlockHeight
+ Haskoin.Store.Data: [$sel:height:BlockData] :: BlockData -> !BlockHeight
+ Haskoin.Store.Data: [$sel:height:BlockRef] :: BlockRef -> !BlockHeight
+ Haskoin.Store.Data: [$sel:index:BinfoBlockInfo] :: BinfoBlockInfo -> !BlockHeight
+ Haskoin.Store.Data: [$sel:index:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:index:BinfoHeader] :: BinfoHeader -> !Word32
+ Haskoin.Store.Data: [$sel:index:BinfoTxInput] :: BinfoTxInput -> !Word32
+ Haskoin.Store.Data: [$sel:index:BinfoTxOutput] :: BinfoTxOutput -> !Word32
+ Haskoin.Store.Data: [$sel:index:BinfoTx] :: BinfoTx -> !BinfoTxId
+ Haskoin.Store.Data: [$sel:index:BinfoUnspent] :: BinfoUnspent -> !Word32
+ Haskoin.Store.Data: [$sel:index:Spender] :: Spender -> !Word32
+ Haskoin.Store.Data: [$sel:info:BinfoMultiAddr] :: BinfoMultiAddr -> !BinfoInfo
+ Haskoin.Store.Data: [$sel:input:BinfoSpender] :: BinfoSpender -> !Word32
+ Haskoin.Store.Data: [$sel:inputCount:BinfoTx] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [$sel:inputs:BinfoTx] :: BinfoTx -> ![BinfoTxInput]
+ Haskoin.Store.Data: [$sel:inputs:Transaction] :: Transaction -> ![StoreInput]
+ Haskoin.Store.Data: [$sel:key:BinfoXPubPath] :: BinfoXPubPath -> !XPubKey
+ Haskoin.Store.Data: [$sel:key:XPubSpec] :: XPubSpec -> !XPubKey
+ Haskoin.Store.Data: [$sel:last:BinfoTicker] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [$sel:lastBlock:HealthCheck] :: HealthCheck -> !TimeHealth
+ Haskoin.Store.Data: [$sel:lastTx:HealthCheck] :: HealthCheck -> !TimeHealth
+ Haskoin.Store.Data: [$sel:local:BinfoSymbol] :: BinfoSymbol -> !Bool
+ Haskoin.Store.Data: [$sel:locktime:BinfoTx] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [$sel:locktime:Transaction] :: Transaction -> !Word32
+ Haskoin.Store.Data: [$sel:main:BinfoBlock] :: BinfoBlock -> !Bool
+ Haskoin.Store.Data: [$sel:main:BlockData] :: BlockData -> !Bool
+ Haskoin.Store.Data: [$sel:max:BlockHealth] :: BlockHealth -> !Int32
+ Haskoin.Store.Data: [$sel:max:MaxHealth] :: MaxHealth -> !Int64
+ Haskoin.Store.Data: [$sel:max:TimeHealth] :: TimeHealth -> !Int64
+ Haskoin.Store.Data: [$sel:merkle:BinfoBlock] :: BinfoBlock -> !Hash256
+ Haskoin.Store.Data: [$sel:min:CountHealth] :: CountHealth -> !Int64
+ Haskoin.Store.Data: [$sel:name:BinfoSymbol] :: BinfoSymbol -> !Text
+ Haskoin.Store.Data: [$sel:network:HealthCheck] :: HealthCheck -> !String
+ Haskoin.Store.Data: [$sel:next:BinfoBlock] :: BinfoBlock -> ![BlockHash]
+ Haskoin.Store.Data: [$sel:nonce:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:ntx:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:ntx:BinfoRawAddr] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [$sel:ntx:BinfoShortBal] :: BinfoShortBal -> !Word64
+ Haskoin.Store.Data: [$sel:outpoint:StoreCoinbase] :: StoreInput -> !OutPoint
+ Haskoin.Store.Data: [$sel:outpoint:Unspent] :: Unspent -> !OutPoint
+ Haskoin.Store.Data: [$sel:output:BinfoTxInput] :: BinfoTxInput -> !BinfoTxOutput
+ Haskoin.Store.Data: [$sel:outputCount:BinfoTx] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [$sel:outputs:BinfoTx] :: BinfoTx -> ![BinfoTxOutput]
+ Haskoin.Store.Data: [$sel:outputs:BlockData] :: BlockData -> !Word64
+ Haskoin.Store.Data: [$sel:outputs:Transaction] :: Transaction -> ![StoreOutput]
+ Haskoin.Store.Data: [$sel:path:XPubBal] :: XPubBal -> ![KeyIndex]
+ Haskoin.Store.Data: [$sel:path:XPubUnspent] :: XPubUnspent -> ![KeyIndex]
+ Haskoin.Store.Data: [$sel:peers:HealthCheck] :: HealthCheck -> !CountHealth
+ Haskoin.Store.Data: [$sel:pendingTxs:HealthCheck] :: HealthCheck -> !MaxHealth
+ Haskoin.Store.Data: [$sel:pkscript:StoreCoinbase] :: StoreInput -> !ByteString
+ Haskoin.Store.Data: [$sel:position:BlockRef] :: BlockRef -> !Word32
+ Haskoin.Store.Data: [$sel:prev:BinfoBlock] :: BinfoBlock -> !BlockHash
+ Haskoin.Store.Data: [$sel:prevs:TxData] :: TxData -> !IntMap Prev
+ Haskoin.Store.Data: [$sel:price:BinfoRate] :: BinfoRate -> !Double
+ Haskoin.Store.Data: [$sel:rateThen:BinfoHistory] :: BinfoHistory -> !Double
+ Haskoin.Store.Data: [$sel:rbf:BinfoTx] :: BinfoTx -> !Bool
+ Haskoin.Store.Data: [$sel:rbf:Transaction] :: Transaction -> !Bool
+ Haskoin.Store.Data: [$sel:rbf:TxData] :: TxData -> !Bool
+ Haskoin.Store.Data: [$sel:received:Balance] :: Balance -> !Word64
+ Haskoin.Store.Data: [$sel:received:BinfoAddrBalance] :: BinfoBalance -> !Word64
+ Haskoin.Store.Data: [$sel:received:BinfoRawAddr] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [$sel:received:BinfoShortBal] :: BinfoShortBal -> !Word64
+ Haskoin.Store.Data: [$sel:received:BinfoWallet] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [$sel:received:XPubSummary] :: XPubSummary -> !Word64
+ Haskoin.Store.Data: [$sel:recommendFee:BinfoMultiAddr] :: BinfoMultiAddr -> !Bool
+ Haskoin.Store.Data: [$sel:relay:PeerInfo] :: PeerInfo -> !Bool
+ Haskoin.Store.Data: [$sel:relayed:BinfoTx] :: BinfoTx -> !ByteString
+ Haskoin.Store.Data: [$sel:script:BinfoTxInput] :: BinfoTxInput -> !ByteString
+ Haskoin.Store.Data: [$sel:script:BinfoTxOutput] :: BinfoTxOutput -> !ByteString
+ Haskoin.Store.Data: [$sel:script:BinfoUnspent] :: BinfoUnspent -> !ByteString
+ Haskoin.Store.Data: [$sel:script:Prev] :: Prev -> !ByteString
+ Haskoin.Store.Data: [$sel:script:StoreCoinbase] :: StoreInput -> !ByteString
+ Haskoin.Store.Data: [$sel:script:StoreOutput] :: StoreOutput -> !ByteString
+ Haskoin.Store.Data: [$sel:script:Unspent] :: Unspent -> !ByteString
+ Haskoin.Store.Data: [$sel:sell:BinfoTicker] :: BinfoTicker -> !Double
+ Haskoin.Store.Data: [$sel:sent:BinfoAddrBalance] :: BinfoBalance -> !Word64
+ Haskoin.Store.Data: [$sel:sent:BinfoRawAddr] :: BinfoRawAddr -> !Int64
+ Haskoin.Store.Data: [$sel:sent:BinfoWallet] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [$sel:sequence:BinfoTxInput] :: BinfoTxInput -> !Word32
+ Haskoin.Store.Data: [$sel:sequence:StoreCoinbase] :: StoreInput -> !Word32
+ Haskoin.Store.Data: [$sel:services:PeerInfo] :: PeerInfo -> !Word64
+ Haskoin.Store.Data: [$sel:size:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:size:BinfoTx] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [$sel:size:BlockData] :: BlockData -> !Word32
+ Haskoin.Store.Data: [$sel:size:Transaction] :: Transaction -> !Word32
+ Haskoin.Store.Data: [$sel:spender:StoreOutput] :: StoreOutput -> !Maybe Spender
+ Haskoin.Store.Data: [$sel:spenders:BinfoTxOutput] :: BinfoTxOutput -> ![BinfoSpender]
+ Haskoin.Store.Data: [$sel:spenders:TxData] :: TxData -> !IntMap Spender
+ Haskoin.Store.Data: [$sel:spent:BinfoTxOutput] :: BinfoTxOutput -> !Bool
+ Haskoin.Store.Data: [$sel:subsidy:BlockData] :: BlockData -> !Word64
+ Haskoin.Store.Data: [$sel:symbol:BinfoSymbol] :: BinfoSymbol -> !Text
+ Haskoin.Store.Data: [$sel:symbol:BinfoTicker] :: BinfoTicker -> !Text
+ Haskoin.Store.Data: [$sel:time:BinfoHistory] :: BinfoHistory -> !Text
+ Haskoin.Store.Data: [$sel:time:HealthCheck] :: HealthCheck -> !Word64
+ Haskoin.Store.Data: [$sel:timestamp:BinfoBlockInfo] :: BinfoBlockInfo -> !Word32
+ Haskoin.Store.Data: [$sel:timestamp:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:timestamp:BinfoHeader] :: BinfoHeader -> !Timestamp
+ Haskoin.Store.Data: [$sel:timestamp:BinfoRate] :: BinfoRate -> !Word64
+ Haskoin.Store.Data: [$sel:timestamp:BinfoTx] :: BinfoTx -> !Word64
+ Haskoin.Store.Data: [$sel:timestamp:BlockRef] :: BlockRef -> !UnixTime
+ Haskoin.Store.Data: [$sel:timestamp:Transaction] :: Transaction -> !Word64
+ Haskoin.Store.Data: [$sel:timestamp:TxData] :: TxData -> !Word64
+ Haskoin.Store.Data: [$sel:tx:TxData] :: TxData -> !Tx
+ Haskoin.Store.Data: [$sel:txid:BinfoHistory] :: BinfoHistory -> !TxHash
+ Haskoin.Store.Data: [$sel:txid:BinfoTx] :: BinfoTx -> !TxHash
+ Haskoin.Store.Data: [$sel:txid:BinfoUnspent] :: BinfoUnspent -> !TxHash
+ Haskoin.Store.Data: [$sel:txid:Spender] :: Spender -> !TxHash
+ Haskoin.Store.Data: [$sel:txid:Transaction] :: Transaction -> !TxHash
+ Haskoin.Store.Data: [$sel:txid:TxRef] :: TxRef -> !TxHash
+ Haskoin.Store.Data: [$sel:txids:BinfoHeader] :: BinfoHeader -> ![BinfoTxId]
+ Haskoin.Store.Data: [$sel:txidx:BinfoSpender] :: BinfoSpender -> !BinfoTxId
+ Haskoin.Store.Data: [$sel:txidx:BinfoTxOutput] :: BinfoTxOutput -> !BinfoTxId
+ Haskoin.Store.Data: [$sel:txidx:BinfoUnspent] :: BinfoUnspent -> !BinfoTxId
+ Haskoin.Store.Data: [$sel:txs:Balance] :: Balance -> !Word64
+ Haskoin.Store.Data: [$sel:txs:BinfoAddrBalance] :: BinfoBalance -> !Word64
+ Haskoin.Store.Data: [$sel:txs:BinfoBlock] :: BinfoBlock -> ![BinfoTx]
+ Haskoin.Store.Data: [$sel:txs:BinfoMultiAddr] :: BinfoMultiAddr -> ![BinfoTx]
+ Haskoin.Store.Data: [$sel:txs:BinfoRawAddr] :: BinfoRawAddr -> ![BinfoTx]
+ Haskoin.Store.Data: [$sel:txs:BinfoWallet] :: BinfoWallet -> !Word64
+ Haskoin.Store.Data: [$sel:txs:BlockData] :: BlockData -> ![TxHash]
+ Haskoin.Store.Data: [$sel:typ:BinfoHistory] :: BinfoHistory -> !Text
+ Haskoin.Store.Data: [$sel:typ:BinfoTxOutput] :: BinfoTxOutput -> !Int
+ Haskoin.Store.Data: [$sel:unconfirmed:Balance] :: Balance -> !Word64
+ Haskoin.Store.Data: [$sel:unconfirmed:XPubSummary] :: XPubSummary -> !Word64
+ Haskoin.Store.Data: [$sel:unspent:XPubUnspent] :: XPubUnspent -> !Unspent
+ Haskoin.Store.Data: [$sel:userAgent:PeerInfo] :: PeerInfo -> !ByteString
+ Haskoin.Store.Data: [$sel:utxo:Balance] :: Balance -> !Word64
+ Haskoin.Store.Data: [$sel:utxo:BinfoRawAddr] :: BinfoRawAddr -> !Word64
+ Haskoin.Store.Data: [$sel:utxo:XPubSummary] :: XPubSummary -> !Word64
+ Haskoin.Store.Data: [$sel:value:BinfoTxOutput] :: BinfoTxOutput -> !Word64
+ Haskoin.Store.Data: [$sel:value:BinfoUnspent] :: BinfoUnspent -> !Word64
+ Haskoin.Store.Data: [$sel:value:Prev] :: Prev -> !Word64
+ Haskoin.Store.Data: [$sel:value:StoreCoinbase] :: StoreInput -> !Word64
+ Haskoin.Store.Data: [$sel:value:StoreOutput] :: StoreOutput -> !Word64
+ Haskoin.Store.Data: [$sel:value:Unspent] :: Unspent -> !Word64
+ Haskoin.Store.Data: [$sel:valueNow:BinfoHistory] :: BinfoHistory -> !Double
+ Haskoin.Store.Data: [$sel:valueThen:BinfoHistory] :: BinfoHistory -> !Double
+ Haskoin.Store.Data: [$sel:version:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:version:BinfoTx] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [$sel:version:HealthCheck] :: HealthCheck -> !String
+ Haskoin.Store.Data: [$sel:version:PeerInfo] :: PeerInfo -> !Word32
+ Haskoin.Store.Data: [$sel:version:Transaction] :: Transaction -> !Word32
+ Haskoin.Store.Data: [$sel:vol24:BinfoRate] :: BinfoRate -> !Double
+ Haskoin.Store.Data: [$sel:wallet:BinfoMultiAddr] :: BinfoMultiAddr -> !BinfoWallet
+ Haskoin.Store.Data: [$sel:weight:BinfoBlock] :: BinfoBlock -> !Word32
+ Haskoin.Store.Data: [$sel:weight:BinfoTx] :: BinfoTx -> !Word32
+ Haskoin.Store.Data: [$sel:weight:BlockData] :: BlockData -> !Word32
+ Haskoin.Store.Data: [$sel:weight:Transaction] :: Transaction -> !Word32
+ Haskoin.Store.Data: [$sel:witness:BinfoTxInput] :: BinfoTxInput -> !ByteString
+ Haskoin.Store.Data: [$sel:witness:StoreCoinbase] :: StoreInput -> !WitnessStack
+ Haskoin.Store.Data: [$sel:work:BlockData] :: BlockData -> !Integer
+ Haskoin.Store.Data: [$sel:xpub:BinfoAddrBalance] :: BinfoBalance -> !XPubKey
+ Haskoin.Store.Data: [$sel:xpub:BinfoTxOutput] :: BinfoTxOutput -> !Maybe BinfoXPubPath
+ Haskoin.Store.Data: [$sel:xpub:BinfoUnspent] :: BinfoUnspent -> !Maybe BinfoXPubPath
+ Haskoin.Store.Data: data PeerInfo
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.BinfoBlockInfos
+ Haskoin.Store.Data: instance Control.DeepSeq.NFData Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.BinfoBlockInfos
+ Haskoin.Store.Data: instance Data.Aeson.Types.FromJSON.FromJSON Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.BinfoBlockInfos
+ Haskoin.Store.Data: instance Data.Aeson.Types.ToJSON.ToJSON Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance Data.Binary.Class.Binary Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance Data.Bytes.Serial.Serial Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance Data.Serialize.Serialize Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.BinfoBlockInfos
+ Haskoin.Store.Data: instance GHC.Classes.Eq Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance GHC.Classes.Ord Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.BinfoBlockInfos
+ Haskoin.Store.Data: instance GHC.Generics.Generic Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.BinfoBlockInfos
+ Haskoin.Store.Data: instance GHC.Show.Show Haskoin.Store.Data.PeerInfo
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoBalance
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoBlock
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoMempool
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoMultiAddr
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoRawAddr
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoTx
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoTxInput
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoTxOutput
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoUnspent
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoUnspents
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) Haskoin.Store.Data.BinfoXPubPath
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) [Haskoin.Store.Data.BinfoBalance]
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) [Haskoin.Store.Data.BinfoBlock]
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON (Haskoin.Network.Data.Network, Crypto.Secp256k1.Ctx) [Haskoin.Store.Data.BinfoTx]
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.Balance
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.BlockData
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.StoreInput
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.StoreOutput
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.Transaction
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.Unspent
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.XPubBal
+ Haskoin.Store.Data: instance Haskoin.Util.Marshal.MarshalJSON Haskoin.Network.Data.Network Haskoin.Store.Data.XPubUnspent
+ Haskoin.Store.Data: newtype BinfoBlockInfos
+ Haskoin.Store.WebClient: [$sel:get:HeightParam] :: HeightParam -> Natural
+ Haskoin.Store.WebClient: [$sel:get:HeightsParam] :: HeightsParam -> [Natural]
+ Haskoin.Store.WebClient: [$sel:get:LimitParam] :: LimitParam -> Natural
+ Haskoin.Store.WebClient: [$sel:get:NoCache] :: NoCache -> Bool
+ Haskoin.Store.WebClient: [$sel:get:NoTx] :: NoTx -> Bool
+ Haskoin.Store.WebClient: [$sel:get:OffsetParam] :: OffsetParam -> Natural
+ Haskoin.Store.WebClient: [$sel:hash:StartParamHash] :: StartParam -> Hash256
+ Haskoin.Store.WebClient: [$sel:height:StartParamHash] :: StartParam -> Natural
+ Haskoin.Store.WebClient: [$sel:host:ApiConfig] :: ApiConfig -> !String
+ Haskoin.Store.WebClient: [$sel:limit:LimitsParam] :: LimitsParam -> Maybe LimitParam
+ Haskoin.Store.WebClient: [$sel:net:ApiConfig] :: ApiConfig -> !Network
+ Haskoin.Store.WebClient: [$sel:offset:LimitsParam] :: LimitsParam -> OffsetParam
+ Haskoin.Store.WebClient: [$sel:start:LimitsParam] :: LimitsParam -> Maybe StartParam
+ Haskoin.Store.WebClient: [$sel:time:StartParamHash] :: StartParam -> UnixTime
+ Haskoin.Store.WebCommon: [$sel:get:HeightParam] :: HeightParam -> Natural
+ Haskoin.Store.WebCommon: [$sel:get:HeightsParam] :: HeightsParam -> [Natural]
+ Haskoin.Store.WebCommon: [$sel:get:LimitParam] :: LimitParam -> Natural
+ Haskoin.Store.WebCommon: [$sel:get:NoCache] :: NoCache -> Bool
+ Haskoin.Store.WebCommon: [$sel:get:NoTx] :: NoTx -> Bool
+ Haskoin.Store.WebCommon: [$sel:get:OffsetParam] :: OffsetParam -> Natural
+ Haskoin.Store.WebCommon: [$sel:get:TimeParam] :: TimeParam -> UnixTime
+ Haskoin.Store.WebCommon: [$sel:hash:StartParamHash] :: StartParam -> Hash256
+ Haskoin.Store.WebCommon: [$sel:height:StartParamHash] :: StartParam -> Natural
+ Haskoin.Store.WebCommon: [$sel:limit:LimitsParam] :: LimitsParam -> Maybe LimitParam
+ Haskoin.Store.WebCommon: [$sel:offset:LimitsParam] :: LimitsParam -> OffsetParam
+ Haskoin.Store.WebCommon: [$sel:start:LimitsParam] :: LimitsParam -> Maybe StartParam
+ Haskoin.Store.WebCommon: [$sel:time:StartParamHash] :: StartParam -> UnixTime
+ Haskoin.Store.WebCommon: instance Haskoin.Store.WebCommon.ApiResource Haskoin.Store.WebCommon.GetPeers (Haskoin.Store.Data.SerialList Haskoin.Store.Data.PeerInfo)
+ Haskoin.Store.WebCommon: instance Haskoin.Store.WebCommon.Param Haskoin.Crypto.Keys.Extended.XPubKey
- Haskoin.Store.Data: BinfoTx :: !TxHash -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word64 -> !ByteString -> !Word32 -> !BinfoTxId -> !Bool -> !Bool -> !Maybe (Int64, Int64) -> !Word64 -> !Maybe Word32 -> !Maybe Word32 -> [BinfoTxInput] -> [BinfoTxOutput] -> BinfoTx
+ Haskoin.Store.Data: BinfoTx :: !TxHash -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word32 -> !Word64 -> !ByteString -> !Word32 -> !BinfoTxId -> !Bool -> !Bool -> !Maybe (Int64, Int64) -> !Word64 -> !Maybe Word32 -> !Maybe Word32 -> ![BinfoTxInput] -> ![BinfoTxOutput] -> BinfoTx
- Haskoin.Store.Data: HealthCheck :: !BlockHealth -> !TimeHealth -> !TimeHealth -> !MaxHealth -> !CountHealth -> !String -> !String -> HealthCheck
+ Haskoin.Store.Data: HealthCheck :: !BlockHealth -> !TimeHealth -> !TimeHealth -> !MaxHealth -> !CountHealth -> !String -> !String -> !Word64 -> HealthCheck
- Haskoin.Store.Data: parseBinfoAddr :: Network -> Text -> Maybe [BinfoAddr]
+ Haskoin.Store.Data: parseBinfoAddr :: Network -> Ctx -> Text -> Maybe [BinfoAddr]
- Haskoin.Store.Data: toTransaction :: TxData -> Transaction
+ Haskoin.Store.Data: toTransaction :: Ctx -> TxData -> Transaction
- Haskoin.Store.WebClient: apiBatch :: (Batchable a b, MonadIO m, MonadError Except m) => Natural -> ApiConfig -> a -> m b
+ Haskoin.Store.WebClient: apiBatch :: (Batchable a b, MonadIO m, MonadError Except m) => Ctx -> Natural -> ApiConfig -> a -> m b
- Haskoin.Store.WebClient: apiCall :: (ApiResource a b, MonadIO m, MonadError Except m) => ApiConfig -> a -> m b
+ Haskoin.Store.WebClient: apiCall :: (ApiResource a b, MonadIO m, MonadError Except m) => Ctx -> ApiConfig -> a -> m b
- Haskoin.Store.WebCommon: class Serial b => ApiResource a b | a -> b
+ Haskoin.Store.WebCommon: class (Serial b) => ApiResource a b | a -> b
- Haskoin.Store.WebCommon: encodeParam :: Param a => Network -> a -> Maybe [Text]
+ Haskoin.Store.WebCommon: encodeParam :: Param a => Network -> Ctx -> a -> Maybe [Text]
- Haskoin.Store.WebCommon: parseParam :: Param a => Network -> [Text] -> Maybe a
+ Haskoin.Store.WebCommon: parseParam :: Param a => Network -> Ctx -> [Text] -> Maybe a
- Haskoin.Store.WebCommon: queryPath :: ApiResource a b => Network -> a -> Text
+ Haskoin.Store.WebCommon: queryPath :: ApiResource a b => Network -> Ctx -> a -> Text

Files

+ CHANGELOG.md view
@@ -0,0 +1,1758 @@+# Changelog+All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)+and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)++## [1.0.0] - 2023-07-28++### Changed++- Compatibility with latest haskoin-core and haskoin-node packages.+- Use DuplicateRecordFields and OverloadedRecordDot language extensions.+- Modernize package in a backwards-incompatible manner.+- Refactor package extensively.++## [0.65.10] - 2023-05-12++### Changed++- Revert to older LTS Haskell to attempt to resolve memory leak.++### Removed++- Remove unnecessary lock release.++## [0.65.9] - 2023-05-09++### Changed++- Be more aggressive caching individual transactions.+- Introduce a periodic cache mempool sync task.++## [0.65.8] - 2023-05-08++### Changed++- Perform health check in separate thread.++## [0.65.7] - 2023-05-05++### Changed++- Do not sync mempool to Redis if node is not up-to-date.++## [0.65.6] - 2023-05-04++### Changed++- Improve caching ingestion performance.++## [0.65.5] - 2022-09-19++### Added++- Fee field added to Blockchain.info-style export-history endpoint.++### Changed++- Bump version due to Git mistake.++## [0.65.3] - 2022-04-13++### Added++- IPv6 peer support.++## [0.65.2] - 2022-04-13++### Fixed++- Correct lazy bytestring deserialization algorithm for web client.++## [0.65.1] - 2022-04-13++### Changed++- Simplify function in Cache module.+- Bump depenedencies.++## [0.65.0] - 2022-02-03++### Changed++- Spenders now embedded into tx objects in database for performance.+- Use faster IORef instead of slow TVar when ingesting.+- Use mutable hash tables when ingesting.+- Allow to disable unscalable API endpoints.++## [0.64.19] - 2022-02-01++### Added++- Allow to set query timeouts.++## [0.64.18] - 2022-01-21++### Fixed++- Off-by-one error when importing change addresses to cache.++## [0.64.17] - 2022-01-21++### Fixed++- Add change addresses to cache when necessary.++## [0.64.16] - 2022-01-10++### Fixed++- Correct fee calculation for blocks that do not claim all rewards.++## [0.64.15] - 2022-01-07++### Fixed++- Correct scale of cache pruning code.++## [0.64.14] - 2022-01-07++### Fixed++- Always prune to 80% of max cache size.++## [0.64.13] - 2022-01-07++### Fixed++- Avoid doing a hissy fit on transient cache issue.++## [0.64.12] - 2022-01-07++### Fixed++- Prune a little more cache to allow for new keys.++## [0.64.11] - 2022-01-06++### Fixed++- Prune cache more often.++## [0.64.10] - 2021-12-30++### Changed++- Return empty addresses in xpub balances call.++## [0.64.9] - 2021-12-29++### Fixed++- Avoid notifying same transactions in mempool.++## [0.64.8] - 2021-12-26++### Changed++- Use better Fourmolu format.+- Be more precise when counting retrieved items.++### Fixed++- Avoid transactions duplicated in xpub data.+- Refactor some functions to make them more robust.++## [0.64.7] - 2021-12-26++### Fixed++- Optimisations for various Blockchain endpoints.+- More accurate item counts for various web endpoints.++## [0.64.6] - 2021-12-25++### Fixed++- Record cache index time just once per xpub.++## [0.64.5] - 2021-12-25++### Changed++- Add suffix cached to cache entries to avoid conflicts with data entries.++## [0.64.4] - 2021-12-25++### Fixed+- Bring dots back and avoid stats hacks.++## [0.64.3] - 2021-12-25++### Fixed++- Fix typo.++## [0.64.2] - 2021-12-25++### Changed++- Replace dots for underscores in metrics.++## [0.64.1] - 2021-12-25++### Changed++- Automatic formatting.+- Improve cache debug logging.++### Fixed++- Add item count to 'all' query metrics.++## [0.64.0] - 2021-12-25++### Changed++- Improve metrics.++### Fixed++- Get lock before importing transactions to cache.++## 0.63.0+### Fixed+- Make cache updates lightweight.++## 0.62.1+### Fixed+- Fix idempotent transaction elimination bug.++## 0.62.0+### Added+- Configurable Blockchain price URLs.+- Reuse HTTP connections with wreq sessions.++## 0.61.1+### Changed+- Upstream improvements on Base58 performance.++## 0.61.0+### Fixed+- Fixed recursion bug in Blockchain format conversion function.++## 0.60.0+### Fixed+- Correct notifications subsystem.++## 0.59.0+### Fixed+- Correct internal dependency.++## 0.58.0+### Fixed+- Correct output freeing algorithm.++## 0.57.0+### Changed+- Report errors freeing outputs.++## 0.56.0+### Changed+- Report all deleted mempool transactions.+- Crash on certain data corruption.++## 0.55.0+### Changed+- Always have a previous output object in Blockchain inputs.++## 0.54.0+### Added+- WebSocket support.+- Testnet4 support.++## 0.53.11+### Changed+- Dummy version increase to signal upstream update of Haskoin Core.++## 0.53.10+### Fixed+- Correct test that was reversed in previous version.++## 0.53.9+### Removed+- Mempool is no longer synced by default to fix public Bitcoin Core regression.++## 0.53.8+### Changed+- Put derivations stat inside database.++## 0.53.7+### Added+- Added counters for database retrievals.+- Added counter for xpub derivations.++### Changed+- Removed some buggy or unnecessary stats.++## 0.53.6+### Changed+- Improve web server statistics.++## 0.53.5+### Addded+- Endpoint to delete an xpub from cache.++## 0.53.4+### Added+- Debugging when reverting block.++### Fixed+- Negative (headers - blocks) diff shown incorrectly.+- Infinite loop when removing a block's coinbase transaction.++## 0.53.3+### Added+- Blockchain.info endpoint: `q/pubkeyhash/:addr`.+- Blockchain.info endpoint: `q/getsentbyaddress/:addr`.+- Blockchain.info endpoint: `q/pubkeyaddr/:addr`.+- Configurable request body limit size.++## 0.53.2+### Fixed+- Return output in satoshi for some endpoints.++## 0.53.1+### Fixed+- Return 404 in case of bad address.++## 0.53.0+### Added+- Blockchain.info endpoint: `q/addresstohash/:addr`.+- Blockchain.info endpoint: `q/addrpubkey/:pubkey`.+- Blockchain.info endpoint: `q/hashpubkey/:pubkey`.+- Blockchain.info endpoint: `q/getblockcount`.+- Blockchain.info endpoint: `q/latesthash`.+- Blockchain.info endpoint: `q/bcperblock`.+- Blockchain.info endpoint: `q/txtotalbtcoutput/:txid`.+- Blockchain.info endpoint: `q/txtotalbtcinput/:txid`.+- Blockchain.info endpoint: `q/txfee/:txid`.+- Blockchain.info endpoint: `q/txresult/:txhash/:addr`.+- Blockchain.info endpoint: `q/getreceivedbyaddress/:addr`.+- Blockchain.info endpoint: `q/addressbalance/:addr`.+- Blockchain.info endpoint: `q/addressfirstseen/:addr`.+- Support xpubs in `blockchain/rawaddr` endpoint.++### Fixed+- Various latest blocks endpoint fixes.+- Allow empty parameters in Blockchain.info POST requests.++## 0.52.13+### Fixed+- Upstream fixes for unknown inv types.++## 0.52.12+### Fixed+- Fix limits for legacy endpoints.++## 0.52.11+### Added+- Alias for Blockchain.info raw address endpoint.++### Fixed+- Fix unknown message types without payload upstream.+- Randomise peer timeouts upstream.++## 0.52.10+### Fixed+- Fix binary search algorithm for blocks upstream.++## 0.52.9+### Added+- Add Blockchain.info latest block endpoint.+- Add Blockchain.info unconfirmed-transactions endpoint.+- Add Blockchain.info blocks for one day endpoint.++## 0.52.8+### Fixed+- Fix special case for zero limit.++## 0.52.7+### Fixed+- Fix cache bug when too many transactions in mempool.++## 0.52.6+### Changed+- Use rationals for Blockchain.info export-history endpoint arithmetics.++## 0.52.5+### Added+- Support for Blockchain.info export-history endpoint.++### Fixed+- Use strict bytestring decoder for HTTP API client.++## 0.52.4+### Fixed+- Use Base58 addresses by default in Blockchain.info balance querios.++## 0.52.3+### Fixed+- Errors should also contain CORS headers.++## 0.52.2+### Fixed+- Blockchain.info raw address limit should use `limit` parametre and not `n`.++## 0.52.1+### Changed+- Stream multiple transactions when requested.++### Fixed+- Fix missing unspent outputs.++## 0.52.0+### Changed+- Remove unnecessary performance optimisations.++### Fixed+- Reduce balance retrievals for xpubs.+- Hack UTXO ordering to avoid unwanted behaviours.+- Do not merge conduits when there are too many.++## 0.51.0+### Fixed+- Data type for RequestTooLarge was missing.++## 0.50.3+### Added+- Log HTTP request bodies.++### Removed+- Remove web request timeout.++### Fixed+- Do not ignore unknown start tx anchors in conduits.+- Use fast transaction counting in cache.++### Changed+- Use WAI middlewares to manage stats.++## 0.50.2+### Fixed+- Do not use lazy parsers for incoming data that may throw exceptions.++## 0.50.1+### Fixed+- Do not allow incoming POST requests of unlimited body size.++## 0.50.0+### Added+- Limit number of simultaneous xpubs being cached.++## 0.49.0+### Changed+- Improve conduit merging algorithm.++## 0.48.0+### Added+- Timeouts and token bucket for web requests.+- Configurable price fetch interval.++## 0.47.6+### Fixed+- Fix web.all stat.++## 0.47.5+### Changed+- Simplify stats.++## 0.47.4+### Added+- Implement Blockchain.info block height.+- Implement Blockchain.info raw address.+- Implement Blockchain.info balances.++### Fixed+- Make Blockchain.info unspent endpoint more efficient.+- Make Blockchain.info xpubs endpoint more efficient.++### Changed+- Use compact JSON serialization by default everywhere.++## 0.47.3+### Fixed+- Fix serialization bug with unspents.++## 0.47.2+### Fixed+- Fix serialization bug for health check.++## 0.47.1+### Fixed+- Fix serialization bugs for web data types.++## 0.47.0+### Added+- Support for legacy block endpoints.++### Changed+- Serialization now uses bytes library and does interleaved IO.++## 0.46.6+### Added+- Allow comma-separated values in multiaddr.++## 0.46.5+### Changed+- Throw error when offset exceeded instead of truncating it.++## 0.46.4+### Fixed+- Do not compute balances separately for loose addresses when part of an xpub.++### Changed+- Do not fetch entire transaction list for loose addresses.+- Transaction count in wallet object is just a synonym for filtered tx count.+- Use conduits to prevent over-fetching transactions from database.++## 0.46.3+### Fixed+- Make hex value encoding compatible with Java BigInteger decoder.++## 0.46.2+### Changed+- Allow negative confirmations for compatibility with old API.++## 0.46.1+### Added+- Filters for transactions in multiaddr endpoint.++## 0.46.0+### Fixed+- Release to fix unintentional upload.++## 0.45.0+### Added+- Using a tx_index that results in a txid conflict returns a 409 - Conflict.++## 0.44.0+### Changed+- Numeric txid now 53 bits long and doesn't return transactions when hashes collide.++## 0.43.1+### Fixed+- Do not overcount statistics.++## 0.43.0+### Changed+- Transaction index now simply encodes txid as a long number (sorry JS users).++## 0.42.4+### Changed+- Optimise encoding of JSON data for Blockchain.info API.++## 0.42.3+### Changed+- Narrow down the balance for transactions to onlyShow set.++## 0.42.2+### Changed+- Make unspent algorithm more efficient.++## 0.42.1+### Added+- Support for Blockchain.info API unspent endpoint.++### Fixed+- Fix RBF transactions on Blockchain.info API.++## 0.42.0+### Removed+- No more pruning of old mempool transactions.++## 0.41.4+### Fixed+- Correct error computing wallet balances.++## 0.41.3+### Changed+- Use whole active set when computing results for multiaddr.+- Compute correct count of transactions for wallet.++## 0.41.2+### Changed+- Caching mempool is not much slower when pruning, so reversing that change.++## 0.41.1+### Changed+- Only prune mempool every ten minutes or so.++## 0.41.0+### Removed+- No more cache eviction endpoint.++## 0.40.22+### Added+- More metrics for cache.++## 0.40.21+### Added+- Support HTTP GET for multiaddr endpoint.++## 0.40.20+### Added+- Prefix 'app.' in statsd when using Nomad variables.++## 0.40.19+### Added+- Statsd support and pleny of statistics.++### Removed+- Removed use of sync lock for importing xpubs in cache due to volume.++## 0.40.18+### Added+- New options to control cache lock retry count and delay.++### Changed+- Cache xpubs asyncrhonously.+- Mempool syncs fully every refresh cycle.++## 0.40.17+### Changed+- Delete more than 1000 xpubs from Redis if needed.+- Attempt to get lock ten times in order to index a new xpub.++## 0.40.16+### Fixed+- Fix best block not set.++## 0.40.15+### Fixed+- Do not update cache best block if it is higher.++## 0.40.14+### Fixed+- Always respond to cache pings to avoid getting stuck.++## 0.40.13+### Added+- Be more careful about importing blocks to the cache.++### Removed+- Reduce excessive cache logging.++## 0.40.12+### Fixed+- Correct bad arithmetics in cache cooldown.++## 0.40.11+### Added+- Debug log cache cooldown.++### Fixed+- Unlimited retry lock acquisition now works.+- Make locking tighter++## 0.40.10+### Changed+- Reduce the number of things that cache do when pinged.++## 0.40.9+### Fixed+- Avoid filling the cache actor mailbox with pings.++## 0.40.8+### Fixed+- Correct a typo in cache code.++## 0.40.7+### Changed+- Improve cache mempool sync algorithm.++## 0.40.6+### Changed+- Make cache cooldown a number.++## 0.40.5+### Added+- Log every non-successful query.++## 0.40.4+### Fixed+- Use Redis TTL for cache cooldown.++## 0.40.3+### Fixed+- Mempool no longer gets saved in cache when node is not in sync.++## 0.40.2+### Changed+- Change source of price data to https://blockchain.info/ticker.++## 0.40.1+### Removed+- Do not expose Paths_haskoin_store module explicitly.++## 0.40.0+### Added+- Blockchain.info API compatibility for multiaddr and rawtx endpoints.++### Changed+- Segwit fields are no longer omitted on incompatible networks.++## 0.39.0+### Changed+- Upgrade Haskoin Core to support higher version on witness addresses.++## 0.38.4+### Fixed+- Fix block search by timestamp.++## 0.38.3+### Changed+- Disable low latency garbage collector by default.++## 0.38.2+### Changed+- Use GHC 8.10 and its low latency garbage collector.++## 0.38.1+### Changed+- Optimise algorithm for fork safety check.++## 0.38.0+### Added+- Support for Bitcoin Cash hard fork.++## 0.37.5+### Added+- Configurable cache refresh time.++## 0.37.4+### Fixed+- Now request mempool when connecting even if already in sync.++### Changed+- Add a delay to mempool sync to allow for block headers to catch up.+- Attempt to sync mempool whenever syncing is done or a new peer connects.++## 0.37.3+### Changed+- Correct logic error when deleting superseded transactions.++## 0.37.2+### Changed+- Simplify multiple algorithms to hopefully solve corruption bug.+- Allow to disable mempool transaction indexing via command line.++## 0.37.1+### Changed+- Better locking around caching operations.+- Have a global cooldown on cache mempool updates.++### Fixed+- Fix address unspent retrieval using correct column family.++## 0.37.0+### Added+- Add support for column families and prefixes breaking database backwards compatibility.++## 0.36.2+### Fixed+- Fix another place where mempool sorting was wrong.++## 0.36.1+### Fixed+- Fix wrong sorting of mempool transactions in database.++## 0.36.0+### Changed+- Do not use STM transactions because it is impossible to reason about preloading.+- Make importing blocks after mempool is populated faster.++## 0.35.2+### Changed+- Reduce frequency of cache mempool updates.++## 0.35.1+### Changed+- Remove cache lock retries.+- Do not use lock when caching a new xpub.++## 0.35.0+### Changed+- Use new RocksDB Haskell bindings.+- Make headers - blocks diff configurable in health check.+- Simplify locking in cache.++## 0.34.8+### Changed+- Minor refactoring.+- Only send one mempool message ever.++## 0.34.7+### Changed+- Fix block heights endpoint URL.++## 0.34.6+### Removed+- Remove asynchronous database retrieval permanently due to instability.++## 0.34.5+### Added+- Preload memory before freeing outputs.++## 0.34.4+### Added+- Asynchronous preloading only for blocks.++## 0.34.3+### Removed+- Disable asynchronous preloading to prevent segmentation fault error.++## 0.34.2+### Removed+- No more upper limit for mempool call.++### Fixed+- Health check uses minimum between last block and mempool tx.++## 0.34.1+### Added+- Pending mempool transactions health check.++### Removed+- No more upper limit for mempool call.++## 0.34.0+### Changed+- New format for health check.++## 0.33.1+### Changed+- Upload with updated Cabal files.++## 0.33.0+### Fixed+- Fix outputs showing up as both spent and unspent.++### Changed+- Simplify data structures used to hold uncommitted changes.+- Split StoreRead class in two.+- Improve pre-loading algorithms.++## 0.32.3+### Fixed+- Fix command line argument mappings to env vars.++## 0.32.2+### Fixed+- Do not complain when deleting non-existent unconfirmed txs.++## 0.32.1+### Fixed+- Fix corruption bug when deleting transactions during some reorgs.+- Fix tests on data package.+- Rename upstream address string functions.++## 0.32.0+### Changed+- Concurrently load dependencies when importing transaction data.+- Import each transaction using STM.+- Rework logging.+- Refactor all block chain importing algorithms.+- Use peer locking when importing data.+- Change license to MIT.++### Added+- Make max peer count configurable.++### Fixed+- Prevent premature peer timeouts when importing blocks.++## 0.31.0+### Added+- Add Haskell web client code to data library.+- Use type-safe definitions for web paths and parameters.+- Support JSON pretty encoding and enable by default for some endpoints.+- Allow environment variables for configuration.++### Changed+- Change command-line arguments to better fit GNU style.+- Refactor code to manipulate address balances.+- Improve logging.+- Rate-limit mempool importing.+- Do not add xpubs to cache if node not in sync.+- Do not import transactions to cache if no xpubs in cache.+- Refactor transaction importing logic extremely.+- Refactor memory database and writer.++### Fixed+- Fix BTC double-count transaction bug.+- Add missing xpub derive parameter to xpub evict documentation.++## 0.30.1+### Changed+- Limit mempool API endpoint list.++## 0.30.0+### Changed+- Raw JSON values are now wrapped in an object with a result property.+- Better logging when importing transactions into mempool.++### Fixed+- Transaction limit by block height works now.++## 0.29.3+### Changed+- Revert concurrent retrieval changes from 0.29.1 and 0.29.0 to solve stability+  issues.+- Bitcoin Cash (BCH) replaces BTC as default network in CLI.+- Peer discovery enabled automatically if no peers specified in CLI.+- Use separate Stack configuration file for data package.++## 0.29.2+### Changed+- Increase timeouts for manager and chain health checks.++## 0.29.1+### Added+- Perform xpub operations concurrently.++## 0.29.0+### Changed+- BlockTx renamed to TxRef.+- Remove unnecessary monad transformers to simplify import code.++### Added+- Retrieve balances and unspent outputs concurrently when importing.+- Coin instance for Unspent in data package.++## 0.28.0+### Changed+- Change the way that limits, start, and offset values are handled.++### Fixed+- Fix incorrect limit responses.++## 0.27.0+### Added+- Complete support for offset.++### Changed+- Improve monad stack for cache.++## 0.26.6+### Added+- Support for offset in all endpoints where it makes sense.++## 0.26.5+### Fixed+- Sort after using the nub function or use Data.List.nub in exceptional cases.++## 0.26.4+### Changed+- Don't do certain validity checks on confirmed transactions.++### Fixed+- Do not crash when syncing the blockchain from scratch on a populated cache.++## 0.26.3+### Changed+- Replace nub function everywhere with faster version written using hash sets.++### Fixed+- Fix shared cache locking.++## 0.26.2+### Changed+- Simplify cache block import code.+- Improve cache import performance.++## 0.26.1+### Fixed+- Do not call zadd or zrem in Redis with incorrect argument count.++### Changed+- Do not use slow Transaction type in Logic.+- Improve logging while importing blocks.+- Better transaction import algorithm.++## 0.26.0+### Changed+- Add information to some binary types for feature parity with JSON.++## 0.25.4+### Changed+- Hide segwit data on non-segwit networks.++### Fixed+- Fix work which now always serialises as a number literal.++## 0.25.3+### Changed+- Fix version display on health check.++## 0.25.2+### Changed+- Fix wrong version of Haskoin Store Data dependency.++## 0.25.1+### Changed+- Improve code organisation.+- Split data definitions and serialisation into its own package.++## 0.24.0+### Changed+- Use mocking to simulate peers instead of connecting to the network while testing.+- Depend on latest version of Haskoin Core for faster JSON serialisation.+- Clean up confusing JSON encoding/decoding codebase.++## 0.23.24+### Changed+- Implement faster JSON encoding using toEncoding from the Aeson library.++## 0.23.23+### Changed+- Do not read full mempool so often from cache code.++## 0.23.22+### Changed+- Depend on latest Haskoin Node that fixes debugging regression.++## 0.23.21+### Changed+- Reduce non-debug log verbosity.++## 0.23.20+### Changed+- Better Haskoin Node logging.++## 0.23.19+### Changed+- Use set peer timeout instead of constant in block store. ++## 0.23.18+### Changed+- Allow setting peer timeouts in command line.++### Fixed+- Bump Haskoin Node dependency to fix another premature timeout condition.++## 0.23.17+### Fixed+- Touch syncing peer whenever we process one of its blocks to avoid premature timeout.++## 0.23.16+### Changed+- Better algorithm to avoid importing transaction multiple times in cache.+- Depend on latest Haskoin Node.++## 0.23.15+### Changed+- Do not storm the cache with mempool transactions.++## 0.23.13+### Fixed+- Do not ignore deleted incoming transactions.++## 0.23.12+### Fixed+- Do not do RBF checks when replacing a mempool transaction with a confirmed one.++## 0.23.11+### Fixed+- Streamline mempool transaction importing.++## 0.23.10+### Fixed+- Fix transactions not recorded in cache mempool set.+- Fix transactions being downloaded multiple times.++### Removed+- Do not store orphan transactions in database.++### Changed+- Use sets for incoming transactions instead of lists.+- Do not do anything to the cache if there are no xpubs in it.++## 0.23.9+### Fixed+- Wiping mempool fixed.++## 0.23.8+### Added+- Chunk transactions to be deleted from mempool.++## 0.23.7+### Added+- Mempool improvements.++## 0.23.6+### Added+- Ability to wipe mempool at start.+- Improvements to mempool processing code.++## 0.23.5+### Changed+- Tighten the locking loop to avoid slow cache building.++## 0.23.5+### Fixed+- Wrong error in cache when acquiring lock.++## 0.23.4+### Added+- Add extra debug logging for cache code.++### Fixed+- Fix a bug with xpub growing algorithm.++### Changed+- Use locks instead of transactions to update cache.++## 0.23.3+### Fixed+- Reduce contention when many instances of Haskoin Store share a cache.++## 0.23.2+### Added+- Allow retrieving xpub data without using cache.++## 0.23.1+### Added+- Allow xpub eviction from cache via API.+- Clarify cache address addition code.++### Fixed+- Balances were incorrectly computed in cache when new transactions arrived.++## 0.23.0+### Added+- Support for Redis transactions.+- Use a smaller initial gap for empty xpubs.++### Changed+- Remove custom JSON encoding class.+- Refactor and code simplification.++## 0.22.5+### Fixed+- Cache was being completely pruned.++## 0.22.4+### Fixed+- Cache now prunes correctly.++## 0.22.3+### Fixed+- Bug was making cache get stuck when pruning.++## 0.22.2+### Changed+- More efficient algorithms for caching and cache misses.+- Better debug logging of cache hits and misses.++## 0.22.1+### Added+- More debug logging for cache hits.++### Fixed+- Bug using maximum against empty list.++## 0.22.0+### Changed+- Extreme code refactoring.+- Move all code to Haskoin and drop Network from modules.++### Added+- Use Redis pipelining when importing multiple transactions into cache.+- Implement configurable LRU for Redis cache.+- Import xpubs directly into cache from web worker thread when a key is requested.++### Removed+- Only expose a few modules to external API.++## 0.21.7+### Changed+- Improve build configuration.++### Fixed+- Use minimum used addresses instead of minimum index for xpub cache decision.++## 0.21.6+### Fixed+- Fix missing xpub unspent outputs when using cache.++## 0.21.5+### Added+- Only store xpubs in cache if they have more than a threshold addresses used.++## 0.21.4+### Fixed+- Fix shared cache case where head is set beyond header chain by another node.++## 0.21.3+### Fixed+- Fix bug where best head was not being registered in cache.+- Fix best head in cache being decoded incorrectly.++## 0.21.2+### Added+- Complete support for Redis xpub cache.++## 0.21.1+### Fixed+- Latest version of secp256k1-haskell works with Debian 9.++## 0.21.0+### Fixed+- Fix output of web API calls when issuing limits with offsets.++### Changed+- Massive refactoring of entire codebase.++### Added+- Work in progress Redis caching for extended public keys.++## 0.20.2+### Changed+- Filter xpub address balances on web API to show only addresses that have been used.++### Removed+- Remove paths and addresses from xpub summary.++## 0.20.1+### Changed+- Refactor code greatly.+- Depend on new Haskoin Store package to avoid missing tx broadcasts.+- Merge StoreStream class into StoreRead.+- Move former streaming functions to use lists instead of conduits.+- Remove excessively verbose debugging.++## 0.20.0+### Added+- Set minimum version bounds for some dependencies.+- Now compatible with GHC 8.8.+- Extended key caching system using Redis.++### Changed+- Massively refactored codebase.+- Less verbose debug logging.++### Removed+- Removed conduits for faster queries.++## 0.19.6+### Added+- Use a parallel strategy to compute key derivations.++### Fixed+- Do not fail health check upon transaction timeout while syncing.++### Changed+- Do not use conduits for xpub balance streams.+- Multiple minor refactorings.++## 0.19.5+### Changed+- Minor refactor to block import code.++### Fixed+- Minor fix to transaction timeout check.++## 0.19.4+### Fixed+- Clarify and correct health check algorithm.++## 0.19.3+### Changed+- Add address transactions to cache.+- Improve multi-address transaction retrieval algorithms.++## 0.19.2+### Removed+- Cache-Control header turned out to be unnecessary.++### Fixed+- Fix some minor errors in web module.++## 0.19.1+### Added+- Set Cache-Control header to no-cache.++## 0.19.0+### Changed+- Store mempool in single key/value pair.++## 0.18.11+### Changed+- Do not stream mempool.++## 0.18.10+### Removed+- Disable timeout checks for testnets.++## 0.18.9+### Added+- Endpoint to locate a block by unix timestamp.++### Removed+- No more persistence for peers due to dependency on newest haskoin-node.++## 0.18.8+### Added+- Transaction and block timeouts for health check.+- Raw blocks.++## 0.18.7+### Fixed+- Missing tranasctions on xpub listings.++## 0.18.6+### Removed+- Follow Stack advise removing `-O2` GHC option.++## 0.18.5+### Added+- Compatibility with SegWit on extended public key endpoints.++### Changed+- Fix syncing peer not reset after timeout.+- Use simpler monad for streaming data.++## 0.18.4+### Changed+- Bump Haskoin Node to fix peers not stored and excessively verbose logging.++## 0.18.3+### Changed+- Configurable HTTP request logging. Disabled by default.++## 0.18.2+### Changed+- Fix for memory leak.++## 0.18.1+### Removed+- Remove transaction count from xpub summary object.++## 0.18.0+### Changed+- Simplified limits and start point handling on web API.+- Made transaction streaming algorithm faster for xpub transactions.+- Extended public key summary output contains all addresses that have received transactions.++### Added+- Fine-grained control for maximum limits via command line options.+- Transaction hash as starting point.+- Block hash as starting point.+- Timestamp as starting point.+- Configurable xpub gap limit.+- Transaction count added to xpub summary.+- UTXO count added to xpub summary.++### Removed+- Mempool endpoint now has no limits or offsets and always returns full list.+- Extended public key summary output no longer includes any transactions.+- Offsets not allowed for transaction lists involving multiple addresses or xpubs.+- Confusing block position parameter no longer part of web API.++## 0.17.2+### Changed+- Stream address balances and unspent outputs.+- Add configurable max value for limit and offset which defaults at 10000.++## 0.17.1+### Changed+- When posting a transaction to the network, timeout is now five seconds.+- Improve error message when transaction post timeout reached.+- Remove obsolete `not found` error for transaction post.+- Endpoints for retrieving blocks now do streaming for better performance.+- Improve Swagger API documentation.++## 0.17.0+### Added+- Endpoints for retrieving block transactions.+- Endpoint for retrieving set of latest blocks.++### Changed+- Use standardized JSON and binary serialization schemes for raw transaction endpoints.++## 0.16.6+### Changed+- Now logging info messages too by default.+- Consolidated web logging in middleware.++### Removed+- UUIDs for web requests.++## 0.16.5+### Removed+- Remove concurrency from xpub balance requests to prevent RocksDB segfaults.++## 0.16.4+### Removed+- Remove concurrency from requests using iterators to prevent RocksDB from segfaulting.++## 0.16.3+### Added+- Debugging information for web API.++## 0.16.2+### Changed+- Debugging disabled by default (use `--debug` to enable).++## 0.16.1+### Added+- Cache mempool transactions.+- Improve initial syncing performance.++## 0.16.0+### Added+- Orphan transaction support.+- Full address balance cache in RocksDB.+- Full unspent output cache in RocksDB.++### Changed+- Significantly refactor code.+- Move web stuff to its own module.+- Change types related to databases.+- Make xpub balance, transaction and unspent queries fetch data in parallel.++## 0.15.2+### Added+- Internal data types to support orphan transactions.++### Changed+- Do not spam block actor with pings.+- Fix balance/unspent cache not reverting when importing fails.+- Fix transaction sorting algorithm not including transaction position information.+- Fix conflicting mempool transaction preventing block from importing.++## 0.15.1+### Changed+- Fix duplicate coinbase transaction id bug.++## 0.15.0+### Removed+- Removed `PreciseUnixTime` data type.++### Changed+- Use 64 bits for Unix time representation.+- Data model now uses simplified Unix time representation.++## 0.14.9+### Added+- Last external/change index information to xpub summary object.++## 0.14.8+### Added+- Endpoint for xpub summaries.+- Endpoints for full transactions.+- Ability to query by offset/limit.+- More API documentation.++## 0.14.7+### Added+- Debug information for `publishTx`.++### Changed+- Transaction publishing no longer requests mempool.+- Fixed serialization for `TxId` freezing the entire program by infinitely recursing.++## 0.14.6+### Changed+- Enable full threading again as it was deemed not responsible for the freezing behaviour.++## 0.14.5+### Changed+- Enable threading but leave it at a single thread to be able to open more than 1024 files.++## 0.14.4+### Changed+- Target LTS Haskell 13.20 and disable threading in new attempt to fix freezing bug.++## 0.14.3+### Changed+- Remove `-O2`.++## 0.14.2+### Changed+- Target LTS Haskell 12.26 to attempt to fix freezing bug.++## 0.14.1+### Added+- Extra debugging around code that freezes.++### Changed+- Bump dependency on `haskoin-node`.++## 0.14.0+### Removed+- Dump slow protobuf serialization.++### Added+- Add custom serialization.+- Extra debug logging.++### Changed+- Bump `haskoin-core` and `haskoin-node`.++## 0.13.1+### Changed+- Bump `haskoin-node` in `stack.yaml`.+- Do not send empty `getdata` messages.++## 0.13.0+### Added+- Primitive content negotiation for web exceptions.+- Protobuf support for errors.+- Protobuf support for tranasction ids.++### Changed+- Protobuf format changed in non-backwards-compatible manner for transaction ids.++## 0.12.0+### Added+- Support for binary serialization using Protocol Buffers.+- New endpoints for binary raw transactions (not hex-encoded).++### Changed+- Services field now a hex string instead of a number to avoid overflowing signed 64-bit integer.+- Flatten list of block data objects when responding to request for multiple block heights.+- Errors now reported in plain text without container JSON object.+- Transaction broadcasts are responded to with transaction id in plaintext hex (no JSON).+- Remove database snapshots to improve performance.++## 0.11.2+### Changed+- Fix duplicate mempool transaction announcements in event stream.++## 0.11.1+### Removed+- Removed latest block time check.++## 0.11.0+### Changed+- Improve post transactions endpoint.++## 0.10.1+### Changed+- Fix bug where transaction lists from multiple addresses would sort incorrectly.+- Address gap reduced to 20.++## 0.10.0+### Removed+- Remove addresses from transaction lists.+- No longer use container objects for xpub transactions.++## 0.9.3+### Added+- Permissive CORS headers to allow queries from any domain.+- Improved documentation using real-world examples from the BCH testnet.++## 0.9.2+### Added+- HTTP JSON API switch to turn off transaction list when retrieving blocks.++## 0.9.1+### Added+- Total block fees.+- Total block outputs.+- Block subsidy.++### Changed+- Do not consider the blocks less one block away from headers as out of sync in health check.+- Health check now returns HTTP 503 when not OK or out of sync.++## 0.9.0+### Added+- Version to health check output.+- Block weight for segwit.+- Transaction weight for segwit.++## 0.8.1+### Added+- Health check endpoint.++## 0.8.0+### Added+- Limits and skips.+- Add timestamps to transactions.+- Add transaction count to address balance object.+- Add Merkle root to block data.+- Total funds received by an address now shows up in balance.+- Balances for any address that ever had funds show up in xpub endpoints.++### Changed+- Data model update.+- Performance improvement for xpub calls.+- Transactions are returned in reverse mempool/block order (highest or most recent first).+- Balance objects do not get deleted from database ever.++## 0.6.9+### Changed+- Reduce number of coinbase checks to 10,000 ancestors.++## 0.6.8+### Changed+- Further optimize coinbase after height checks.++## 0.6.7+### Changed+- Impose restrictions on recursion for coinbase after height checks.++## 0.6.6+### Added+- Check whether a transaction can be traced back to a coinbase after certain height.++## 0.6.5+### Changed+- Delete transactions in reverse topological order when reverting a block.++## 0.6.4+### Changed+- Do not fail silently when importing orphan transactions into the mempool.++## 0.6.3+### Changed+- Dummy release to bump haskoin-node in stack.yaml.++## 0.6.2+### Changed+- Correct bug where coinbase transactions weren't properly flagged.++## 0.6.1+### Changed+- Compatibility with Bitcoin Cash hard fork.+- Various bug fixes.++## 0.6.0+### Added+- Address balance cache in memory.++### Changed+- Simplify data model further.+- Fix bug importing outputs with UTXO cache.+- Unspent balances cannot be negative.++## 0.5.0+### Added+- Add UTXO cache in memory.+- Get transactions with witness data in segwit networks.++### Changed+- Paths for derivations in xpubs is a list and no longer a string.+- Various bug fixes.++## 0.4.2+### Removed+- Remove extended public key itself from output of relevant endpoints to save bandwidth.++## 0.4.1+### Changed+- Fix bug when deleting coinbase transactions.+- Extended public key API support.++## 0.4.0+### Changed+- Generate events for mempool transactions.+- Respond with entire block data when querying blocks by height.++## 0.3.1+### Changed+- Do not import transactions to mempool while not synced.+- Only sync mempool against a single peer.+- Allow duplicate transactions to fix re-introduced sync bug.++## 0.3.0+### Added+- Update dependencies.+- Keep orphan blocks and deleted transactions in database.+- Add a `mainchain` field for block data and a `deleted` field for transactions.+- Stream records for performance.+- Show witness data for transaction inputs in SegWit network.+- Support RBF in SegWit network.++### Changed+- Refactor all data access code away from actor.+- Refactor import logic away from actor.+- Abstract data access using typeclasses.+- Implement data access using clean layered architecture.+- Make most of import logic code pure.+- Database now in `db` as opposed to `blocks` directory.+- Use latest `haskoin-node`.++### Removed+- Remove some data from peer information output.+- Remove full transaction from address transaction data.+- Remove limits from address transaction data.+- Remove block data from previous output.+- Remove spender from JSON response when output not spent.+- Remove block hash from block reference.++## 0.2.3+### Removed+- Do not send transaction notifications if a block is being imported.++## 0.2.2+### Added+- Peer information endpoint.++### Changed+- Update `haskoin-node`.++## 0.2.1+### Changed+- Fix tests++## 0.2.0+### Added+- Documentation everywhere.+- Ability to retrieve address transactions.++### Changed+- New versions of NQE and Haskoin Node upstream.+- Improve and simplify API.+- Multi-element endpoints return arrays of arrays.+- Database snapshots for all queries are now mandatory.++### Removed+- Retrieving unspent and spent outputs for an address.+- Redundant API endpoints for multiple elements.++## 0.1.3+### Changed+- Fix a bug with transaction notifications.+- Improve handling orphan transactions.++## 0.1.2+### Changed+- Specify dependencies better.++## 0.1.1+### Changed+- Dependency `secp256k1` is now `secp256k1-haskell`.++## 0.1.0+### Added+- New `CHANGELOG.md` file.+- Bitcoin (BTC) and Bitcoin Cash (BCH) compatibility.+- RocksDB database.+- Mempool support.+- HTTP streaming for events.+- CashAddr support.+- Bech32 support.+- Rate limits.++### Changed+- Split out of former `haskoin` repository.+- Use hpack and `package.yaml`.++### Removed+- Removed Stylish Haskell configuration file.+- Remvoed `haskoin-core` and `haskoin-wallet` packages from this repository.
+ README.md view
@@ -0,0 +1,57 @@+# Haskoin Store++Block chain store and index featuring:++- Persistent storage using [RocksDB](https://rocksdb.org/).+- [Bitcoin Cash (BCH)](https://www.bitcoincash.org/) support.+- [Bitcoin Core (BTC)](https://bitcoin.org/) support.+- Indices for address balances, transactions, and unspent outputs (UTXO).+- Persistent mempool.+- Replace Bitcoin Core (BTC) RBF transactions by default.+- Query transactions, balances and UTXO on extended keys (xpub).+- Optional accelerated xpub cache using Redis.+- RESTful API with JSON and binary serialization.+- High performance concurrent architecture.+- Compatibility with Blockchain.info-style endpoints.++## Install with Nix on any distribution++* Get [Nix](https://nixos.org/nix/)++```sh+nix-env --install stack+git clone https://github.com/haskoin/haskoin-store.git+cd haskoin-store+stack --nix build --copy-bins+~/.local/bin/haskoin-store --help+```++## Install on Ubuntu or Debian++```sh+apt install git zlib1g-dev libsecp256k1-dev librocksdb-dev pkg-config haskell-stack+git clone https://github.com/haskoin/haskoin-store.git+cd haskoin-store+stack build --copy-bins+~/.local/bin/haskoin-store --help+```++## Install on Fedora++```sh+dnf install git zlib-devel libsecp256k1-devel rocksdb-devel stack+git clone https://github.com/haskoin/haskoin-store.git+cd haskoin-store+stack build --copy-bins+~/.local/bin/haskoin-store --help+```++## Non-Haskell Dependencies++* [zlib](https://zlib.net/)+* [libsecp256k1](https://github.com/Bitcoin-ABC/secp256k1)+* [RocksDB](https://github.com/facebook/rocksdb)++## API Documentation++* [Swagger API Documentation](https://api.haskoin.com/)
haskoin-store-data.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.36.0.+-- This file has been generated from package.yaml by hpack version 0.35.2. -- -- see: https://github.com/sol/hpack ----- hash: 03532ff6fc9ef1e166af059c92a3233d35b358009d28980754f5bbce1abec023+-- hash: 5c6d0f79cd18954de7032dc7b391fccf7ff90f5afd84f693d4533a84f7a2afcc  name:           haskoin-store-data-version:        0.65.12+version:        1.0.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@@ -18,6 +18,9 @@ license:        MIT license-file:   LICENSE build-type:     Simple+extra-source-files:+    README.md+    CHANGELOG.md  source-repository head   type: git@@ -43,7 +46,7 @@     , data-default >=0.7.1.1     , deepseq >=1.4.4.0     , hashable >=1.3.0.0-    , haskoin-core >=0.21.0+    , haskoin-core >=1.0.0     , http-client >=0.6.4.1     , http-types >=0.12.3     , lens >=4.18.1@@ -54,10 +57,11 @@     , text >=1.2.3.0     , time >=1.9.3     , unordered-containers >=0.2.10+    , vector >=0.13.0.0     , wreq >=0.5.3.2   default-language: Haskell2010 -test-suite haskoin-store-test+test-suite spec   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:@@ -79,7 +83,7 @@     , data-default >=0.7.1.1     , deepseq >=1.4.4.0     , hashable >=1.3.0.0-    , haskoin-core >=0.21.0+    , haskoin-core >=1.0.0     , haskoin-store-data     , hspec >=2.7.1     , http-client >=0.6.4.1@@ -92,6 +96,7 @@     , text >=1.2.3.0     , time >=1.9.3     , unordered-containers >=0.2.10+    , vector >=0.13.0.0     , wreq >=0.5.3.2   default-language: Haskell2010   build-tool-depends: hspec-discover:hspec-discover
src/Haskoin/Store/Data.hs view
@@ -1,3623 +1,3476 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Haskoin.Store.Data-  ( -- * Address Balances-    Balance (..),-    balanceToJSON,-    balanceToEncoding,-    balanceParseJSON,-    zeroBalance,-    nullBalance,--    -- * Block Data-    BlockData (..),-    blockDataToJSON,-    blockDataToEncoding,-    confirmed,--    -- * Transactions-    TxRef (..),-    TxData (..),-    txDataFee,-    isCoinbaseTx,-    Transaction (..),-    transactionToJSON,-    transactionToEncoding,-    transactionParseJSON,-    transactionData,-    fromTransaction,-    toTransaction,-    StoreInput (..),-    storeInputToJSON,-    storeInputToEncoding,-    storeInputParseJSON,-    isCoinbase,-    StoreOutput (..),-    storeOutputToJSON,-    storeOutputToEncoding,-    storeOutputParseJSON,-    Prev (..),-    Spender (..),-    BlockRef (..),-    UnixTime,-    getUnixTime,-    putUnixTime,-    BlockPos,--    -- * Unspent Outputs-    Unspent (..),-    unspentToJSON,-    unspentToEncoding,-    unspentParseJSON,--    -- * Extended Public Keys-    XPubSpec (..),-    XPubBal (..),-    xPubBalToJSON,-    xPubBalToEncoding,-    xPubBalParseJSON,-    XPubUnspent (..),-    xPubUnspentToJSON,-    xPubUnspentToEncoding,-    xPubUnspentParseJSON,-    XPubSummary (..),-    DeriveType (..),-    textToDeriveType,-    deriveTypeToText,--    -- * Other Data-    TxId (..),-    GenericResult (..),-    SerialList (..),-    RawResult (..),-    RawResultList (..),-    PeerInformation (..),-    Healthy (..),-    BlockHealth (..),-    TimeHealth (..),-    CountHealth (..),-    MaxHealth (..),-    HealthCheck (..),-    Event (..),-    Except (..),--    -- * Blockchain.info API-    BinfoBlockId (..),-    BinfoTxId (..),-    encodeBinfoTxId,-    BinfoFilter (..),-    BinfoMultiAddr (..),-    binfoMultiAddrToJSON,-    binfoMultiAddrToEncoding,-    binfoMultiAddrParseJSON,-    BinfoShortBal (..),-    BinfoBalance (..),-    toBinfoAddrs,-    binfoBalanceToJSON,-    binfoBalanceToEncoding,-    binfoBalanceParseJSON,-    BinfoRawAddr (..),-    binfoRawAddrToJSON,-    binfoRawAddrToEncoding,-    binfoRawAddrParseJSON,-    BinfoAddr (..),-    parseBinfoAddr,-    BinfoWallet (..),-    BinfoUnspent (..),-    binfoUnspentToJSON,-    binfoUnspentToEncoding,-    binfoUnspentParseJSON,-    binfoHexValue,-    BinfoUnspents (..),-    binfoUnspentsToJSON,-    binfoUnspentsToEncoding,-    binfoUnspentsParseJSON,-    BinfoBlock (..),-    toBinfoBlock,-    binfoBlockToJSON,-    binfoBlockToEncoding,-    binfoBlockParseJSON,-    binfoBlocksToJSON,-    binfoBlocksToEncoding,-    binfoBlocksParseJSON,-    BinfoTx (..),-    relevantTxs,-    toBinfoTx,-    toBinfoTxSimple,-    binfoTxToJSON,-    binfoTxToEncoding,-    binfoTxParseJSON,-    BinfoTxInput (..),-    binfoTxInputToJSON,-    binfoTxInputToEncoding,-    binfoTxInputParseJSON,-    BinfoTxOutput (..),-    binfoTxOutputToJSON,-    binfoTxOutputToEncoding,-    binfoTxOutputParseJSON,-    BinfoSpender (..),-    BinfoXPubPath (..),-    binfoXPubPathToJSON,-    binfoXPubPathToEncoding,-    binfoXPubPathParseJSON,-    BinfoInfo (..),-    BinfoBlockInfo (..),-    toBinfoBlockInfo,-    BinfoSymbol (..),-    BinfoTicker (..),-    BinfoRate (..),-    BinfoHistory (..),-    toBinfoHistory,-    BinfoDate (..),-    BinfoHeader (..),-    BinfoMempool (..),-    binfoMempoolToJSON,-    binfoMempoolToEncoding,-    binfoMempoolParseJSON,-    BinfoBlocks (..),-  )-where--import Control.Applicative ((<|>))-import Control.DeepSeq (NFData)-import Control.Exception (Exception)-import Control.Monad (join, mzero, unless, (<=<))-import Data.Aeson-  ( Encoding,-    FromJSON (..),-    ToJSON (..),-    Value (..),-    (.!=),-    (.:),-    (.:?),-    (.=),-  )-import qualified Data.Aeson as A-import qualified Data.Aeson.Encoding as AE-import Data.Aeson.Types (Parser)-import Data.Binary (Binary (get, put))-import Data.Bits (Bits (..))-import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Builder as BSB-import Data.Bytes.Get-import qualified Data.Bytes.Get as Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Data.Default (Default (..))-import Data.Foldable (toList)-import Data.Function (on)-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as HashMap-import Data.HashSet (HashSet)-import qualified Data.HashSet as HashSet-import Data.Hashable (Hashable (..))-import Data.Int (Int32, Int64)-import qualified Data.IntMap as IntMap-import Data.IntMap.Strict (IntMap)-import Data.Maybe-  ( catMaybes,-    fromMaybe,-    isJust,-    isNothing,-    mapMaybe,-    maybeToList,-  )-import Data.Serialize (Serialize (..))-import Data.String.Conversions (cs)-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Encoding as TE-import qualified Data.Text.Lazy as TL-import qualified Data.Text.Lazy.Encoding as TLE-import Data.Time.Clock.POSIX-  ( posixSecondsToUTCTime,-    utcTimeToPOSIXSeconds,-  )-import Data.Time.Format-  ( defaultTimeLocale,-    formatTime,-    parseTimeM,-  )-import Data.Word (Word32, Word64)-import GHC.Generics (Generic)-import Haskoin hiding (inputAddress)-import Web.Scotty.Trans (Parsable (..), ScottyError (..))--data DeriveType-  = DeriveNormal-  | DeriveP2SH-  | DeriveP2WPKH-  deriving (Show, Eq, Generic, NFData)--textToDeriveType :: Text -> Maybe DeriveType-textToDeriveType "normal" = Just DeriveNormal-textToDeriveType "compat" = Just DeriveP2SH-textToDeriveType "segwit" = Just DeriveP2WPKH-textToDeriveType _ = Nothing--deriveTypeToText :: DeriveType -> Text-deriveTypeToText DeriveNormal = "normal"-deriveTypeToText DeriveP2SH = "compat"-deriveTypeToText DeriveP2WPKH = "segwit"--instance Serial DeriveType where-  serialize DeriveNormal = putWord8 0x00-  serialize DeriveP2SH = putWord8 0x01-  serialize DeriveP2WPKH = putWord8 0x02--  deserialize =-    getWord8 >>= \case-      0x00 -> return DeriveNormal-      0x01 -> return DeriveP2SH-      0x02 -> return DeriveP2WPKH-      _ -> return DeriveNormal--instance Binary DeriveType where-  put = serialize-  get = deserialize--instance Serialize DeriveType where-  put = serialize-  get = deserialize--instance Default DeriveType where-  def = DeriveNormal--instance Parsable DeriveType where-  parseParam txt =-    case textToDeriveType (TL.toStrict txt) of-      Nothing -> Left "invalid derivation type"-      Just x -> Right x--data XPubSpec = XPubSpec-  { xPubSpecKey :: !XPubKey,-    xPubDeriveType :: !DeriveType-  }-  deriving (Show, Eq, Generic, NFData)--instance Hashable XPubSpec where-  hashWithSalt i XPubSpec {xPubSpecKey = XPubKey {xPubKey = pubkey}} =-    hashWithSalt i pubkey--instance Serial XPubSpec where-  serialize XPubSpec {xPubSpecKey = k, xPubDeriveType = t} = do-    putWord8 (xPubDepth k)-    serialize (xPubParent k)-    putWord32be (xPubIndex k)-    serialize (xPubChain k)-    serialize (wrapPubKey True (xPubKey k))-    serialize t-  deserialize = do-    d <- getWord8-    p <- deserialize-    i <- getWord32be-    c <- deserialize-    k <- deserialize-    t <- deserialize-    let x =-          XPubKey-            { xPubDepth = d,-              xPubParent = p,-              xPubIndex = i,-              xPubChain = c,-              xPubKey = pubKeyPoint k-            }-    return XPubSpec {xPubSpecKey = x, xPubDeriveType = t}--instance Serialize XPubSpec where-  put = serialize-  get = deserialize--instance Binary XPubSpec where-  put = serialize-  get = deserialize--type UnixTime = Word64--type BlockPos = Word32---- | Binary such that ordering is inverted.-putUnixTime :: MonadPut m => Word64 -> m ()-putUnixTime w = putWord64be $ maxBound - w--getUnixTime :: MonadGet m => m Word64-getUnixTime = (maxBound -) <$> getWord64be---- | Reference to a block where a transaction is stored.-data BlockRef-  = BlockRef-      { -- | block height in the chain-        blockRefHeight :: !BlockHeight,-        -- | position of transaction within the block-        blockRefPos :: !Word32-      }-  | MemRef-      { memRefTime :: !UnixTime-      }-  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)---- | Serial entities will sort in reverse order.-instance Serial BlockRef where-  serialize MemRef {memRefTime = t} = do-    putWord8 0x00-    putUnixTime t-  serialize BlockRef {blockRefHeight = h, blockRefPos = p} = do-    putWord8 0x01-    putWord32be (maxBound - h)-    putWord32be (maxBound - p)-  deserialize =-    getWord8 >>= \case-      0x00 -> getmemref-      0x01 -> getblockref-      _ -> fail "Cannot decode BlockRef"-    where-      getmemref = do-        MemRef <$> getUnixTime-      getblockref = do-        h <- (maxBound -) <$> getWord32be-        p <- (maxBound -) <$> getWord32be-        return BlockRef {blockRefHeight = h, blockRefPos = p}--instance Serialize BlockRef where-  put = serialize-  get = deserialize--instance Binary BlockRef where-  put = serialize-  get = deserialize--confirmed :: BlockRef -> Bool-confirmed BlockRef {} = True-confirmed MemRef {} = False--instance ToJSON BlockRef where-  toJSON BlockRef {blockRefHeight = h, blockRefPos = p} =-    A.object ["height" .= h, "position" .= p]-  toJSON MemRef {memRefTime = t} = A.object ["mempool" .= t]-  toEncoding BlockRef {blockRefHeight = h, blockRefPos = p} =-    AE.pairs ("height" .= h <> "position" .= p)-  toEncoding MemRef {memRefTime = t} = AE.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-  { -- | block information-    txRefBlock :: !BlockRef,-    -- | transaction hash-    txRefHash :: !TxHash-  }-  deriving (Show, Eq, Ord, Generic, Hashable, NFData)--instance Serial TxRef where-  serialize (TxRef b h) = do-    serialize b-    serialize h--  deserialize =-    TxRef <$> deserialize <*> deserialize--instance Binary TxRef where-  put = serialize-  get = deserialize--instance Serialize TxRef where-  put = serialize-  get = deserialize--instance ToJSON TxRef where-  toJSON btx =-    A.object-      [ "txid" .= txRefHash btx,-        "block" .= txRefBlock btx-      ]-  toEncoding btx =-    AE.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-  { -- | address balance-    balanceAddress :: !Address,-    -- | confirmed balance-    balanceAmount :: !Word64,-    -- | unconfirmed balance-    balanceZero :: !Word64,-    -- | number of unspent outputs-    balanceUnspentCount :: !Word64,-    -- | number of transactions-    balanceTxCount :: !Word64,-    -- | total amount from all outputs in this address-    balanceTotalReceived :: !Word64-  }-  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)--instance Serial Balance where-  serialize Balance {..} = do-    serialize balanceAddress-    putWord64be balanceAmount-    putWord64be balanceZero-    putWord64be balanceUnspentCount-    putWord64be balanceTxCount-    putWord64be balanceTotalReceived--  deserialize = do-    balanceAddress <- deserialize-    balanceAmount <- getWord64be-    balanceZero <- getWord64be-    balanceUnspentCount <- getWord64be-    balanceTxCount <- getWord64be-    balanceTotalReceived <- getWord64be-    return Balance {..}--instance Binary Balance where-  put = serialize-  get = deserialize--instance Serialize Balance where-  put = serialize-  get = deserialize--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 =-  A.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 =-  AE.pairs $-    "address" `AE.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 :: !ByteString,-    unspentAddress :: !(Maybe Address)-  }-  deriving (Show, Eq, Generic, Hashable, NFData)---- | Follow same order as in database and cache by inverting outpoint sort--- order.-instance Ord Unspent where-  compare a b =-    compare-      (unspentBlock a, unspentPoint b)-      (unspentBlock b, unspentPoint a)--instance Serial Unspent where-  serialize Unspent {..} = do-    serialize unspentBlock-    serialize unspentPoint-    putWord64be unspentAmount-    putLengthBytes unspentScript-    putMaybe serialize unspentAddress--  deserialize = do-    unspentBlock <- deserialize-    unspentPoint <- deserialize-    unspentAmount <- getWord64be-    unspentScript <- getLengthBytes-    unspentAddress <- getMaybe deserialize-    return Unspent {..}--instance Binary Unspent where-  put = serialize-  get = deserialize--instance Serialize Unspent where-  put = serialize-  get = deserialize--instance Coin Unspent where-  coinValue = unspentAmount--unspentToJSON :: Network -> Unspent -> Value-unspentToJSON net u =-  A.object-    [ "address" .= (addrToJSON net <$> unspentAddress u),-      "block" .= unspentBlock u,-      "txid" .= outPointHash (unspentPoint u),-      "index" .= outPointIndex (unspentPoint u),-      "pkscript" .= encodeHex (unspentScript u),-      "value" .= unspentAmount u-    ]--unspentToEncoding :: Network -> Unspent -> Encoding-unspentToEncoding net u =-  AE.pairs $-    "address" `AE.pair` maybe AE.null_ (addrToEncoding net) (unspentAddress u)-      <> "block" .= unspentBlock u-      <> "txid" .= outPointHash (unspentPoint u)-      <> "index" .= outPointIndex (unspentPoint u)-      <> "pkscript" `AE.pair` AE.text (encodeHex (unspentScript u))-      <> "value" .= unspentAmount u--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 <- 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-  { -- | height of the block in the chain-    blockDataHeight :: !BlockHeight,-    -- | is this block in the main chain?-    blockDataMainChain :: !Bool,-    -- | accumulated work in that block-    blockDataWork :: !Integer,-    -- | block header-    blockDataHeader :: !BlockHeader,-    -- | size of the block including witnesses-    blockDataSize :: !Word32,-    -- | weight of this block (for segwit networks)-    blockDataWeight :: !Word32,-    -- | block transactions-    blockDataTxs :: ![TxHash],-    -- | sum of all transaction outputs-    blockDataOutputs :: !Word64,-    -- | sum of all transaction fees-    blockDataFees :: !Word64,-    -- | block subsidy-    blockDataSubsidy :: !Word64-  }-  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)--instance Serial BlockData where-  serialize BlockData {..} = do-    putWord32be blockDataHeight-    serialize blockDataMainChain-    putInteger blockDataWork-    serialize blockDataHeader-    putWord32be blockDataSize-    putWord32be blockDataWeight-    putList serialize blockDataTxs-    putWord64be blockDataOutputs-    putWord64be blockDataFees-    putWord64be blockDataSubsidy--  deserialize = do-    blockDataHeight <- getWord32be-    blockDataMainChain <- deserialize-    blockDataWork <- getInteger-    blockDataHeader <- deserialize-    blockDataSize <- getWord32be-    blockDataWeight <- getWord32be-    blockDataTxs <- getList deserialize-    blockDataOutputs <- getWord64be-    blockDataFees <- getWord64be-    blockDataSubsidy <- getWord64be-    return BlockData {..}--instance Serialize BlockData where-  put = serialize-  get = deserialize--instance Binary BlockData where-  put = serialize-  get = deserialize--blockDataToJSON :: Network -> BlockData -> Value-blockDataToJSON net bv =-  A.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 =-  AE.pairs $-    "hash"-      `AE.pair` AE.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"-        `AE.pair` AE.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, Hashable, NFData)--instance Serial StoreInput where-  serialize StoreCoinbase {..} = do-    putWord8 0x00-    serialize inputPoint-    putWord32be inputSequence-    putLengthBytes inputSigScript-    putList putLengthBytes inputWitness-  serialize StoreInput {..} = do-    putWord8 0x01-    serialize inputPoint-    putWord32be inputSequence-    putLengthBytes inputSigScript-    putLengthBytes inputPkScript-    putWord64be inputAmount-    putList putLengthBytes inputWitness-    putMaybe serialize inputAddress--  deserialize =-    getWord8 >>= \case-      0x00 -> do-        inputPoint <- deserialize-        inputSequence <- getWord32be-        inputSigScript <- getLengthBytes-        inputWitness <- getList getLengthBytes-        return StoreCoinbase {..}-      0x01 -> do-        inputPoint <- deserialize-        inputSequence <- getWord32be-        inputSigScript <- getLengthBytes-        inputPkScript <- getLengthBytes-        inputAmount <- getWord64be-        inputWitness <- getList getLengthBytes-        inputAddress <- getMaybe deserialize-        return StoreInput {..}-      x -> fail $ "Unknown input id: " <> cs (show x)--instance Serialize StoreInput where-  put = serialize-  get = deserialize--instance Binary StoreInput where-  put = serialize-  get = deserialize--isCoinbaseTx :: Tx -> Bool-isCoinbaseTx = all ((== nullOutPoint) . prevOutput) . txIn--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-    } =-    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-  _-  StoreCoinbase-    { inputPoint = OutPoint oph opi,-      inputSequence = sq,-      inputSigScript = ss,-      inputWitness = wit-    } =-    A.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-    } =-    AE.pairs $-      "coinbase" .= False-        <> "txid" .= oph-        <> "output" .= opi-        <> "sigscript" `AE.pair` AE.text (encodeHex ss)-        <> "sequence" .= sq-        <> "pkscript" `AE.pair` AE.text (encodeHex ps)-        <> "value" .= val-        <> "address" `AE.pair` maybe AE.null_ (addrToEncoding net) a-        <> "witness" .= map encodeHex wit-storeInputToEncoding-  _-  StoreCoinbase-    { inputPoint = OutPoint oph opi,-      inputSequence = sq,-      inputSigScript = ss,-      inputWitness = wit-    } =-    AE.pairs $-      "coinbase" .= True-        <> "txid" `AE.pair` AE.text (txHashToHex oph)-        <> "output" .= opi-        <> "sigscript" `AE.pair` AE.text (encodeHex ss)-        <> "sequence" .= sq-        <> "pkscript" `AE.pair` AE.null_-        <> "value" `AE.pair` AE.null_-        <> "address" `AE.pair` AE.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-  { -- | input transaction hash-    spenderHash :: !TxHash,-    -- | input position in transaction-    spenderIndex :: !Word32-  }-  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)--instance Serial Spender where-  serialize Spender {..} = do-    serialize spenderHash-    putWord32be spenderIndex-  deserialize = Spender <$> deserialize <*> getWord32be--instance Serialize Spender where-  put = serialize-  get = deserialize--instance Binary Spender where-  put = serialize-  get = deserialize--instance ToJSON Spender where-  toJSON n =-    A.object-      [ "txid" .= txHashToHex (spenderHash n),-        "input" .= spenderIndex n-      ]-  toEncoding n =-    AE.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),-    outputAddr :: !(Maybe Address)-  }-  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)--instance Serial StoreOutput where-  serialize StoreOutput {..} = do-    putWord64be outputAmount-    putLengthBytes outputScript-    putMaybe serialize outputSpender-    putMaybe serialize outputAddr-  deserialize = do-    outputAmount <- getWord64be-    outputScript <- getLengthBytes-    outputSpender <- getMaybe deserialize-    outputAddr <- getMaybe deserialize-    return StoreOutput {..}--instance Serialize StoreOutput where-  put = serialize-  get = deserialize--instance Binary StoreOutput where-  put = serialize-  get = deserialize--storeOutputToJSON :: Network -> StoreOutput -> Value-storeOutputToJSON net d =-  A.object-    [ "address" .= (addrToJSON net <$> outputAddr d),-      "pkscript" .= encodeHex (outputScript d),-      "value" .= outputAmount d,-      "spent" .= isJust (outputSpender d),-      "spender" .= outputSpender d-    ]--storeOutputToEncoding :: Network -> StoreOutput -> Encoding-storeOutputToEncoding net d =-  AE.pairs $-    "address" `AE.pair` maybe AE.null_ (addrToEncoding net) (outputAddr d)-      <> "pkscript" `AE.pair` AE.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,-          outputAddr = addr-        }--data Prev = Prev-  { prevScript :: !ByteString,-    prevAmount :: !Word64-  }-  deriving (Show, Eq, Ord, Generic, Hashable, NFData)--instance Serial Prev where-  serialize Prev {..} = do-    putLengthBytes prevScript-    putWord64be prevAmount-  deserialize = do-    prevScript <- getLengthBytes-    prevAmount <- getWord64be-    return Prev {..}--instance Binary Prev where-  put = serialize-  get = deserialize--instance Serialize Prev where-  put = serialize-  get = deserialize--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,-      outputAddr = eitherToMaybe (scriptToAddressBS (scriptOutput o))-    }--data TxData = TxData-  { txDataBlock :: !BlockRef,-    txData :: !Tx,-    txDataPrevs :: !(IntMap Prev),-    txDataDeleted :: !Bool,-    txDataRBF :: !Bool,-    txDataTime :: !Word64,-    txDataSpenders :: !(IntMap Spender)-  }-  deriving (Show, Eq, Ord, Generic, NFData)--instance Serial TxData where-  serialize TxData {..} = do-    serialize txDataBlock-    serialize txData-    putIntMap (putWord64be . fromIntegral) serialize txDataPrevs-    serialize txDataDeleted-    serialize txDataRBF-    putWord64be txDataTime-    putIntMap (putWord64be . fromIntegral) serialize txDataSpenders-  deserialize = do-    txDataBlock <- deserialize-    txData <- deserialize-    txDataPrevs <- getIntMap (fromIntegral <$> getWord64be) deserialize-    txDataDeleted <- deserialize-    txDataRBF <- deserialize-    txDataTime <- getWord64be-    txDataSpenders <- getIntMap (fromIntegral <$> getWord64be) deserialize-    return TxData {..}--instance Serialize TxData where-  put = serialize-  get = deserialize--instance Binary TxData where-  put = serialize-  get = deserialize--txDataFee :: TxData -> Word64-txDataFee TxData {..} =-  if isCoinbaseTx txData then 0 else inputs - outputs-  where-    inputs = sum . map prevAmount $ IntMap.elems txDataPrevs-    outputs = sum . map outValue $ txOut txData--toTransaction :: TxData -> Transaction-toTransaction t =-  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 $ BS.length (runPutS (serialize (txData t)))-    txweight =-      let b = BS.length $ runPutS $ serialize (txData t) {txWitness = []}-          x = BS.length $ runPutS $ serialize (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 (txDataSpenders t))-    outs = zipWith g [0 ..] (txOut (txData t))--fromTransaction :: Transaction -> TxData-fromTransaction t =-  TxData-    { txDataBlock = transactionBlock t,-      txData = transactionData t,-      txDataPrevs = ps,-      txDataDeleted = transactionDeleted t,-      txDataRBF = transactionRBF t,-      txDataTime = transactionTime t,-      txDataSpenders = sm-    }-  where-    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-  { -- | block information for this transaction-    transactionBlock :: !BlockRef,-    -- | transaction version-    transactionVersion :: !Word32,-    -- | lock time-    transactionLockTime :: !Word32,-    -- | transaction inputs-    transactionInputs :: ![StoreInput],-    -- | transaction outputs-    transactionOutputs :: ![StoreOutput],-    -- | this transaction has been deleted and is no longer valid-    transactionDeleted :: !Bool,-    -- | this transaction can be replaced in the mempool-    transactionRBF :: !Bool,-    -- | time the transaction was first seen or time of block-    transactionTime :: !Word64,-    -- | transaction id-    transactionId :: !TxHash,-    -- | serialized transaction size (includes witness data)-    transactionSize :: !Word32,-    -- | transaction weight-    transactionWeight :: !Word32,-    -- | fees that this transaction pays (0 for coinbase)-    transactionFees :: !Word64-  }-  deriving (Show, Eq, Ord, Generic, Hashable, NFData)--instance Serial Transaction where-  serialize Transaction {..} = do-    serialize transactionBlock-    putWord32be transactionVersion-    putWord32be transactionLockTime-    putList serialize transactionInputs-    putList serialize transactionOutputs-    serialize transactionDeleted-    serialize transactionRBF-    putWord64be transactionTime-    serialize transactionId-    putWord32be transactionSize-    putWord32be transactionWeight-    putWord64be transactionFees-  deserialize = do-    transactionBlock <- deserialize-    transactionVersion <- getWord32be-    transactionLockTime <- getWord32be-    transactionInputs <- getList deserialize-    transactionOutputs <- getList deserialize-    transactionDeleted <- deserialize-    transactionRBF <- deserialize-    transactionTime <- getWord64be-    transactionId <- deserialize-    transactionSize <- getWord32be-    transactionWeight <- getWord32be-    transactionFees <- getWord64be-    return Transaction {..}--instance Serialize Transaction where-  put = serialize-  get = deserialize--instance Binary Transaction where-  put = serialize-  get = deserialize--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 =-  A.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 =-  AE.pairs $-    "txid" .= transactionId dtx-      <> "size" .= transactionSize dtx-      <> "version" .= transactionVersion dtx-      <> "locktime" .= transactionLockTime dtx-      <> "fee" .= transactionFees dtx-      <> "inputs"-        `AE.pair` AE.list-          (storeInputToEncoding net)-          (transactionInputs dtx)-      <> "outputs"-        `AE.pair` AE.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-  { -- | user agent string-    peerUserAgent :: !ByteString,-    -- | network address-    peerAddress :: !String,-    -- | version number-    peerVersion :: !Word32,-    -- | services field-    peerServices :: !Word64,-    -- | will relay transactions-    peerRelay :: !Bool-  }-  deriving (Show, Eq, Ord, Generic, NFData)--instance Serial PeerInformation where-  serialize PeerInformation {..} = do-    putLengthBytes peerUserAgent-    putLengthBytes (TE.encodeUtf8 (T.pack peerAddress))-    putWord32be peerVersion-    putWord64be peerServices-    serialize peerRelay-  deserialize = do-    peerUserAgent <- getLengthBytes-    peerAddress <- T.unpack . TE.decodeUtf8 <$> getLengthBytes-    peerVersion <- getWord32be-    peerServices <- getWord64be-    peerRelay <- deserialize-    return PeerInformation {..}--instance Serialize PeerInformation where-  put = serialize-  get = deserialize--instance Binary PeerInformation where-  put = serialize-  get = deserialize--instance ToJSON PeerInformation where-  toJSON p =-    A.object-      [ "useragent" .= String (cs (peerUserAgent p)),-        "address" .= peerAddress p,-        "version" .= peerVersion p,-        "services"-          .= String (encodeHex (runPutS (serialize (peerServices p)))),-        "relay" .= peerRelay p-      ]-  toEncoding p =-    AE.pairs $-      "useragent" `AE.pair` AE.text (cs (peerUserAgent p))-        <> "address" .= peerAddress p-        <> "version" .= peerVersion p-        <> "services"-          `AE.pair` AE.text-            (encodeHex (runPutS (serialize (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 runGetS deserialize 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, NFData)--instance Serial XPubBal where-  serialize XPubBal {..} = do-    putList putWord32be xPubBalPath-    serialize xPubBal-  deserialize = do-    xPubBalPath <- getList getWord32be-    xPubBal <- deserialize-    return XPubBal {..}--instance Serialize XPubBal where-  put = serialize-  get = deserialize--instance Binary XPubBal where-  put = serialize-  get = deserialize--xPubBalToJSON :: Network -> XPubBal -> Value-xPubBalToJSON net XPubBal {xPubBalPath = p, xPubBal = b} =-  A.object ["path" .= p, "balance" .= balanceToJSON net b]--xPubBalToEncoding :: Network -> XPubBal -> Encoding-xPubBalToEncoding net XPubBal {xPubBalPath = p, xPubBal = b} =-  AE.pairs ("path" .= p <> "balance" `AE.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-  { xPubUnspent :: !Unspent,-    xPubUnspentPath :: ![KeyIndex]-  }-  deriving (Show, Eq, Ord, Generic, NFData)--instance Serial XPubUnspent where-  serialize XPubUnspent {..} = do-    putList putWord32be xPubUnspentPath-    serialize xPubUnspent-  deserialize = do-    xPubUnspentPath <- getList getWord32be-    xPubUnspent <- deserialize-    return XPubUnspent {..}--instance Serialize XPubUnspent where-  put = serialize-  get = deserialize--instance Binary XPubUnspent where-  put = serialize-  get = deserialize--xPubUnspentToJSON :: Network -> XPubUnspent -> Value-xPubUnspentToJSON net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} =-  A.object ["unspent" .= unspentToJSON net u, "path" .= p]--xPubUnspentToEncoding :: Network -> XPubUnspent -> Encoding-xPubUnspentToEncoding net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} =-  AE.pairs $ "unspent" `AE.pair` unspentToEncoding net u <> "path" .= p--xPubUnspentParseJSON :: Network -> Value -> Parser XPubUnspent-xPubUnspentParseJSON net =-  A.withObject "xpubunspent" $ \o -> do-    u <- o .: "unspent" >>= unspentParseJSON net-    p <- o .: "path"-    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, NFData)--instance Serial XPubSummary where-  serialize XPubSummary {..} = do-    putWord64be xPubSummaryConfirmed-    putWord64be xPubSummaryZero-    putWord64be xPubSummaryReceived-    putWord64be xPubUnspentCount-    putWord32be xPubExternalIndex-    putWord32be xPubChangeIndex-  deserialize = do-    xPubSummaryConfirmed <- getWord64be-    xPubSummaryZero <- getWord64be-    xPubSummaryReceived <- getWord64be-    xPubUnspentCount <- getWord64be-    xPubExternalIndex <- getWord32be-    xPubChangeIndex <- getWord32be-    return XPubSummary {..}--instance Binary XPubSummary where-  put = serialize-  get = deserialize--instance Serialize XPubSummary where-  put = serialize-  get = deserialize--instance ToJSON XPubSummary where-  toJSON-    XPubSummary-      { xPubSummaryConfirmed = c,-        xPubSummaryZero = z,-        xPubSummaryReceived = r,-        xPubUnspentCount = u,-        xPubExternalIndex = ext,-        xPubChangeIndex = ch-      } =-      A.object-        [ "balance"-            .= A.object-              [ "confirmed" .= c,-                "unconfirmed" .= z,-                "received" .= r,-                "utxo" .= u-              ],-          "indices"-            .= A.object-              [ "change" .= ch,-                "external" .= ext-              ]-        ]-  toEncoding-    XPubSummary-      { xPubSummaryConfirmed = c,-        xPubSummaryZero = z,-        xPubSummaryReceived = r,-        xPubUnspentCount = u,-        xPubExternalIndex = ext,-        xPubChangeIndex = ch-      } =-      AE.pairs $-        "balance"-          `AE.pair` AE.pairs-            ( "confirmed" .= c-                <> "unconfirmed" .= z-                <> "received" .= r-                <> "utxo" .= u-            )-          <> "indices"-            `AE.pair` AE.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 :: !Int32-  }-  deriving (Show, Eq, Generic, NFData)--instance Serial BlockHealth where-  serialize h@BlockHealth {..} = do-    serialize (isOK h)-    putWord32be blockHealthHeaders-    putWord32be blockHealthBlocks-    putInt32be blockHealthMaxDiff-  deserialize = do-    k <- deserialize-    blockHealthHeaders <- getWord32be-    blockHealthBlocks <- getWord32be-    blockHealthMaxDiff <- getInt32be-    let h = BlockHealth {..}-    unless (k == isOK h) $ fail "Inconsistent health check"-    return h--instance Serialize BlockHealth where-  put = serialize-  get = deserialize--instance Binary BlockHealth where-  put = serialize-  get = deserialize--instance Healthy BlockHealth where-  isOK BlockHealth {..} =-    h - b <= blockHealthMaxDiff-    where-      h = fromIntegral blockHealthHeaders-      b = fromIntegral blockHealthBlocks--instance ToJSON BlockHealth where-  toJSON h@BlockHealth {..} =-    A.object-      [ "headers" .= blockHealthHeaders,-        "blocks" .= blockHealthBlocks,-        "diff" .= diff,-        "max" .= blockHealthMaxDiff,-        "ok" .= isOK h-      ]-    where-      diff = toInteger blockHealthHeaders - toInteger 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 :: !Int64,-    timeHealthMax :: !Int64-  }-  deriving (Show, Eq, Generic, NFData)--instance Serial TimeHealth where-  serialize h@TimeHealth {..} = do-    serialize (isOK h)-    putInt64be timeHealthAge-    putInt64be timeHealthMax-  deserialize = do-    k <- deserialize-    timeHealthAge <- getInt64be-    timeHealthMax <- getInt64be-    let t = TimeHealth {..}-    unless (k == isOK t) $ fail "Inconsistent health check"-    return t--instance Binary TimeHealth where-  put = serialize-  get = deserialize--instance Serialize TimeHealth where-  put = serialize-  get = deserialize--instance Healthy TimeHealth where-  isOK TimeHealth {..} =-    timeHealthAge <= timeHealthMax--instance ToJSON TimeHealth where-  toJSON h@TimeHealth {..} =-    A.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 :: !Int64,-    countHealthMin :: !Int64-  }-  deriving (Show, Eq, Generic, NFData)--instance Serial CountHealth where-  serialize h@CountHealth {..} = do-    serialize (isOK h)-    putInt64be countHealthNum-    putInt64be countHealthMin-  deserialize = do-    k <- deserialize-    countHealthNum <- getInt64be-    countHealthMin <- getInt64be-    let c = CountHealth {..}-    unless (k == isOK c) $ fail "Inconsistent health check"-    return c--instance Serialize CountHealth where-  put = serialize-  get = deserialize--instance Binary CountHealth where-  put = serialize-  get = deserialize--instance Healthy CountHealth where-  isOK CountHealth {..} =-    countHealthMin <= countHealthNum--instance ToJSON CountHealth where-  toJSON h@CountHealth {..} =-    A.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 :: !Int64,-    maxHealthMax :: !Int64-  }-  deriving (Show, Eq, Generic, NFData)--instance Serial MaxHealth where-  serialize h@MaxHealth {..} = do-    serialize $ isOK h-    putInt64be maxHealthNum-    putInt64be maxHealthMax-  deserialize = do-    k <- deserialize-    maxHealthNum <- getInt64be-    maxHealthMax <- getInt64be-    let h = MaxHealth {..}-    unless (k == isOK h) $ fail "Inconsistent health check"-    return h--instance Binary MaxHealth where-  put = serialize-  get = deserialize--instance Serialize MaxHealth where-  put = serialize-  get = deserialize--instance Healthy MaxHealth where-  isOK MaxHealth {..} = maxHealthNum <= maxHealthMax--instance ToJSON MaxHealth where-  toJSON h@MaxHealth {..} =-    A.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 Serial HealthCheck where-  serialize h@HealthCheck {..} = do-    serialize (isOK h)-    serialize healthBlocks-    serialize healthLastBlock-    serialize healthLastTx-    serialize healthPendingTxs-    serialize healthPeers-    putLengthBytes ((TE.encodeUtf8 . T.pack) healthNetwork)-    putLengthBytes ((TE.encodeUtf8 . T.pack) healthVersion)-  deserialize = do-    k <- deserialize-    healthBlocks <- deserialize-    healthLastBlock <- deserialize-    healthLastTx <- deserialize-    healthPendingTxs <- deserialize-    healthPeers <- deserialize-    healthNetwork <- T.unpack . TE.decodeUtf8 <$> getLengthBytes-    healthVersion <- T.unpack . TE.decodeUtf8 <$> getLengthBytes-    let h = HealthCheck {..}-    unless (k == isOK h) $ fail "Inconsistent health check"-    return h--instance Binary HealthCheck where-  put = serialize-  get = deserialize--instance Serialize HealthCheck where-  put = serialize-  get = deserialize--instance Healthy HealthCheck where-  isOK HealthCheck {..} =-    isOK healthBlocks-      && isOK healthLastBlock-      && isOK healthLastTx-      && isOK healthPendingTxs-      && isOK healthPeers--instance ToJSON HealthCheck where-  toJSON h@HealthCheck {..} =-    A.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, NFData)--instance Serial Event where-  serialize (EventBlock bh) = putWord8 0x00 >> serialize bh-  serialize (EventTx th) = putWord8 0x01 >> serialize th-  deserialize =-    getWord8 >>= \case-      0x00 -> EventBlock <$> deserialize-      0x01 -> EventTx <$> deserialize-      _ -> fail "Not an Event"--instance Serialize Event where-  put = serialize-  get = deserialize--instance Binary Event where-  put = serialize-  get = deserialize--instance ToJSON Event where-  toJSON (EventTx h) =-    A.object-      [ "type" .= String "tx",-        "id" .= h-      ]-  toJSON (EventBlock h) =-    A.object-      [ "type" .= String "block",-        "id" .= h-      ]-  toEncoding (EventTx h) =-    AE.pairs $-      "type" `AE.pair` AE.text "tx"-        <> "id" `AE.pair` AE.text (txHashToHex h)-  toEncoding (EventBlock h) =-    AE.pairs $-      "type" `AE.pair` AE.text "block"-        <> "id" `AE.pair` AE.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, NFData)--instance Serial a => Serial (GenericResult a) where-  serialize (GenericResult x) = serialize x-  deserialize = GenericResult <$> deserialize--instance Serial a => Serialize (GenericResult a) where-  put = serialize-  get = deserialize--instance Serial a => Binary (GenericResult a) where-  put = serialize-  get = deserialize--instance ToJSON a => ToJSON (GenericResult a) where-  toJSON (GenericResult b) = A.object ["result" .= b]-  toEncoding (GenericResult b) = AE.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, NFData)--instance Serial a => Serial (RawResult a) where-  serialize (RawResult x) = serialize x-  deserialize = RawResult <$> deserialize--instance Serial a => Serialize (RawResult a) where-  put = serialize-  get = deserialize--instance Serial a => Binary (RawResult a) where-  put = serialize-  get = deserialize--instance Serial a => ToJSON (RawResult a) where-  toJSON (RawResult b) =-    A.object ["result" .= x b]-    where-      x =-        TLE.decodeUtf8-          . BSB.toLazyByteString-          . BSB.lazyByteStringHex-          . runPutL-          . serialize-  toEncoding (RawResult b) =-    AE.pairs $ "result" `AE.pair` AE.unsafeToEncoding str-    where-      str =-        BSB.char7 '"'-          <> BSB.lazyByteStringHex (runPutL (serialize b))-          <> BSB.char7 '"'--instance Serial a => FromJSON (RawResult a) where-  parseJSON =-    A.withObject "RawResult" $ \o -> do-      res <- o .: "result"-      let m =-            eitherToMaybe . Bytes.Get.runGetS deserialize-              =<< decodeHex res-      maybe mzero (return . RawResult) m--newtype SerialList a = SerialList {getSerialList :: [a]}-  deriving (Show, Eq, Generic, NFData)--instance Semigroup (SerialList a) where-  SerialList a <> SerialList b = SerialList (a <> b)--instance Monoid (SerialList a) where-  mempty = SerialList mempty--instance Serial a => Serial (SerialList a) where-  serialize (SerialList ls) = putList serialize ls-  deserialize = SerialList <$> getList deserialize--instance ToJSON a => ToJSON (SerialList a) where-  toJSON (SerialList ls) = toJSON ls-  toEncoding (SerialList ls) = toEncoding ls--instance FromJSON a => FromJSON (SerialList a) where-  parseJSON = fmap SerialList . parseJSON--newtype RawResultList a = RawResultList-  { getRawResultList :: [a]-  }-  deriving (Show, Eq, Generic, NFData)--instance Serial a => Serial (RawResultList a) where-  serialize (RawResultList xs) =-    mapM_ serialize xs-  deserialize = RawResultList <$> go-    where-      go =-        isEmpty >>= \case-          True -> return []-          False -> (:) <$> deserialize <*> go--instance Serial a => Serialize (RawResultList a) where-  put = serialize-  get = deserialize--instance Serial a => Binary (RawResultList a) where-  put = serialize-  get = deserialize--instance Semigroup (RawResultList a) where-  (RawResultList a) <> (RawResultList b) = RawResultList $ a <> b--instance Monoid (RawResultList a) where-  mempty = RawResultList mempty--instance Serial a => ToJSON (RawResultList a) where-  toJSON (RawResultList xs) =-    toJSON $-      TLE.decodeUtf8-        . BSB.toLazyByteString-        . BSB.lazyByteStringHex-        . runPutL-        . serialize-        <$> xs-  toEncoding (RawResultList xs) =-    AE.list (AE.unsafeToEncoding . str) xs-    where-      str x =-        BSB.char7 '"'-          <> BSB.lazyByteStringHex (runPutL (serialize x))-          <> BSB.char7 '"'--instance Serial 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 . Bytes.Get.runGetS deserialize <=< decodeHex--newtype TxId-  = TxId TxHash-  deriving (Show, Eq, Generic, NFData)--instance Serial TxId where-  serialize (TxId h) = serialize h-  deserialize = TxId <$> deserialize--instance Serialize TxId where-  put = serialize-  get = deserialize--instance Binary TxId where-  put = serialize-  get = deserialize--instance ToJSON TxId where-  toJSON (TxId h) = A.object ["txid" .= h]-  toEncoding (TxId h) = AE.pairs ("txid" `AE.pair` AE.text (txHashToHex h))--instance FromJSON TxId where-  parseJSON = A.withObject "txid" $ \o -> TxId <$> o .: "txid"--data Except-  = ThingNotFound-  | ServerError-  | BadRequest-  | UserError !String-  | StringError !String-  | TxIndexConflict ![TxHash]-  | ServerTimeout-  | RequestTooLarge-  deriving (Show, Eq, Ord, Generic, NFData)--instance Exception Except--instance ScottyError Except where-  stringError = StringError-  showError = TL.pack . show--instance ToJSON Except where-  toJSON e =-    A.object $-      case e of-        ThingNotFound ->-          [ "error" .= String "not-found-or-invalid-arg",-            "message" .= String "Item not found or argument invalid"-          ]-        ServerError ->-          [ "error" .= String "server-error",-            "message" .= String "Server error"-          ]-        BadRequest ->-          [ "error" .= String "bad-request",-            "message" .= String "Invalid request"-          ]-        UserError msg' ->-          [ "error" .= String "user-error",-            "message" .= String (cs msg')-          ]-        StringError msg' ->-          [ "error" .= String "string-error",-            "message" .= String (cs msg')-          ]-        TxIndexConflict txids ->-          [ "error" .= String "multiple-tx-index",-            "message" .= String "Multiple txs match that index",-            "txids" .= txids-          ]-        ServerTimeout ->-          [ "error" .= String "server-timeout",-            "message" .= String "Request is taking too long"-          ]-        RequestTooLarge ->-          [ "error" .= String "request-too-large",-            "message" .= String "Request body too large"-          ]--instance FromJSON Except where-  parseJSON =-    A.withObject "Except" $ \o -> do-      ctr <- o .: "error"-      msg' <- o .:? "message" .!= ""-      case ctr of-        String "not-found-or-invalid-arg" ->-          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 "multiple-tx-index" -> do-          txids <- o .: "txids"-          return $ TxIndexConflict txids-        String "server-timeout" ->-          return ServerTimeout-        String "request-too-large" ->-          return RequestTooLarge-        _ -> mzero--toIntTxId :: TxHash -> Word64-toIntTxId h =-  let bs = runPutS (serialize h)-      Right w64 = runGetS getWord64be bs-   in w64 `shift` (-11)-------------------------------------------- Blockchain.info API Compatibility --------------------------------------------data BinfoBlockId-  = BinfoBlockHash !BlockHash-  | BinfoBlockIndex !Word32-  deriving (Eq, Show, Read, Generic, NFData)--instance Parsable BinfoBlockId where-  parseParam t =-    hex <> igr-    where-      hex = case hexToBlockHash (TL.toStrict t) of-        Nothing -> Left "could not decode txid"-        Just h -> Right $ BinfoBlockHash h-      igr = BinfoBlockIndex <$> parseParam t--data BinfoTxId-  = BinfoTxIdHash !TxHash-  | BinfoTxIdIndex !Word64-  deriving (Eq, Show, Read, Generic, NFData)--encodeBinfoTxId :: Bool -> TxHash -> BinfoTxId-encodeBinfoTxId False = BinfoTxIdHash-encodeBinfoTxId True = BinfoTxIdIndex . toIntTxId--instance Parsable BinfoTxId where-  parseParam t =-    hex <> igr-    where-      hex =-        case hexToTxHash (TL.toStrict t) of-          Nothing -> Left "could not decode txid"-          Just h -> Right $ BinfoTxIdHash h-      igr = BinfoTxIdIndex <$> parseParam 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 BinfoFilter-  = BinfoFilterAll-  | BinfoFilterSent-  | BinfoFilterReceived-  | BinfoFilterMoved-  | BinfoFilterConfirmed-  | BinfoFilterMempool-  deriving (Eq, Show, Generic, NFData)--instance Parsable BinfoFilter where-  parseParam t =-    parseParam t >>= \case-      (0 :: Int) -> return BinfoFilterAll-      1 -> return BinfoFilterSent-      2 -> return BinfoFilterReceived-      3 -> return BinfoFilterMoved-      5 -> return BinfoFilterConfirmed-      6 -> return BinfoFilterAll-      7 -> return BinfoFilterMempool-      _ -> Left "could not parse filter parameter"--data BinfoMultiAddr = BinfoMultiAddr-  { getBinfoMultiAddrAddresses :: ![BinfoBalance],-    getBinfoMultiAddrWallet :: !BinfoWallet,-    getBinfoMultiAddrTxs :: ![BinfoTx],-    getBinfoMultiAddrInfo :: !BinfoInfo,-    getBinfoMultiAddrRecommendFee :: !Bool,-    getBinfoMultiAddrCashAddr :: !Bool-  }-  deriving (Eq, Show, Generic, NFData)--binfoMultiAddrToJSON :: Network -> BinfoMultiAddr -> Value-binfoMultiAddrToJSON net' BinfoMultiAddr {..} =-  A.object $-    [ "addresses" .= map (binfoBalanceToJSON 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 = A.withObject "multiaddr" $ \o -> do-  getBinfoMultiAddrAddresses <--    mapM (binfoBalanceParseJSON 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 {..} =-  AE.pairs-    ( "addresses" `AE.pair` as-        <> "wallet" .= getBinfoMultiAddrWallet-        <> "txs" `AE.pair` ts-        <> "info" .= getBinfoMultiAddrInfo-        <> "recommend_include_fee" .= getBinfoMultiAddrRecommendFee-        <> if getBinfoMultiAddrCashAddr then "cash_addr" .= True else mempty-    )-  where-    as = AE.list (binfoBalanceToEncoding net) getBinfoMultiAddrAddresses-    ts = AE.list (binfoTxToEncoding net) getBinfoMultiAddrTxs-    net = if not getBinfoMultiAddrCashAddr && net' == bch then btc else net'--data BinfoRawAddr = BinfoRawAddr-  { binfoRawAddr :: !BinfoAddr,-    binfoRawBalance :: !Word64,-    binfoRawTxCount :: !Word64,-    binfoRawUnredeemed :: !Word64,-    binfoRawReceived :: !Word64,-    binfoRawSent :: !Int64,-    binfoRawTxs :: ![BinfoTx]-  }-  deriving (Eq, Show, Generic, NFData)--binfoRawAddrToJSON :: Network -> BinfoRawAddr -> Value-binfoRawAddrToJSON net BinfoRawAddr {..} =-  A.object-    [ "hash160" .= h160,-      "address" .= address,-      "n_tx" .= binfoRawTxCount,-      "n_unredeemed" .= binfoRawUnredeemed,-      "total_received" .= binfoRawReceived,-      "total_sent" .= binfoRawSent,-      "final_balance" .= binfoRawBalance,-      "txs" .= map (binfoTxToJSON net) binfoRawTxs-    ]-  where-    address = case binfoRawAddr of-      BinfoAddr a -> addrToJSON net a-      BinfoXpub x -> xPubToJSON net x-    h160 =-      encodeHex . runPutS . serialize-        <$> case binfoRawAddr of-          BinfoAddr a -> case a of-            PubKeyAddress h -> Just h-            ScriptAddress h -> Just h-            WitnessPubKeyAddress h -> Just h-            _ -> Nothing-          _ -> Nothing--binfoRawAddrToEncoding :: Network -> BinfoRawAddr -> Encoding-binfoRawAddrToEncoding net BinfoRawAddr {..} =-  AE.pairs $-    "hash160" .= h160-      <> "address" `AE.pair` address-      <> "n_tx" .= binfoRawTxCount-      <> "n_unredeemed" .= binfoRawUnredeemed-      <> "total_received" .= binfoRawReceived-      <> "total_sent" .= binfoRawSent-      <> "final_balance" .= binfoRawBalance-      <> "txs" `AE.pair` AE.list (binfoTxToEncoding net) binfoRawTxs-  where-    address = case binfoRawAddr of-      BinfoAddr a -> addrToEncoding net a-      BinfoXpub x -> xPubToEncoding net x-    h160 =-      encodeHex . runPutS . serialize-        <$> case binfoRawAddr of-          BinfoAddr a -> case a of-            PubKeyAddress h -> Just h-            ScriptAddress h -> Just h-            WitnessPubKeyAddress h -> Just h-            _ -> Nothing-          _ -> Nothing--binfoRawAddrParseJSON :: Network -> Value -> Parser BinfoRawAddr-binfoRawAddrParseJSON net = A.withObject "balancetxs" $ \o -> do-  addr <- o .: "address"-  binfoRawAddr <--    BinfoAddr <$> addrFromJSON net addr-      <|> BinfoXpub <$> xPubFromJSON net addr-  binfoRawBalance <- o .: "final_balance"-  binfoRawUnredeemed <- o .: "n_unredeemed"-  binfoRawTxCount <- o .: "n_tx"-  binfoRawReceived <- o .: "total_received"-  binfoRawSent <- o .: "total_sent"-  binfoRawTxs <- mapM (binfoTxParseJSON net) =<< o .: "txs"-  return BinfoRawAddr {..}--data BinfoShortBal = BinfoShortBal-  { binfoShortBalFinal :: !Word64,-    binfoShortBalTxCount :: !Word64,-    binfoShortBalReceived :: !Word64-  }-  deriving (Eq, Show, Read, Generic, NFData)--instance ToJSON BinfoShortBal where-  toJSON BinfoShortBal {..} =-    A.object-      [ "final_balance" .= binfoShortBalFinal,-        "n_tx" .= binfoShortBalTxCount,-        "total_received" .= binfoShortBalReceived-      ]-  toEncoding BinfoShortBal {..} =-    AE.pairs $-      "final_balance" .= binfoShortBalFinal-        <> "n_tx" .= binfoShortBalTxCount-        <> "total_received" .= binfoShortBalReceived--instance FromJSON BinfoShortBal where-  parseJSON = A.withObject "short_balance" $ \o -> do-    binfoShortBalFinal <- o .: "final_balance"-    binfoShortBalTxCount <- o .: "n_tx"-    binfoShortBalReceived <- o .: "total_received"-    return BinfoShortBal {..}--data BinfoBalance-  = BinfoAddrBalance-      { getBinfoAddress :: !Address,-        getBinfoAddrTxCount :: !Word64,-        getBinfoAddrReceived :: !Word64,-        getBinfoAddrSent :: !Word64,-        getBinfoAddrBalance :: !Word64-      }-  | BinfoXPubBalance-      { getBinfoXPubKey :: !XPubKey,-        getBinfoAddrTxCount :: !Word64,-        getBinfoAddrReceived :: !Word64,-        getBinfoAddrSent :: !Word64,-        getBinfoAddrBalance :: !Word64,-        getBinfoXPubAccountIndex :: !Word32,-        getBinfoXPubChangeIndex :: !Word32-      }-  deriving (Eq, Show, Generic, NFData)--binfoBalanceToJSON :: Network -> BinfoBalance -> Value-binfoBalanceToJSON net BinfoAddrBalance {..} =-  A.object-    [ "address" .= addrToJSON net getBinfoAddress,-      "final_balance" .= getBinfoAddrBalance,-      "n_tx" .= getBinfoAddrTxCount,-      "total_received" .= getBinfoAddrReceived,-      "total_sent" .= getBinfoAddrSent-    ]-binfoBalanceToJSON net BinfoXPubBalance {..} =-  A.object-    [ "address" .= xPubToJSON net getBinfoXPubKey,-      "change_index" .= getBinfoXPubChangeIndex,-      "account_index" .= getBinfoXPubAccountIndex,-      "final_balance" .= getBinfoAddrBalance,-      "n_tx" .= getBinfoAddrTxCount,-      "total_received" .= getBinfoAddrReceived,-      "total_sent" .= getBinfoAddrSent-    ]--binfoBalanceParseJSON :: Network -> Value -> Parser BinfoBalance-binfoBalanceParseJSON net = A.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 BinfoXPubBalance {..}-    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 BinfoAddrBalance {..}--binfoBalanceToEncoding :: Network -> BinfoBalance -> Encoding-binfoBalanceToEncoding net BinfoAddrBalance {..} =-  AE.pairs-    ( "address" `AE.pair` addrToEncoding net getBinfoAddress-        <> "final_balance" .= getBinfoAddrBalance-        <> "n_tx" .= getBinfoAddrTxCount-        <> "total_received" .= getBinfoAddrReceived-        <> "total_sent" .= getBinfoAddrSent-    )-binfoBalanceToEncoding net BinfoXPubBalance {..} =-  AE.pairs-    ( "address" `AE.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, NFData)--instance ToJSON BinfoWallet where-  toJSON BinfoWallet {..} =-    A.object-      [ "final_balance" .= getBinfoWalletBalance,-        "n_tx" .= getBinfoWalletTxCount,-        "n_tx_filtered" .= getBinfoWalletFilteredCount,-        "total_received" .= getBinfoWalletTotalReceived,-        "total_sent" .= getBinfoWalletTotalSent-      ]-  toEncoding BinfoWallet {..} =-    AE.pairs-      ( "final_balance" .= getBinfoWalletBalance-          <> "n_tx" .= getBinfoWalletTxCount-          <> "n_tx_filtered" .= getBinfoWalletFilteredCount-          <> "total_received" .= getBinfoWalletTotalReceived-          <> "total_sent" .= getBinfoWalletTotalSent-      )--instance FromJSON BinfoWallet where-  parseJSON = A.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 {..}--binfoHexValue :: Word64 -> Text-binfoHexValue w64 =-  let bs = BS.dropWhile (== 0x00) (runPutS (serialize w64))-   in encodeHex $-        if BS.null bs || BS.head bs `testBit` 7-          then BS.cons 0x00 bs-          else bs--data BinfoUnspent = BinfoUnspent-  { getBinfoUnspentHash :: !TxHash,-    getBinfoUnspentOutputIndex :: !Word32,-    getBinfoUnspentScript :: !ByteString,-    getBinfoUnspentValue :: !Word64,-    getBinfoUnspentConfirmations :: !Int32,-    getBinfoUnspentTxIndex :: !BinfoTxId,-    getBinfoUnspentXPub :: !(Maybe BinfoXPubPath)-  }-  deriving (Eq, Show, Generic, NFData)--binfoUnspentToJSON :: Network -> BinfoUnspent -> Value-binfoUnspentToJSON net BinfoUnspent {..} =-  A.object $-    [ "tx_hash_big_endian" .= getBinfoUnspentHash,-      "tx_hash"-        .= encodeHex (runPutS (serialize (getTxHash getBinfoUnspentHash))),-      "tx_output_n" .= getBinfoUnspentOutputIndex,-      "script" .= encodeHex getBinfoUnspentScript,-      "value" .= getBinfoUnspentValue,-      "value_hex" .= binfoHexValue getBinfoUnspentValue,-      "confirmations" .= getBinfoUnspentConfirmations,-      "tx_index" .= getBinfoUnspentTxIndex-    ]-      <> [ "xpub" .= binfoXPubPathToJSON net x-           | x <- maybeToList getBinfoUnspentXPub-         ]--binfoUnspentToEncoding :: Network -> BinfoUnspent -> Encoding-binfoUnspentToEncoding net BinfoUnspent {..} =-  AE.pairs $-    "tx_hash_big_endian" .= getBinfoUnspentHash-      <> "tx_hash"-      .= encodeHex (runPutS (serialize (getTxHash getBinfoUnspentHash)))-      <> "tx_output_n"-      .= getBinfoUnspentOutputIndex-      <> "script"-      .= encodeHex getBinfoUnspentScript-      <> "value"-      .= getBinfoUnspentValue-      <> "value_hex"-      .= binfoHexValue getBinfoUnspentValue-      <> "confirmations"-      .= getBinfoUnspentConfirmations-      <> "tx_index"-      .= getBinfoUnspentTxIndex-      <> maybe-        mempty-        (("xpub" `AE.pair`) . binfoXPubPathToEncoding net)-        getBinfoUnspentXPub--binfoUnspentParseJSON :: Network -> Value -> Parser BinfoUnspent-binfoUnspentParseJSON net = A.withObject "unspent" $ \o -> do-  getBinfoUnspentHash <- o .: "tx_hash_big_endian"-  getBinfoUnspentOutputIndex <- o .: "tx_output_n"-  getBinfoUnspentScript <- maybe mzero return . decodeHex =<< o .: "script"-  getBinfoUnspentValue <- o .: "value"-  getBinfoUnspentConfirmations <- o .: "confirmations"-  getBinfoUnspentTxIndex <- o .: "tx_index"-  getBinfoUnspentXPub <- mapM (binfoXPubPathParseJSON net) =<< o .:? "xpub"-  return BinfoUnspent {..}--newtype BinfoUnspents = BinfoUnspents [BinfoUnspent]-  deriving (Eq, Show, Generic, NFData)--binfoUnspentsToJSON :: Network -> BinfoUnspents -> Value-binfoUnspentsToJSON net (BinfoUnspents us) =-  A.object-    [ "notice" .= T.empty,-      "unspent_outputs" .= map (binfoUnspentToJSON net) us-    ]--binfoUnspentsToEncoding :: Network -> BinfoUnspents -> Encoding-binfoUnspentsToEncoding net (BinfoUnspents us) =-  AE.pairs $-    "notice" .= T.empty-      <> "unspent_outputs" `AE.pair` AE.list (binfoUnspentToEncoding net) us--binfoUnspentsParseJSON :: Network -> Value -> Parser BinfoUnspents-binfoUnspentsParseJSON net = A.withObject "unspents" $ \o -> do-  us <- mapM (binfoUnspentParseJSON net) =<< o .: "unspent_outputs"-  return (BinfoUnspents us)--binfoBlocksToJSON :: Network -> [BinfoBlock] -> Value-binfoBlocksToJSON net blocks =-  A.object ["blocks" .= map (binfoBlockToJSON net) blocks]--binfoBlocksToEncoding :: Network -> [BinfoBlock] -> Encoding-binfoBlocksToEncoding net blocks =-  AE.pairs $ "blocks" `AE.pair` AE.list (binfoBlockToEncoding net) blocks--binfoBlocksParseJSON :: Network -> Value -> Parser [BinfoBlock]-binfoBlocksParseJSON net =-  A.withObject "blocks" $ \o ->-    mapM (binfoBlockParseJSON net) =<< o .: "blocks"--toBinfoBlock :: BlockData -> [BinfoTx] -> [BlockHash] -> BinfoBlock-toBinfoBlock BlockData {..} txs next_blocks =-  BinfoBlock-    { getBinfoBlockHash = headerHash blockDataHeader,-      getBinfoBlockVer = blockVersion blockDataHeader,-      getBinfoPrevBlock = prevBlock blockDataHeader,-      getBinfoMerkleRoot = merkleRoot blockDataHeader,-      getBinfoBlockTime = blockTimestamp blockDataHeader,-      getBinfoBlockBits = blockBits blockDataHeader,-      getBinfoNextBlock = next_blocks,-      getBinfoBlockFee = blockDataFees,-      getBinfoBlockNonce = bhNonce blockDataHeader,-      getBinfoBlockTxCount = fromIntegral (length txs),-      getBinfoBlockSize = blockDataSize,-      getBinfoBlockIndex = blockDataHeight,-      getBinfoBlockMain = blockDataMainChain,-      getBinfoBlockHeight = blockDataHeight,-      getBinfoBlockWeight = blockDataWeight,-      getBinfoBlockTx = txs-    }--data BinfoBlock = BinfoBlock-  { getBinfoBlockHash :: !BlockHash,-    getBinfoBlockVer :: !Word32,-    getBinfoPrevBlock :: !BlockHash,-    getBinfoMerkleRoot :: !Hash256,-    getBinfoBlockTime :: !Word32,-    getBinfoBlockBits :: !Word32,-    getBinfoNextBlock :: ![BlockHash],-    getBinfoBlockFee :: !Word64,-    getBinfoBlockNonce :: !Word32,-    getBinfoBlockTxCount :: !Word32,-    getBinfoBlockSize :: !Word32,-    getBinfoBlockIndex :: !Word32,-    getBinfoBlockMain :: !Bool,-    getBinfoBlockHeight :: !Word32,-    getBinfoBlockWeight :: !Word32,-    getBinfoBlockTx :: ![BinfoTx]-  }-  deriving (Eq, Show, Generic, NFData)--binfoBlockToJSON :: Network -> BinfoBlock -> Value-binfoBlockToJSON net BinfoBlock {..} =-  A.object-    [ "hash" .= getBinfoBlockHash,-      "ver" .= getBinfoBlockVer,-      "prev_block" .= getBinfoPrevBlock,-      "mrkl_root" .= TxHash getBinfoMerkleRoot,-      "time" .= getBinfoBlockTime,-      "bits" .= getBinfoBlockBits,-      "next_block" .= getBinfoNextBlock,-      "fee" .= getBinfoBlockFee,-      "nonce" .= getBinfoBlockNonce,-      "n_tx" .= getBinfoBlockTxCount,-      "size" .= getBinfoBlockSize,-      "block_index" .= getBinfoBlockIndex,-      "main_chain" .= getBinfoBlockMain,-      "height" .= getBinfoBlockHeight,-      "weight" .= getBinfoBlockWeight,-      "tx" .= map (binfoTxToJSON net) getBinfoBlockTx-    ]--binfoBlockToEncoding :: Network -> BinfoBlock -> Encoding-binfoBlockToEncoding net BinfoBlock {..} =-  AE.pairs $-    "hash" .= getBinfoBlockHash-      <> "ver" .= getBinfoBlockVer-      <> "prev_block" .= getBinfoPrevBlock-      <> "mrkl_root" .= TxHash getBinfoMerkleRoot-      <> "time" .= getBinfoBlockTime-      <> "bits" .= getBinfoBlockBits-      <> "next_block" .= getBinfoNextBlock-      <> "fee" .= getBinfoBlockFee-      <> "nonce" .= getBinfoBlockNonce-      <> "n_tx" .= getBinfoBlockTxCount-      <> "size" .= getBinfoBlockSize-      <> "block_index" .= getBinfoBlockIndex-      <> "main_chain" .= getBinfoBlockMain-      <> "height" .= getBinfoBlockHeight-      <> "weight" .= getBinfoBlockWeight-      <> "tx" `AE.pair` AE.list (binfoTxToEncoding net) getBinfoBlockTx--binfoBlockParseJSON :: Network -> Value -> Parser BinfoBlock-binfoBlockParseJSON net = A.withObject "block" $ \o -> do-  getBinfoBlockHash <- o .: "hash"-  getBinfoBlockVer <- o .: "ver"-  getBinfoPrevBlock <- o .: "prev_block"-  getBinfoMerkleRoot <- getTxHash <$> o .: "mrkl_root"-  getBinfoBlockTime <- o .: "time"-  getBinfoBlockBits <- o .: "bits"-  getBinfoNextBlock <- o .: "next_block"-  getBinfoBlockFee <- o .: "fee"-  getBinfoBlockNonce <- o .: "nonce"-  getBinfoBlockTxCount <- o .: "n_tx"-  getBinfoBlockSize <- o .: "size"-  getBinfoBlockIndex <- o .: "block_index"-  getBinfoBlockMain <- o .: "main_chain"-  getBinfoBlockHeight <- o .: "height"-  getBinfoBlockWeight <- o .: "weight"-  getBinfoBlockTx <- o .: "tx" >>= mapM (binfoTxParseJSON net)-  return BinfoBlock {..}--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,-    getBinfoTxRBF :: !Bool,-    getBinfoTxResultBal :: !(Maybe (Int64, Int64)),-    getBinfoTxTime :: !Word64,-    getBinfoTxBlockIndex :: !(Maybe Word32),-    getBinfoTxBlockHeight :: !(Maybe Word32),-    getBinfoTxInputs :: [BinfoTxInput],-    getBinfoTxOutputs :: [BinfoTxOutput]-  }-  deriving (Eq, Show, Generic, NFData)--binfoTxToJSON :: Network -> BinfoTx -> Value-binfoTxToJSON net BinfoTx {..} =-  A.object $-    [ "hash" .= getBinfoTxHash,-      "ver" .= getBinfoTxVer,-      "vin_sz" .= getBinfoTxVinSz,-      "vout_sz" .= getBinfoTxVoutSz,-      "size" .= getBinfoTxSize,-      "weight" .= getBinfoTxWeight,-      "fee" .= getBinfoTxFee,-      "relayed_by" .= TE.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-    ]-      ++ bal-      ++ rbf-  where-    bal =-      case getBinfoTxResultBal of-        Nothing -> []-        Just (res, bal') -> ["result" .= res, "balance" .= bal']-    rbf = ["rbf" .= True | getBinfoTxRBF]--binfoTxToEncoding :: Network -> BinfoTx -> Encoding-binfoTxToEncoding net BinfoTx {..} =-  AE.pairs $-    "hash" .= getBinfoTxHash-      <> "ver" .= getBinfoTxVer-      <> "vin_sz" .= getBinfoTxVinSz-      <> "vout_sz" .= getBinfoTxVoutSz-      <> "size" .= getBinfoTxSize-      <> "weight" .= getBinfoTxWeight-      <> "fee" .= getBinfoTxFee-      <> "relayed_by" .= TE.decodeUtf8 getBinfoTxRelayedBy-      <> "lock_time" .= getBinfoTxLockTime-      <> "tx_index" .= getBinfoTxIndex-      <> "double_spend" .= getBinfoTxDoubleSpend-      <> "time" .= getBinfoTxTime-      <> "block_index" .= getBinfoTxBlockIndex-      <> "block_height" .= getBinfoTxBlockHeight-      <> "inputs" `AE.pair` AE.list (binfoTxInputToEncoding net) getBinfoTxInputs-      <> "out" `AE.pair` AE.list (binfoTxOutputToEncoding net) getBinfoTxOutputs-      <> bal-      <> rbf-  where-    bal =-      case getBinfoTxResultBal of-        Nothing -> mempty-        Just (res, bal') -> "result" .= res <> "balance" .= bal'-    rbf = if getBinfoTxRBF then "rbf" .= True else mempty--binfoTxParseJSON :: Network -> Value -> Parser BinfoTx-binfoTxParseJSON net = A.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 <- TE.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)-  getBinfoTxRBF <- o .:? "rbf" .!= False-  res <- o .:? "result"-  bal <- o .:? "balance"-  let getBinfoTxResultBal = (,) <$> res <*> bal-  return BinfoTx {..}--data BinfoTxInput = BinfoTxInput-  { getBinfoTxInputSeq :: !Word32,-    getBinfoTxInputWitness :: !ByteString,-    getBinfoTxInputScript :: !ByteString,-    getBinfoTxInputIndex :: !Word32,-    getBinfoTxInputPrevOut :: !BinfoTxOutput-  }-  deriving (Eq, Show, Generic, NFData)--binfoTxInputToJSON :: Network -> BinfoTxInput -> Value-binfoTxInputToJSON net BinfoTxInput {..} =-  A.object-    [ "sequence" .= getBinfoTxInputSeq,-      "witness" .= encodeHex getBinfoTxInputWitness,-      "script" .= encodeHex getBinfoTxInputScript,-      "index" .= getBinfoTxInputIndex,-      "prev_out" .= binfoTxOutputToJSON net getBinfoTxInputPrevOut-    ]--binfoTxInputToEncoding :: Network -> BinfoTxInput -> Encoding-binfoTxInputToEncoding net BinfoTxInput {..} =-  AE.pairs-    ( "sequence" .= getBinfoTxInputSeq-        <> "witness" .= encodeHex getBinfoTxInputWitness-        <> "script" .= encodeHex getBinfoTxInputScript-        <> "index" .= getBinfoTxInputIndex-        <> "prev_out" .= binfoTxOutputToJSON net getBinfoTxInputPrevOut-    )--binfoTxInputParseJSON :: Network -> Value -> Parser BinfoTxInput-binfoTxInputParseJSON net = A.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"-      >>= 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, NFData)--binfoTxOutputToJSON :: Network -> BinfoTxOutput -> Value-binfoTxOutputToJSON net BinfoTxOutput {..} =-  A.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 {..} =-  AE.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 = A.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, NFData)--instance ToJSON BinfoSpender where-  toJSON BinfoSpender {..} =-    A.object-      [ "tx_index" .= getBinfoSpenderTxIndex,-        "n" .= getBinfoSpenderIndex-      ]-  toEncoding BinfoSpender {..} =-    AE.pairs-      ( "tx_index" .= getBinfoSpenderTxIndex-          <> "n" .= getBinfoSpenderIndex-      )--instance FromJSON BinfoSpender where-  parseJSON = A.withObject "spender" $ \o -> do-    getBinfoSpenderTxIndex <- o .: "tx_index"-    getBinfoSpenderIndex <- o .: "n"-    return BinfoSpender {..}--data BinfoXPubPath = BinfoXPubPath-  { getBinfoXPubPathKey :: !XPubKey,-    getBinfoXPubPathDeriv :: !SoftPath-  }-  deriving (Eq, Show, Generic, NFData)--instance Ord BinfoXPubPath where-  compare = compare `on` f-    where-      f b =-        ( xPubParent (getBinfoXPubPathKey b),-          getBinfoXPubPathDeriv b-        )--binfoXPubPathToJSON :: Network -> BinfoXPubPath -> Value-binfoXPubPathToJSON net BinfoXPubPath {..} =-  A.object-    [ "m" .= xPubToJSON net getBinfoXPubPathKey,-      "path" .= ("M" ++ pathToStr getBinfoXPubPathDeriv)-    ]--binfoXPubPathToEncoding :: Network -> BinfoXPubPath -> Encoding-binfoXPubPathToEncoding net BinfoXPubPath {..} =-  AE.pairs $-    "m" `AE.pair` xPubToEncoding net getBinfoXPubPathKey-      <> "path" .= ("M" ++ pathToStr getBinfoXPubPathDeriv)--binfoXPubPathParseJSON :: Network -> Value -> Parser BinfoXPubPath-binfoXPubPathParseJSON net = A.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 {..} =-    A.object-      [ "nconnected" .= getBinfoConnected,-        "conversion" .= getBinfoConversion,-        "symbol_local" .= getBinfoLocal,-        "symbol_btc" .= getBinfoBTC,-        "latest_block" .= getBinfoLatestBlock-      ]-  toEncoding BinfoInfo {..} =-    AE.pairs-      ( "nconnected" .= getBinfoConnected-          <> "conversion" .= getBinfoConversion-          <> "symbol_local" .= getBinfoLocal-          <> "symbol_btc" .= getBinfoBTC-          <> "latest_block" .= getBinfoLatestBlock-      )--instance FromJSON BinfoInfo where-  parseJSON = A.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, NFData)--instance ToJSON BinfoBlockInfo where-  toJSON BinfoBlockInfo {..} =-    A.object-      [ "hash" .= getBinfoBlockInfoHash,-        "height" .= getBinfoBlockInfoHeight,-        "time" .= getBinfoBlockInfoTime,-        "block_index" .= getBinfoBlockInfoIndex-      ]-  toEncoding BinfoBlockInfo {..} =-    AE.pairs-      ( "hash" .= getBinfoBlockInfoHash-          <> "height" .= getBinfoBlockInfoHeight-          <> "time" .= getBinfoBlockInfoTime-          <> "block_index" .= getBinfoBlockInfoIndex-      )--instance FromJSON BinfoBlockInfo where-  parseJSON = A.withObject "block_info" $ \o -> do-    getBinfoBlockInfoHash <- o .: "hash"-    getBinfoBlockInfoHeight <- o .: "height"-    getBinfoBlockInfoTime <- o .: "time"-    getBinfoBlockInfoIndex <- o .: "block_index"-    return BinfoBlockInfo {..}--toBinfoBlockInfo :: BlockData -> BinfoBlockInfo-toBinfoBlockInfo BlockData {..} =-  BinfoBlockInfo-    { getBinfoBlockInfoHash = headerHash blockDataHeader,-      getBinfoBlockInfoHeight = blockDataHeight,-      getBinfoBlockInfoTime = blockTimestamp blockDataHeader,-      getBinfoBlockInfoIndex = blockDataHeight-    }--data BinfoRate = BinfoRate-  { binfoRateTime :: !Word64,-    binfoRatePrice :: !Double,-    binfoRateVol24 :: !Double-  }-  deriving (Eq, Show, Generic, NFData)--instance ToJSON BinfoRate where-  toJSON BinfoRate {..} =-    A.object-      [ "timestamp" .= binfoRateTime,-        "price" .= binfoRatePrice,-        "volume24h" .= binfoRateVol24-      ]--instance FromJSON BinfoRate where-  parseJSON = A.withObject "price" $ \o -> do-    binfoRateTime <- o .: "timestamp"-    binfoRatePrice <- o .: "price"-    binfoRateVol24 <- o .: "volume24h"-    return BinfoRate {..}--data BinfoHistory = BinfoHistory-  { binfoHistoryDate :: !Text,-    binfoHistoryTime :: !Text,-    binfoHistoryType :: !Text,-    binfoHistoryAmount :: !Double,-    binfoHistoryValueThen :: !Double,-    binfoHistoryValueNow :: !Double,-    binfoHistoryExchangeRateThen :: !Double,-    binfoHistoryTx :: !TxHash,-    binfoHistoryFee :: !Double-  }-  deriving (Eq, Show, Generic, NFData)--instance ToJSON BinfoHistory where-  toJSON BinfoHistory {..} =-    A.object-      [ "date" .= binfoHistoryDate,-        "time" .= binfoHistoryTime,-        "type" .= binfoHistoryType,-        "amount" .= binfoHistoryAmount,-        "value_then" .= binfoHistoryValueThen,-        "value_now" .= binfoHistoryValueNow,-        "exchange_rate_then" .= binfoHistoryExchangeRateThen,-        "tx" .= binfoHistoryTx,-        "fee" .= binfoHistoryFee-      ]--instance FromJSON BinfoHistory where-  parseJSON = A.withObject "history" $ \o -> do-    binfoHistoryDate <- o .: "date"-    binfoHistoryTime <- o .: "time"-    binfoHistoryType <- o .: "type"-    binfoHistoryAmount <- o .: "amount"-    binfoHistoryValueThen <- o .: "value_then"-    binfoHistoryValueNow <- o .: "value_now"-    binfoHistoryExchangeRateThen <- o .: "exchange_rate_then"-    binfoHistoryTx <- o .: "tx"-    binfoHistoryFee <- o .: "fee"-    return BinfoHistory {..}--toBinfoHistory ::-  Int64 ->-  Word64 ->-  Double ->-  Double ->-  Word64 ->-  TxHash ->-  BinfoHistory-toBinfoHistory satoshi timestamp rate_then rate_now fee txhash =-  BinfoHistory-    { binfoHistoryDate =-        T.pack $ formatTime defaultTimeLocale "%Y-%m-%d" t,-      binfoHistoryTime =-        T.pack $ formatTime defaultTimeLocale "%H:%M:%S GMT %Ez" t,-      binfoHistoryType =-        if satoshi <= 0 then "sent" else "received",-      binfoHistoryAmount =-        fromRational v,-      binfoHistoryValueThen =-        fromRational v1,-      binfoHistoryValueNow =-        fromRational v2,-      binfoHistoryExchangeRateThen =-        rate_then,-      binfoHistoryTx =-        txhash,-      binfoHistoryFee =-        fromRational f-    }-  where-    t = posixSecondsToUTCTime (realToFrac timestamp)-    v = toRational satoshi / (100 * 1000 * 1000)-    r1 = toRational rate_then-    r2 = toRational rate_now-    f = toRational fee / (100 * 1000 * 1000)-    v1 = v * r1-    v2 = v * r2--newtype BinfoDate = BinfoDate Word64-  deriving (Eq, Show, Read, Generic, NFData)--instance Parsable BinfoDate where-  parseParam t =-    maybeToEither "Cannot parse date"-      . fmap (BinfoDate . round . utcTimeToPOSIXSeconds)-      $ p "%d-%m-%Y" <|> p "%d/%m/%Y"-    where-      s = TL.unpack t-      p fmt = parseTimeM False defaultTimeLocale fmt s--data BinfoTicker = BinfoTicker-  { binfoTicker15m :: !Double,-    binfoTickerLast :: !Double,-    binfoTickerBuy :: !Double,-    binfoTickerSell :: !Double,-    binfoTickerSymbol :: !Text-  }-  deriving (Eq, Show, Generic, NFData)--instance Default BinfoTicker where-  def =-    BinfoTicker-      { binfoTickerSymbol = "XXX",-        binfoTicker15m = 0.0,-        binfoTickerLast = 0.0,-        binfoTickerBuy = 0.0,-        binfoTickerSell = 0.0-      }--instance ToJSON BinfoTicker where-  toJSON BinfoTicker {..} =-    A.object-      [ "symbol" .= binfoTickerSymbol,-        "sell" .= binfoTickerSell,-        "buy" .= binfoTickerBuy,-        "last" .= binfoTickerLast,-        "15m" .= binfoTicker15m-      ]-  toEncoding BinfoTicker {..} =-    AE.pairs $-      "symbol" .= binfoTickerSymbol-        <> "sell" .= binfoTickerSell-        <> "buy" .= binfoTickerBuy-        <> "last" .= binfoTickerLast-        <> "15m" .= binfoTicker15m--instance FromJSON BinfoTicker where-  parseJSON = A.withObject "ticker" $ \o -> do-    binfoTickerSymbol <- o .: "symbol"-    binfoTicker15m <- o .: "15m"-    binfoTickerSell <- o .: "sell"-    binfoTickerBuy <- o .: "buy"-    binfoTickerLast <- o .: "last"-    return BinfoTicker {..}--data BinfoSymbol = BinfoSymbol-  { getBinfoSymbolCode :: !Text,-    getBinfoSymbolString :: !Text,-    getBinfoSymbolName :: !Text,-    getBinfoSymbolConversion :: !Double,-    getBinfoSymbolAfter :: !Bool,-    getBinfoSymbolLocal :: !Bool-  }-  deriving (Eq, Show, Generic, NFData)--instance Default BinfoSymbol where-  def =-    BinfoSymbol-      { getBinfoSymbolCode = "XXX",-        getBinfoSymbolString = "¤",-        getBinfoSymbolName = "No currency",-        getBinfoSymbolConversion = 0.0,-        getBinfoSymbolAfter = False,-        getBinfoSymbolLocal = True-      }--instance ToJSON BinfoSymbol where-  toJSON BinfoSymbol {..} =-    A.object-      [ "code" .= getBinfoSymbolCode,-        "symbol" .= getBinfoSymbolString,-        "name" .= getBinfoSymbolName,-        "conversion" .= getBinfoSymbolConversion,-        "symbolAppearsAfter" .= getBinfoSymbolAfter,-        "local" .= getBinfoSymbolLocal-      ]-  toEncoding BinfoSymbol {..} =-    AE.pairs-      ( "code" .= getBinfoSymbolCode-          <> "symbol" .= getBinfoSymbolString-          <> "name" .= getBinfoSymbolName-          <> "conversion" .= getBinfoSymbolConversion-          <> "symbolAppearsAfter" .= getBinfoSymbolAfter-          <> "local" .= getBinfoSymbolLocal-      )--instance FromJSON BinfoSymbol where-  parseJSON = A.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 outputAddr 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 XPubSpec [XPubBal] ->-  HashMap XPubSpec Word64 ->-  [BinfoBalance]-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 = HashMap.lookupDefault 0 k xpub_txs-          ax = foldl max 0 (map (i 0) xs)-          cx = foldl max 0 (map (i 1) xs)-       in BinfoXPubBalance-            { getBinfoXPubKey = xPubSpecKey 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 BinfoAddrBalance-                  { getBinfoAddress = addr,-                    getBinfoAddrTxCount = tx_count,-                    getBinfoAddrReceived = recv,-                    getBinfoAddrSent = sent,-                    getBinfoAddrBalance = bal-                  }-       in map f $ HashMap.elems only_addrs--toBinfoTxSimple ::-  Bool ->-  Transaction ->-  BinfoTx-toBinfoTxSimple numtxid =-  toBinfoTx numtxid HashMap.empty False 0--toBinfoTxInputs ::-  Bool ->-  HashMap Address (Maybe BinfoXPubPath) ->-  Transaction ->-  [BinfoTxInput]-toBinfoTxInputs numtxid 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-        [] -> BS.empty-        ws -> runPutS (put_witness ws)-    prev = inputToBinfoTxOutput numtxid abook t-    put_witness ws = do-      putVarInt (length ws)-      mapM_ put_item ws-    put_item bs = do-      putVarInt (BS.length bs)-      putByteString bs--toBinfoBlockIndex :: Transaction -> Maybe BlockHeight-toBinfoBlockIndex Transaction {transactionDeleted = True} = Nothing-toBinfoBlockIndex Transaction {transactionBlock = MemRef _} = Nothing-toBinfoBlockIndex Transaction {transactionBlock = BlockRef h _} = Just h--toBinfoTx ::-  Bool ->-  HashMap Address (Maybe BinfoXPubPath) ->-  Bool ->-  Int64 ->-  Transaction ->-  BinfoTx-toBinfoTx numtxid abook 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 =-        encodeBinfoTxId numtxid (txHash (transactionData t)),-      getBinfoTxDoubleSpend = transactionDeleted,-      getBinfoTxRBF = transactionRBF,-      getBinfoTxTime = transactionTime,-      getBinfoTxBlockIndex = toBinfoBlockIndex t,-      getBinfoTxBlockHeight = toBinfoBlockIndex t,-      getBinfoTxInputs = toBinfoTxInputs numtxid abook t,-      getBinfoTxOutputs = outs,-      getBinfoTxResultBal = resbal-    }-  where-    simple = HashMap.null abook && bal == 0-    resbal = if simple then Nothing else Just (getTxResult aset t, bal)-    aset = HashMap.keysSet abook-    outs =-      let p = prune && getTxResult aset t > 0-          f = toBinfoTxOutput numtxid abook p t-       in catMaybes $ zipWith f [0 ..] transactionOutputs--getTxResult :: HashSet Address -> Transaction -> Int64-getTxResult aset 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 aset-      output_sum = sum $ map out_value transactionOutputs-      out_value StoreOutput {..} =-        case outputAddr of-          Nothing -> 0-          Just a ->-            if test_addr a-              then fromIntegral outputAmount-              else 0-   in input_sum + output_sum--toBinfoTxOutput ::-  Bool ->-  HashMap Address (Maybe BinfoXPubPath) ->-  Bool ->-  Transaction ->-  Word32 ->-  StoreOutput ->-  Maybe BinfoTxOutput-toBinfoTxOutput numtxid abook prune t n StoreOutput {..} =-  let getBinfoTxOutputType = 0-      getBinfoTxOutputSpent = isJust outputSpender-      getBinfoTxOutputValue = outputAmount-      getBinfoTxOutputIndex = n-      getBinfoTxOutputTxIndex =-        encodeBinfoTxId numtxid (txHash (transactionData t))-      getBinfoTxOutputScript = outputScript-      getBinfoTxOutputSpenders =-        maybeToList $ toBinfoSpender numtxid <$> outputSpender-      getBinfoTxOutputAddress = outputAddr-      getBinfoTxOutputXPub =-        outputAddr >>= join . (`HashMap.lookup` abook)-   in if prune && isNothing (outputAddr >>= (`HashMap.lookup` abook))-        then Nothing-        else Just BinfoTxOutput {..}--toBinfoSpender :: Bool -> Spender -> BinfoSpender-toBinfoSpender numtxid Spender {..} =-  let getBinfoSpenderTxIndex = encodeBinfoTxId numtxid spenderHash-      getBinfoSpenderIndex = spenderIndex-   in BinfoSpender {..}--inputToBinfoTxOutput ::-  Bool ->-  HashMap Address (Maybe BinfoXPubPath) ->-  Transaction ->-  Word32 ->-  StoreInput ->-  BinfoTxOutput-inputToBinfoTxOutput numtxid abook t n i =-  BinfoTxOutput-    { getBinfoTxOutputIndex = out_index,-      getBinfoTxOutputType = 0,-      getBinfoTxOutputSpent = True,-      getBinfoTxOutputValue = inputAmount,-      getBinfoTxOutputTxIndex = encodeBinfoTxId numtxid out_hash,-      getBinfoTxOutputScript = inputPkScript,-      getBinfoTxOutputSpenders = [spender],-      getBinfoTxOutputAddress = inputAddress,-      getBinfoTxOutputXPub = xpub-    }-  where-    inputAmount =-      case i of-        StoreCoinbase {} -> 0-        StoreInput {..} -> inputAmount-    inputPkScript =-      case i of-        StoreCoinbase {} -> BS.empty-        StoreInput {..} -> inputPkScript-    inputAddress =-      case i of-        StoreCoinbase {} -> Nothing-        StoreInput {..} -> inputAddress-    OutPoint out_hash out_index = inputPoint i-    spender =-      BinfoSpender-        (encodeBinfoTxId numtxid (txHash (transactionData t)))-        n-    xpub = inputAddress >>= join . (`HashMap.lookup` abook)--data BinfoAddr-  = BinfoAddr !Address-  | BinfoXpub !XPubKey-  deriving (Eq, Show, Generic, Hashable, NFData)--parseBinfoAddr :: Network -> Text -> Maybe [BinfoAddr]-parseBinfoAddr _ "" = Just []-parseBinfoAddr net s =-  mapM f $-    filter (not . T.null) $-      concatMap (T.splitOn ",") (T.splitOn "|" s)-  where-    f x = BinfoAddr <$> textToAddr net x <|> BinfoXpub <$> xPubImport net x--data BinfoHeader = BinfoHeader-  { binfoHeaderHash :: !BlockHash,-    binfoHeaderTime :: !Timestamp,-    binfoHeaderIndex :: !Word32,-    binfoHeaderHeight :: !BlockHeight,-    binfoTxIndices :: ![BinfoTxId]-  }-  deriving (Eq, Show, Generic, NFData)--instance ToJSON BinfoHeader where-  toJSON BinfoHeader {..} =-    A.object-      [ "hash" .= binfoHeaderHash,-        "time" .= binfoHeaderTime,-        "block_index" .= binfoHeaderIndex,-        "height" .= binfoHeaderHeight,-        "txIndexes" .= binfoTxIndices-      ]--instance FromJSON BinfoHeader where-  parseJSON = A.withObject "header" $ \o -> do-    binfoHeaderHash <- o .: "hash"-    binfoHeaderTime <- o .: "time"-    binfoHeaderIndex <- o .: "block_index"-    binfoHeaderHeight <- o .: "height"-    binfoTxIndices <- o .: "txIndexes"-    return BinfoHeader {..}--newtype BinfoMempool = BinfoMempool {getBinfoMempool :: [BinfoTx]}-  deriving (Eq, Show, Generic, NFData)--binfoMempoolToJSON :: Network -> BinfoMempool -> Value-binfoMempoolToJSON net (BinfoMempool txs) =-  A.object ["txs" .= map (binfoTxToJSON net) txs]--binfoMempoolToEncoding :: Network -> BinfoMempool -> Encoding-binfoMempoolToEncoding net (BinfoMempool txs) =-  AE.pairs $ AE.pair "txs" (AE.list (binfoTxToEncoding net) txs)--binfoMempoolParseJSON :: Network -> Value -> Parser BinfoMempool-binfoMempoolParseJSON net =-  A.withObject "mempool" $ \o ->-    BinfoMempool <$> (mapM (binfoTxParseJSON net) =<< o .: "txs")--newtype BinfoBlocks = BinfoBlocks {binfoBlocks :: [BinfoBlockInfo]}-  deriving (Eq, Show, Generic, NFData)--instance ToJSON BinfoBlocks where-  toJSON BinfoBlocks {..} = A.object ["blocks" .= binfoBlocks]--instance FromJSON BinfoBlocks where-  parseJSON = A.withObject "blocks" $ \o -> BinfoBlocks <$> o .: "blocks"+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoFieldSelectors #-}++module Haskoin.Store.Data+  ( -- * Address Balances+    Balance (..),+    zeroBalance,+    nullBalance,++    -- * Block Data+    BlockData (..),+    confirmed,++    -- * Transactions+    TxRef (..),+    TxData (..),+    txDataFee,+    isCoinbaseTx,+    Transaction (..),+    transactionData,+    fromTransaction,+    toTransaction,+    StoreInput (..),+    isCoinbase,+    StoreOutput (..),+    Prev (..),+    Spender (..),+    BlockRef (..),+    UnixTime,+    getUnixTime,+    putUnixTime,+    BlockPos,++    -- * Unspent Outputs+    Unspent (..),++    -- * Extended Public Keys+    XPubSpec (..),+    XPubBal (..),+    XPubUnspent (..),+    XPubSummary (..),+    DeriveType (..),+    textToDeriveType,+    deriveTypeToText,++    -- * Other Data+    TxId (..),+    GenericResult (..),+    SerialList (..),+    RawResult (..),+    RawResultList (..),+    PeerInfo (..),+    Healthy (..),+    BlockHealth (..),+    TimeHealth (..),+    CountHealth (..),+    MaxHealth (..),+    HealthCheck (..),+    Event (..),+    Except (..),++    -- * Blockchain.info API+    BinfoInfo (..),+    BinfoBlockId (..),+    BinfoTxId (..),+    encodeBinfoTxId,+    BinfoFilter (..),+    BinfoMultiAddr (..),+    BinfoShortBal (..),+    BinfoBalance (..),+    toBinfoAddrs,+    BinfoRawAddr (..),+    BinfoAddr (..),+    parseBinfoAddr,+    BinfoWallet (..),+    BinfoUnspent (..),+    binfoHexValue,+    BinfoUnspents (..),+    BinfoBlock (..),+    toBinfoBlock,+    BinfoTx (..),+    relevantTxs,+    toBinfoTx,+    toBinfoTxSimple,+    BinfoTxInput (..),+    BinfoTxOutput (..),+    BinfoSpender (..),+    BinfoXPubPath (..),+    BinfoBlockInfo (..),+    toBinfoBlockInfo,+    BinfoSymbol (..),+    BinfoTicker (..),+    BinfoRate (..),+    BinfoHistory (..),+    toBinfoHistory,+    BinfoDate (..),+    BinfoHeader (..),+    BinfoMempool (..),+    BinfoBlockInfos (..),+  )+where++import Control.Applicative (optional, (<|>))+import Control.DeepSeq (NFData)+import Control.Exception (Exception)+import Control.Monad (guard, join, mzero, unless, (<=<))+import Data.Aeson+  ( Encoding,+    FromJSON (..),+    ToJSON (..),+    Value (..),+    (.!=),+    (.:),+    (.:?),+    (.=),+  )+import Data.Aeson qualified as A+import Data.Aeson.Encoding qualified as A+import Data.Aeson.Types (Parser)+import Data.Binary (Binary (get, put))+import Data.Bits (Bits (..))+import Data.Bool (bool)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Builder qualified as Builder+import Data.Bytes.Get+import Data.Bytes.Get qualified as Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (Default (..))+import Data.Foldable (toList)+import Data.Function (on)+import Data.HashMap.Strict (HashMap)+import Data.HashMap.Strict qualified as HashMap+import Data.HashSet (HashSet)+import Data.HashSet qualified as HashSet+import Data.Hashable (Hashable (..))+import Data.Int (Int32, Int64)+import Data.IntMap qualified as IntMap+import Data.IntMap.Strict (IntMap)+import Data.Maybe+  ( catMaybes,+    fromMaybe,+    isJust,+    isNothing,+    mapMaybe,+    maybeToList,+  )+import Data.Serialize (Serialize (..))+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as T+import Data.Text.Lazy qualified as LazyText+import Data.Text.Lazy.Encoding qualified as LazyText+import Data.Time (UTCTime (UTCTime), rfc822DateFormat)+import Data.Time.Clock.POSIX+  ( posixSecondsToUTCTime,+    utcTimeToPOSIXSeconds,+  )+import Data.Time.Format+  ( defaultTimeLocale,+    formatTime,+    parseTimeM,+  )+import Data.Time.Format.ISO8601+  ( iso8601ParseM,+    iso8601Show,+  )+import Data.Vector qualified as V+import Data.Word (Word32, Word64)+import GHC.Generics (Generic)+import Haskoin+import Web.Scotty.Trans (Parsable (..), ScottyError (..))++data DeriveType+  = DeriveNormal+  | DeriveP2SH+  | DeriveP2WPKH+  deriving (Show, Eq, Generic, NFData)++textToDeriveType :: Text -> Maybe DeriveType+textToDeriveType "normal" = Just DeriveNormal+textToDeriveType "compat" = Just DeriveP2SH+textToDeriveType "segwit" = Just DeriveP2WPKH+textToDeriveType _ = Nothing++deriveTypeToText :: DeriveType -> Text+deriveTypeToText DeriveNormal = "normal"+deriveTypeToText DeriveP2SH = "compat"+deriveTypeToText DeriveP2WPKH = "segwit"++instance Serial DeriveType where+  serialize DeriveNormal = putWord8 0x00+  serialize DeriveP2SH = putWord8 0x01+  serialize DeriveP2WPKH = putWord8 0x02++  deserialize =+    getWord8 >>= \case+      0x00 -> return DeriveNormal+      0x01 -> return DeriveP2SH+      0x02 -> return DeriveP2WPKH+      _ -> return DeriveNormal++instance Binary DeriveType where+  put = serialize+  get = deserialize++instance Serialize DeriveType where+  put = serialize+  get = deserialize++instance Default DeriveType where+  def = DeriveNormal++instance Parsable DeriveType where+  parseParam txt =+    case textToDeriveType (LazyText.toStrict txt) of+      Nothing -> Left "invalid derivation type"+      Just x -> Right x++data XPubSpec = XPubSpec+  { key :: !XPubKey,+    deriv :: !DeriveType+  }+  deriving (Show, Eq, Generic, NFData)++instance Hashable XPubSpec where+  hashWithSalt i = hashWithSalt i . (.key.key)++instance Serial XPubSpec where+  serialize XPubSpec {key = k, deriv = t} = do+    putWord8 k.depth+    serialize k.parent+    putWord32be k.index+    serialize k.chain+    putByteString k.key.get+    serialize t+  deserialize = do+    depth <- getWord8+    parent <- deserialize+    index <- getWord32be+    chain <- deserialize+    key <- PubKey <$> getByteString 64+    deriv <- deserialize+    return XPubSpec {key = XPubKey {..}, deriv}++instance Serialize XPubSpec where+  put = serialize+  get = deserialize++instance Binary XPubSpec where+  put = serialize+  get = deserialize++type UnixTime = Word64++type BlockPos = Word32++-- | Binary such that ordering is inverted.+putUnixTime :: (MonadPut m) => Word64 -> m ()+putUnixTime w = putWord64be $ maxBound - w++getUnixTime :: (MonadGet m) => m Word64+getUnixTime = (maxBound -) <$> getWord64be++-- | Reference to a block where a transaction is stored.+data BlockRef+  = BlockRef+      { -- | block height in the chain+        height :: !BlockHeight,+        -- | position of transaction within the block+        position :: !Word32+      }+  | MemRef+      { timestamp :: !UnixTime+      }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++-- | Serial entities will sort in reverse order.+instance Serial BlockRef where+  serialize MemRef {timestamp = t} = do+    putWord8 0x00+    putUnixTime t+  serialize BlockRef {height = h, position = p} = do+    putWord8 0x01+    putWord32be (maxBound - h)+    putWord32be (maxBound - p)+  deserialize =+    getWord8 >>= \case+      0x00 -> getmemref+      0x01 -> getblockref+      _ -> fail "Cannot decode BlockRef"+    where+      getmemref = do+        MemRef <$> getUnixTime+      getblockref = do+        h <- (maxBound -) <$> getWord32be+        p <- (maxBound -) <$> getWord32be+        return BlockRef {height = h, position = p}++instance Serialize BlockRef where+  put = serialize+  get = deserialize++instance Binary BlockRef where+  put = serialize+  get = deserialize++confirmed :: BlockRef -> Bool+confirmed BlockRef {} = True+confirmed MemRef {} = False++instance ToJSON BlockRef where+  toJSON BlockRef {height = h, position = p} =+    A.object+      [ "height" .= h,+        "position" .= p+      ]+  toJSON MemRef {timestamp = t} =+    A.object ["mempool" .= t]+  toEncoding BlockRef {height = h, position = p} =+    A.pairs $+      mconcat+        [ "height" `A.pair` A.word32 h,+          "position" `A.pair` A.word32 p+        ]+  toEncoding MemRef {timestamp = t} =+    A.pairs $ "mempool" `A.pair` A.word64 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 {..}+      m o =+        MemRef <$> o .: "mempool"++-- | Transaction in relation to an address.+data TxRef = TxRef+  { -- | block information+    block :: !BlockRef,+    -- | transaction hash+    txid :: !TxHash+  }+  deriving (Show, Eq, Ord, Generic, Hashable, NFData)++instance Serial TxRef where+  serialize (TxRef b h) = do+    serialize b+    serialize h++  deserialize =+    TxRef <$> deserialize <*> deserialize++instance Binary TxRef where+  put = serialize+  get = deserialize++instance Serialize TxRef where+  put = serialize+  get = deserialize++instance ToJSON TxRef where+  toJSON x =+    A.object+      [ "txid" .= x.txid,+        "block" .= x.block+      ]+  toEncoding btx =+    A.pairs $+      mconcat+        [ "txid" `A.pair` toEncoding btx.txid,+          "block" `A.pair` toEncoding btx.block+        ]++instance FromJSON TxRef where+  parseJSON =+    A.withObject "TxRef" $ \o -> do+      txid <- o .: "txid"+      block <- o .: "block"+      return TxRef {..}++-- | Address balance information.+data Balance = Balance+  { -- | address balance+    address :: !Address,+    -- | confirmed balance+    confirmed :: !Word64,+    -- | unconfirmed balance+    unconfirmed :: !Word64,+    -- | number of unspent outputs+    utxo :: !Word64,+    -- | number of transactions+    txs :: !Word64,+    -- | total amount from all outputs in this address+    received :: !Word64+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance Serial Balance where+  serialize b = do+    serialize b.address+    putWord64be b.confirmed+    putWord64be b.unconfirmed+    putWord64be b.utxo+    putWord64be b.txs+    putWord64be b.received++  deserialize = do+    address <- deserialize+    confirmed <- getWord64be+    unconfirmed <- getWord64be+    utxo <- getWord64be+    txs <- getWord64be+    received <- getWord64be+    return Balance {..}++instance Binary Balance where+  put = serialize+  get = deserialize++instance Serialize Balance where+  put = serialize+  get = deserialize++zeroBalance :: Address -> Balance+zeroBalance a =+  Balance+    { address = a,+      confirmed = 0,+      unconfirmed = 0,+      utxo = 0,+      txs = 0,+      received = 0+    }++nullBalance :: Balance -> Bool+nullBalance+  Balance+    { confirmed = 0,+      unconfirmed = 0,+      utxo = 0,+      txs = 0,+      received = 0+    } = True+nullBalance _ = False++instance MarshalJSON Network Balance where+  marshalValue net b =+    A.object+      [ "address" .= marshalValue net b.address,+        "confirmed" .= b.confirmed,+        "unconfirmed" .= b.unconfirmed,+        "utxo" .= b.utxo,+        "txs" .= b.txs,+        "received" .= b.received+      ]++  marshalEncoding net b =+    A.pairs $+      mconcat+        [ "address" `A.pair` marshalEncoding net b.address,+          "confirmed" `A.pair` A.word64 b.confirmed,+          "unconfirmed" `A.pair` A.word64 b.unconfirmed,+          "utxo" `A.pair` A.word64 b.utxo,+          "txs" `A.pair` A.word64 b.txs,+          "received" `A.pair` A.word64 b.received+        ]++  unmarshalValue net =+    A.withObject "Balance" $ \o -> do+      confirmed <- o .: "confirmed"+      unconfirmed <- o .: "unconfirmed"+      utxo <- o .: "utxo"+      txs <- o .: "txs"+      received <- o .: "received"+      address <- unmarshalValue net =<< o .: "address"+      return Balance {..}++-- | Unspent output.+data Unspent = Unspent+  { block :: !BlockRef,+    outpoint :: !OutPoint,+    value :: !Word64,+    script :: !ByteString,+    address :: !(Maybe Address)+  }+  deriving (Show, Eq, Generic, Hashable, NFData)++-- | Follow same order as in database and cache by inverting outpoint sort+-- order.+instance Ord Unspent where+  compare a b =+    compare+      (a.block, b.block)+      (b.block, a.block)++instance Serial Unspent where+  serialize u = do+    serialize u.block+    serialize u.outpoint+    putWord64be u.value+    putLengthBytes u.script+    putMaybe serialize u.address++  deserialize = do+    block <- deserialize+    outpoint <- deserialize+    value <- getWord64be+    script <- getLengthBytes+    address <- getMaybe deserialize+    return Unspent {..}++instance Binary Unspent where+  put = serialize+  get = deserialize++instance Serialize Unspent where+  put = serialize+  get = deserialize++instance Coin Unspent where+  coinValue = (.value)++instance MarshalJSON Network Unspent where+  marshalValue net u =+    A.object+      [ "address" .= fmap (marshalValue net) u.address,+        "block" .= u.block,+        "txid" .= u.outpoint.hash,+        "index" .= u.outpoint.index,+        "pkscript" .= encodeHex u.script,+        "value" .= u.value+      ]++  marshalEncoding net u =+    A.pairs $+      mconcat+        [ "address" `A.pair` maybe A.null_ (marshalEncoding net) u.address,+          "block" `A.pair` toEncoding u.block,+          "txid" `A.pair` toEncoding u.outpoint.hash,+          "index" `A.pair` A.word32 u.outpoint.index,+          "pkscript" `A.pair` hexEncoding (B.fromStrict u.script),+          "value" `A.pair` A.word64 u.value+        ]++  unmarshalValue net =+    A.withObject "Unspent" $ \o -> do+      block <- o .: "block"+      hash <- o .: "txid"+      index <- o .: "index"+      value <- o .: "value"+      script <- o .: "pkscript" >>= jsonHex+      address <-+        o .: "address"+          >>= maybe (return Nothing) (optional . unmarshalValue net)+      return Unspent {outpoint = OutPoint {..}, ..}++-- | Database value for a block entry.+data BlockData = BlockData+  { -- | height of the block in the chain+    height :: !BlockHeight,+    -- | is this block in the main chain?+    main :: !Bool,+    -- | accumulated work in that block+    work :: !Integer,+    -- | block header+    header :: !BlockHeader,+    -- | size of the block including witnesses+    size :: !Word32,+    -- | weight of this block (for segwit networks)+    weight :: !Word32,+    -- | block transactions+    txs :: ![TxHash],+    -- | sum of all transaction outputs+    outputs :: !Word64,+    -- | sum of all transaction fee+    fee :: !Word64,+    -- | block subsidy+    subsidy :: !Word64+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance Serial BlockData where+  serialize b = do+    putWord32be b.height+    serialize b.main+    putInteger b.work+    serialize b.header+    putWord32be b.size+    putWord32be b.weight+    putList serialize b.txs+    putWord64be b.outputs+    putWord64be b.fee+    putWord64be b.subsidy++  deserialize = do+    height <- getWord32be+    main <- deserialize+    work <- getInteger+    header <- deserialize+    size <- getWord32be+    weight <- getWord32be+    txs <- getList deserialize+    outputs <- getWord64be+    fee <- getWord64be+    subsidy <- getWord64be+    return BlockData {..}++instance Serialize BlockData where+  put = serialize+  get = deserialize++instance Binary BlockData where+  put = serialize+  get = deserialize++instance FromJSON BlockData where+  parseJSON =+    A.withObject "BlockData" $ \o -> do+      height <- o .: "height"+      main <- o .: "mainchain"+      prev <- o .: "previous"+      timestamp <- o .: "time"+      version <- o .: "version"+      bits <- o .: "bits"+      nonce <- o .: "nonce"+      size <- o .: "size"+      txs <- o .: "tx"+      TxHash merkle <- o .: "merkle"+      subsidy <- o .: "subsidy"+      fee <- o .: "fees"+      outputs <- o .: "outputs"+      work <- o .: "work"+      weight <- o .:? "weight" .!= 0+      return BlockData {header = BlockHeader {..}, ..}++instance MarshalJSON Network BlockData where+  marshalValue net b =+    A.object $+      [ "hash" .= headerHash b.header,+        "height" .= b.height,+        "mainchain" .= b.main,+        "previous" .= b.header.prev,+        "time" .= b.header.timestamp,+        "version" .= b.header.version,+        "bits" .= b.header.bits,+        "nonce" .= b.header.nonce,+        "size" .= b.size,+        "tx" .= b.txs,+        "merkle" .= TxHash b.header.merkle,+        "subsidy" .= b.subsidy,+        "fees" .= b.fee,+        "outputs" .= b.outputs,+        "work" .= b.work+      ]+        <> ["weight" .= b.weight | net.segWit]++  marshalEncoding net b =+    A.pairs $+      mconcat+        [ "hash" `A.pair` toEncoding (headerHash b.header),+          "height" `A.pair` A.word32 b.height,+          "mainchain" `A.pair` A.bool b.main,+          "previous" `A.pair` toEncoding b.header.prev,+          "time" `A.pair` A.word32 b.header.timestamp,+          "version" `A.pair` A.word32 b.header.version,+          "bits" `A.pair` A.word32 b.header.bits,+          "nonce" `A.pair` A.word32 b.header.nonce,+          "size" `A.pair` A.word32 b.size,+          "tx" `A.pair` A.list toEncoding b.txs,+          "merkle" `A.pair` toEncoding (TxHash b.header.merkle),+          "subsidy" `A.pair` A.word64 b.subsidy,+          "fees" `A.pair` A.word64 b.fee,+          "outputs" `A.pair` A.word64 b.outputs,+          "work" `A.pair` A.integer b.work,+          bool mempty ("weight" `A.pair` A.word32 b.weight) net.segWit+        ]++  unmarshalValue net = parseJSON++data StoreInput+  = StoreCoinbase+      { outpoint :: !OutPoint,+        sequence :: !Word32,+        script :: !ByteString,+        witness :: !WitnessStack+      }+  | StoreInput+      { outpoint :: !OutPoint,+        sequence :: !Word32,+        script :: !ByteString,+        pkscript :: !ByteString,+        value :: !Word64,+        witness :: !WitnessStack,+        address :: !(Maybe Address)+      }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance Serial StoreInput where+  serialize i@StoreCoinbase {} = do+    putWord8 0x00+    serialize i.outpoint+    putWord32be i.sequence+    putLengthBytes i.script+    putList putLengthBytes i.witness+  serialize i@StoreInput {} = do+    putWord8 0x01+    serialize i.outpoint+    putWord32be i.sequence+    putLengthBytes i.script+    putLengthBytes i.pkscript+    putWord64be i.value+    putList putLengthBytes i.witness+    putMaybe serialize i.address++  deserialize =+    getWord8 >>= \case+      0x00 -> do+        outpoint <- deserialize+        sequence <- getWord32be+        script <- getLengthBytes+        witness <- getList getLengthBytes+        return StoreCoinbase {..}+      0x01 -> do+        outpoint <- deserialize+        sequence <- getWord32be+        script <- getLengthBytes+        pkscript <- getLengthBytes+        value <- getWord64be+        witness <- getList getLengthBytes+        address <- getMaybe deserialize+        return StoreInput {..}+      x -> fail $ "Unknown input id: " <> cs (show x)++instance Serialize StoreInput where+  put = serialize+  get = deserialize++instance Binary StoreInput where+  put = serialize+  get = deserialize++isCoinbaseTx :: Tx -> Bool+isCoinbaseTx = all ((== nullOutPoint) . (.outpoint)) . (.inputs)++isCoinbase :: StoreInput -> Bool+isCoinbase StoreCoinbase {} = True+isCoinbase StoreInput {} = False++instance MarshalJSON Network StoreInput where+  marshalValue net i@StoreInput {} =+    A.object+      [ "coinbase" .= False,+        "txid" .= i.outpoint.hash,+        "output" .= i.outpoint.index,+        "sigscript" .= String (encodeHex i.script),+        "sequence" .= i.sequence,+        "pkscript" .= String (encodeHex i.pkscript),+        "value" .= i.value,+        "address" .= (marshalValue net <$> i.address),+        "witness" .= map encodeHex i.witness+      ]+  marshalValue net i@StoreCoinbase {} =+    A.object+      [ "coinbase" .= True,+        "txid" .= i.outpoint.hash,+        "output" .= i.outpoint.index,+        "sigscript" .= String (encodeHex i.script),+        "sequence" .= i.sequence,+        "pkscript" .= Null,+        "value" .= Null,+        "address" .= Null,+        "witness" .= map encodeHex i.witness+      ]++  marshalEncoding net i@StoreInput {} =+    A.pairs $+      mconcat+        [ "coinbase" `A.pair` A.bool False,+          "txid" `A.pair` toEncoding i.outpoint.hash,+          "output" `A.pair` A.word32 i.outpoint.index,+          "sigscript" `A.pair` hexEncoding (B.fromStrict i.script),+          "sequence" `A.pair` A.word32 i.sequence,+          "pkscript" `A.pair` hexEncoding (B.fromStrict i.pkscript),+          "value" `A.pair` A.word64 i.value,+          "address" `A.pair` maybe A.null_ (marshalEncoding net) i.address,+          "witness" `A.pair` A.list (hexEncoding . B.fromStrict) i.witness+        ]+  marshalEncoding net i@StoreCoinbase {} =+    A.pairs $+      mconcat+        [ "coinbase" .= True,+          "txid" `A.pair` toEncoding i.outpoint.hash,+          "output" `A.pair` A.word32 i.outpoint.index,+          "sigscript" `A.pair` hexEncoding (B.fromStrict i.script),+          "sequence" `A.pair` A.word32 i.sequence,+          "pkscript" `A.pair` A.null_,+          "value" `A.pair` A.null_,+          "address" `A.pair` A.null_,+          "witness" `A.pair` A.list (hexEncoding . B.fromStrict) i.witness+        ]++  unmarshalValue net =+    A.withObject "StoreInput" $ \o -> do+      coinbase <- o .: "coinbase"+      outpoint <- OutPoint <$> o .: "txid" <*> o .: "output"+      sequence <- o .: "sequence"+      witness <- mapM jsonHex =<< o .:? "witness" .!= []+      script <- o .: "sigscript" >>= jsonHex+      if coinbase+        then return StoreCoinbase {..}+        else do+          pkscript <- o .: "pkscript" >>= jsonHex+          value <- o .: "value"+          address <-+            o .: "address"+              >>= maybe (return Nothing) (optional . unmarshalValue net)+          return StoreInput {..}++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+  { -- | input transaction hash+    txid :: !TxHash,+    -- | input position in transaction+    index :: !Word32+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance Serial Spender where+  serialize s = do+    serialize s.txid+    putWord32be s.index+  deserialize = Spender <$> deserialize <*> getWord32be++instance Serialize Spender where+  put = serialize+  get = deserialize++instance Binary Spender where+  put = serialize+  get = deserialize++instance ToJSON Spender where+  toJSON s =+    A.object+      [ "txid" .= s.txid,+        "input" .= s.index+      ]+  toEncoding s =+    A.pairs $+      mconcat+        [ "txid" `A.pair` toEncoding s.txid,+          "input" `A.pair` A.word32 s.index+        ]++instance FromJSON Spender where+  parseJSON =+    A.withObject "Spender" $ \o ->+      Spender <$> o .: "txid" <*> o .: "input"++-- | Output information.+data StoreOutput = StoreOutput+  { value :: !Word64,+    script :: !ByteString,+    spender :: !(Maybe Spender),+    address :: !(Maybe Address)+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance Serial StoreOutput where+  serialize o = do+    putWord64be o.value+    putLengthBytes o.script+    putMaybe serialize o.spender+    putMaybe serialize o.address+  deserialize = do+    value <- getWord64be+    script <- getLengthBytes+    spender <- getMaybe deserialize+    address <- getMaybe deserialize+    return StoreOutput {..}++instance Serialize StoreOutput where+  put = serialize+  get = deserialize++instance Binary StoreOutput where+  put = serialize+  get = deserialize++instance MarshalJSON Network StoreOutput where+  marshalValue net o =+    A.object+      [ "address" .= (marshalValue net <$> o.address),+        "pkscript" .= encodeHex o.script,+        "value" .= o.value,+        "spent" .= isJust o.spender,+        "spender" .= o.spender+      ]++  marshalEncoding net o =+    A.pairs $+      mconcat+        [ "address" `A.pair` maybe A.null_ (marshalEncoding net) o.address,+          "pkscript" `A.pair` hexEncoding (B.fromStrict o.script),+          "value" `A.pair` A.word64 o.value,+          "spent" `A.pair` A.bool (isJust o.spender),+          "spender" `A.pair` toEncoding o.spender+        ]++  unmarshalValue net =+    A.withObject "StoreOutput" $ \o -> do+      value <- o .: "value"+      script <- o .: "pkscript" >>= jsonHex+      spender <- o .: "spender"+      address <-+        o .: "address"+          >>= maybe (return Nothing) (optional . unmarshalValue net)+      return StoreOutput {..}++data Prev = Prev+  { script :: !ByteString,+    value :: !Word64+  }+  deriving (Show, Eq, Ord, Generic, Hashable, NFData)++instance Serial Prev where+  serialize p = do+    putLengthBytes p.script+    putWord64be p.value+  deserialize = do+    script <- getLengthBytes+    value <- getWord64be+    return Prev {..}++instance Binary Prev where+  put = serialize+  get = deserialize++instance Serialize Prev where+  put = serialize+  get = deserialize++toInput :: Ctx -> TxIn -> Maybe Prev -> WitnessStack -> StoreInput+toInput ctx i Nothing w =+  StoreCoinbase+    { outpoint = i.outpoint,+      sequence = i.sequence,+      script = i.script,+      witness = w+    }+toInput ctx i (Just p) w =+  StoreInput+    { outpoint = i.outpoint,+      sequence = i.sequence,+      script = i.script,+      pkscript = p.script,+      value = p.value,+      witness = w,+      address = eitherToMaybe (scriptToAddressBS ctx p.script)+    }++toOutput :: Ctx -> TxOut -> Maybe Spender -> StoreOutput+toOutput ctx o s =+  StoreOutput+    { value = o.value,+      script = o.script,+      spender = s,+      address = eitherToMaybe (scriptToAddressBS ctx o.script)+    }++data TxData = TxData+  { block :: !BlockRef,+    tx :: !Tx,+    prevs :: !(IntMap Prev),+    deleted :: !Bool,+    rbf :: !Bool,+    timestamp :: !Word64,+    spenders :: !(IntMap Spender)+  }+  deriving (Show, Eq, Ord, Generic, NFData)++instance Serial TxData where+  serialize t = do+    serialize t.block+    serialize t.tx+    putIntMap (putWord64be . fromIntegral) serialize t.prevs+    serialize t.deleted+    serialize t.rbf+    putWord64be t.timestamp+    putIntMap (putWord64be . fromIntegral) serialize t.spenders+  deserialize = do+    block <- deserialize+    tx <- deserialize+    prevs <- getIntMap (fromIntegral <$> getWord64be) deserialize+    deleted <- deserialize+    rbf <- deserialize+    timestamp <- getWord64be+    spenders <- getIntMap (fromIntegral <$> getWord64be) deserialize+    return TxData {..}++instance Serialize TxData where+  put = serialize+  get = deserialize++instance Binary TxData where+  put = serialize+  get = deserialize++txDataFee :: TxData -> Word64+txDataFee t =+  if isCoinbaseTx t.tx+    then 0+    else inputs - outputs+  where+    inputs = sum $ map (.value) $ IntMap.elems t.prevs+    outputs = sum $ map (.value) t.tx.outputs++toTransaction :: Ctx -> TxData -> Transaction+toTransaction ctx t =+  Transaction+    { version = t.tx.version,+      locktime = t.tx.locktime,+      block = t.block,+      deleted = t.deleted,+      rbf = t.rbf,+      timestamp = t.timestamp,+      txid = txHash t.tx,+      inputs = ins,+      outputs = outs,+      size =+        fromIntegral $+          B.length $+            runPutS $+              serialize t.tx,+      weight =+        let b = B.length $ runPutS $ serialize witless+            x = B.length $ runPutS $ serialize t.tx+         in fromIntegral $ b * 3 + x,+      fee =+        if any isCoinbase ins+          then 0+          else inv - outv+    }+  where+    ins = zipWith f [0 ..] t.tx.inputs+    witless =+      let Tx {..} = t.tx+       in Tx {witness = [], ..}+    inv = sum (map (.value) ins)+    outs = zipWith g [0 ..] t.tx.outputs+    outv = sum $ map (.value) outs+    ws = take (length t.tx.inputs) $ t.tx.witness <> repeat []+    f n i = toInput ctx i (IntMap.lookup n t.prevs) (ws !! n)+    g n o = toOutput ctx o $ IntMap.lookup n t.spenders++fromTransaction :: Transaction -> TxData+fromTransaction t =+  TxData+    { block = t.block,+      tx = tx,+      deleted = t.deleted,+      rbf = t.rbf,+      timestamp = t.timestamp,+      prevs =+        IntMap.fromList $+          catMaybes $+            zipWith f [0 ..] t.inputs,+      spenders =+        IntMap.fromList $+          catMaybes $+            zipWith g [0 ..] t.outputs+    }+  where+    tx = transactionData t+    f _ StoreCoinbase {} = Nothing+    f n StoreInput {script = s, value = v} =+      Just (n, Prev {script = s, value = v})+    g _ StoreOutput {spender = Nothing} = Nothing+    g n StoreOutput {spender = Just s} = Just (n, s)++-- | Detailed transaction information.+data Transaction = Transaction+  { -- | block information for this transaction+    block :: !BlockRef,+    -- | transaction version+    version :: !Word32,+    -- | lock time+    locktime :: !Word32,+    -- | transaction inputs+    inputs :: ![StoreInput],+    -- | transaction outputs+    outputs :: ![StoreOutput],+    -- | this transaction has been deleted and is no longer valid+    deleted :: !Bool,+    -- | this transaction can be replaced in the mempool+    rbf :: !Bool,+    -- | time the transaction was first seen or time of block+    timestamp :: !Word64,+    -- | transaction id+    txid :: !TxHash,+    -- | serialized transaction size (includes witness data)+    size :: !Word32,+    -- | transaction weight+    weight :: !Word32,+    -- | fees that this transaction pays (0 for coinbase)+    fee :: !Word64+  }+  deriving (Show, Eq, Ord, Generic, Hashable, NFData)++instance Serial Transaction where+  serialize t = do+    serialize t.block+    putWord32be t.version+    putWord32be t.locktime+    putList serialize t.inputs+    putList serialize t.outputs+    serialize t.deleted+    serialize t.rbf+    putWord64be t.timestamp+    serialize t.txid+    putWord32be t.size+    putWord32be t.weight+    putWord64be t.fee+  deserialize = do+    block <- deserialize+    version <- getWord32be+    locktime <- getWord32be+    inputs <- getList deserialize+    outputs <- getList deserialize+    deleted <- deserialize+    rbf <- deserialize+    timestamp <- getWord64be+    txid <- deserialize+    size <- getWord32be+    weight <- getWord32be+    fee <- getWord64be+    return Transaction {..}++instance Serialize Transaction where+  put = serialize+  get = deserialize++instance Binary Transaction where+  put = serialize+  get = deserialize++transactionData :: Transaction -> Tx+transactionData t =+  Tx+    { inputs = map i t.inputs,+      outputs = map o t.outputs,+      version = t.version,+      locktime = t.locktime,+      witness = w $ map (.witness) t.inputs+    }+  where+    i StoreCoinbase {..} = TxIn {..}+    i StoreInput {..} = TxIn {..}+    o StoreOutput {..} = TxOut {..}+    w xs+      | all null xs = []+      | otherwise = xs++instance MarshalJSON Network Transaction where+  marshalValue net t =+    A.object+      [ "txid" .= t.txid,+        "size" .= t.size,+        "version" .= t.version,+        "locktime" .= t.locktime,+        "fee" .= t.fee,+        "inputs" .= map (marshalValue net) t.inputs,+        "outputs" .= map (marshalValue net) t.outputs,+        "block" .= t.block,+        "deleted" .= t.deleted,+        "time" .= t.timestamp,+        "rbf" .= t.rbf,+        "weight" .= t.weight+      ]++  marshalEncoding net t =+    A.pairs $+      mconcat+        [ "txid" `A.pair` toEncoding t.txid,+          "size" `A.pair` A.word32 t.size,+          "version" `A.pair` A.word32 t.version,+          "locktime" `A.pair` A.word32 t.locktime,+          "fee" `A.pair` A.word64 t.fee,+          "inputs" `A.pair` A.list (marshalEncoding net) t.inputs,+          "outputs" `A.pair` A.list (marshalEncoding net) t.outputs,+          "block" `A.pair` toEncoding t.block,+          "deleted" `A.pair` A.bool t.deleted,+          "time" `A.pair` A.word64 t.timestamp,+          "rbf" `A.pair` A.bool t.rbf,+          "weight" `A.pair` A.word32 t.weight+        ]++  unmarshalValue net = A.withObject "Transaction" $ \o -> do+    version <- o .: "version"+    locktime <- o .: "locktime"+    inputs <- o .: "inputs" >>= mapM (unmarshalValue net)+    outputs <- o .: "outputs" >>= mapM (unmarshalValue net)+    block <- o .: "block"+    deleted <- o .: "deleted"+    timestamp <- o .: "time"+    rbf <- o .:? "rbf" .!= False+    weight <- o .:? "weight" .!= 0+    size <- o .: "size"+    txid <- o .: "txid"+    fee <- o .: "fee"+    return Transaction {..}++-- | Information about a connected peer.+data PeerInfo = PeerInfo+  { -- | user agent string+    userAgent :: !ByteString,+    -- | network address+    address :: !String,+    -- | version number+    version :: !Word32,+    -- | services field+    services :: !Word64,+    -- | will relay transactions+    relay :: !Bool+  }+  deriving (Show, Eq, Ord, Generic, NFData)++instance Serial PeerInfo where+  serialize p = do+    putLengthBytes p.userAgent+    putLengthBytes $ T.encodeUtf8 $ T.pack p.address+    putWord32be p.version+    putWord64be p.services+    serialize p.relay+  deserialize = do+    userAgent <- getLengthBytes+    address <- T.unpack . T.decodeUtf8 <$> getLengthBytes+    version <- getWord32be+    services <- getWord64be+    relay <- deserialize+    return PeerInfo {..}++instance Serialize PeerInfo where+  put = serialize+  get = deserialize++instance Binary PeerInfo where+  put = serialize+  get = deserialize++instance ToJSON PeerInfo where+  toJSON p =+    A.object+      [ "useragent" .= String (T.decodeUtf8 p.userAgent),+        "address" .= p.address,+        "version" .= p.version,+        "services"+          .= String (encodeHex $ runPutS $ serialize p.services),+        "relay" .= p.relay+      ]+  toEncoding p =+    A.pairs $+      mconcat+        [ "useragent" `A.pair` A.text (T.decodeUtf8 p.userAgent),+          "address" `A.pair` toEncoding p.address,+          "version" `A.pair` A.word32 p.version,+          "services" `A.pair` hexEncoding (runPutL $ serialize p.services),+          "relay" `A.pair` A.bool p.relay+        ]++instance FromJSON PeerInfo where+  parseJSON =+    A.withObject "PeerInfo" $ \o -> do+      String userAgentText <- o .: "useragent"+      let userAgent = T.encodeUtf8 userAgentText+      address <- o .: "address"+      version <- o .: "version"+      services <-+        o .: "services" >>= jsonHex >>= \b ->+          case runGetS deserialize b of+            Left e -> fail $ "Could not decode services: " <> e+            Right s -> return s+      relay <- o .: "relay"+      return PeerInfo {..}++-- | Address balances for an extended public key.+data XPubBal = XPubBal+  { path :: ![KeyIndex],+    balance :: !Balance+  }+  deriving (Show, Ord, Eq, Generic, NFData)++instance Serial XPubBal where+  serialize b = do+    putList putWord32be b.path+    serialize b.balance+  deserialize = do+    path <- getList getWord32be+    balance <- deserialize+    return XPubBal {..}++instance Serialize XPubBal where+  put = serialize+  get = deserialize++instance Binary XPubBal where+  put = serialize+  get = deserialize++instance MarshalJSON Network XPubBal where+  marshalValue net b =+    A.object+      [ "path" .= b.path,+        "balance" .= marshalValue net b.balance+      ]++  marshalEncoding net b =+    A.pairs $+      mconcat+        [ "path" `A.pair` A.list A.word32 b.path,+          "balance" `A.pair` marshalEncoding net b.balance+        ]++  unmarshalValue net =+    A.withObject "XPubBal" $ \o -> do+      path <- o .: "path"+      balance <- unmarshalValue net =<< o .: "balance"+      return XPubBal {..}++-- | Unspent transaction for extended public key.+data XPubUnspent = XPubUnspent+  { unspent :: !Unspent,+    path :: ![KeyIndex]+  }+  deriving (Show, Eq, Ord, Generic, NFData)++instance Serial XPubUnspent where+  serialize u = do+    putList putWord32be u.path+    serialize u.unspent+  deserialize = do+    path <- getList getWord32be+    unspent <- deserialize+    return XPubUnspent {..}++instance Serialize XPubUnspent where+  put = serialize+  get = deserialize++instance Binary XPubUnspent where+  put = serialize+  get = deserialize++instance MarshalJSON Network XPubUnspent where+  marshalValue net u =+    A.object+      [ "unspent" .= marshalValue net u.unspent,+        "path" .= u.path+      ]++  marshalEncoding net u =+    A.pairs $+      mconcat+        [ "unspent" `A.pair` marshalEncoding net u.unspent,+          "path" `A.pair` A.list A.word32 u.path+        ]++  unmarshalValue net =+    A.withObject "XPubUnspent" $ \o -> do+      unspent <- o .: "unspent" >>= unmarshalValue net+      path <- o .: "path"+      return XPubUnspent {..}++data XPubSummary = XPubSummary+  { confirmed :: !Word64,+    unconfirmed :: !Word64,+    received :: !Word64,+    utxo :: !Word64,+    external :: !Word32,+    change :: !Word32+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial XPubSummary where+  serialize s = do+    putWord64be s.confirmed+    putWord64be s.unconfirmed+    putWord64be s.received+    putWord64be s.utxo+    putWord32be s.external+    putWord32be s.change+  deserialize = do+    confirmed <- getWord64be+    unconfirmed <- getWord64be+    received <- getWord64be+    utxo <- getWord64be+    external <- getWord32be+    change <- getWord32be+    return XPubSummary {..}++instance Binary XPubSummary where+  put = serialize+  get = deserialize++instance Serialize XPubSummary where+  put = serialize+  get = deserialize++instance ToJSON XPubSummary where+  toJSON s =+    A.object+      [ "balance"+          .= A.object+            [ "confirmed" .= s.confirmed,+              "unconfirmed" .= s.unconfirmed,+              "received" .= s.received,+              "utxo" .= s.utxo+            ],+        "indices"+          .= A.object+            [ "change" .= s.change,+              "external" .= s.external+            ]+      ]+  toEncoding s =+    A.pairs $+      mconcat+        [ A.pair "balance" $+            A.pairs $+              mconcat+                [ "confirmed" `A.pair` A.word64 s.confirmed,+                  "unconfirmed" `A.pair` A.word64 s.unconfirmed,+                  "received" `A.pair` A.word64 s.received,+                  "utxo" `A.pair` A.word64 s.utxo+                ],+          A.pair "indices" $+            A.pairs $+              mconcat+                [ "change" `A.pair` A.word32 s.change,+                  "external" `A.pair` A.word32 s.external+                ]+        ]++instance FromJSON XPubSummary where+  parseJSON =+    A.withObject "XPubSummary" $ \o -> do+      b <- o .: "balance"+      i <- o .: "indices"+      confirmed <- b .: "confirmed"+      unconfirmed <- b .: "unconfirmed"+      received <- b .: "received"+      utxo <- b .: "utxo"+      change <- i .: "change"+      external <- i .: "external"+      return XPubSummary {..}++class Healthy a where+  isOK :: a -> Bool++data BlockHealth = BlockHealth+  { headers :: !BlockHeight,+    blocks :: !BlockHeight,+    max :: !Int32+  }+  deriving (Show, Eq, Generic, NFData)++instance Serial BlockHealth where+  serialize h = do+    serialize (isOK h)+    putWord32be h.headers+    putWord32be h.blocks+    putInt32be h.max+  deserialize = do+    k <- deserialize+    headers <- getWord32be+    blocks <- getWord32be+    max <- getInt32be+    let h = BlockHealth {..}+    unless (k == isOK h) $ fail "Inconsistent health check"+    return h++instance Serialize BlockHealth where+  put = serialize+  get = deserialize++instance Binary BlockHealth where+  put = serialize+  get = deserialize++instance Healthy BlockHealth where+  isOK x =+    h - b <= x.max+    where+      h = fromIntegral x.headers+      b = fromIntegral x.blocks++instance ToJSON BlockHealth where+  toJSON h =+    A.object+      [ "headers" .= h.headers,+        "blocks" .= h.blocks,+        "diff" .= diff,+        "max" .= h.max,+        "ok" .= isOK h+      ]+    where+      diff = toInteger h.headers - toInteger h.blocks++instance FromJSON BlockHealth where+  parseJSON =+    A.withObject "BlockHealth" $ \o -> do+      headers <- o .: "headers"+      blocks <- o .: "blocks"+      max <- o .: "max"+      return BlockHealth {..}++data TimeHealth = TimeHealth+  { age :: !Int64,+    max :: !Int64+  }+  deriving (Show, Eq, Generic, NFData)++instance Serial TimeHealth where+  serialize h = do+    serialize (isOK h)+    putInt64be h.age+    putInt64be h.max+  deserialize = do+    k <- deserialize+    age <- getInt64be+    max <- getInt64be+    let t = TimeHealth {..}+    unless (k == isOK t) $ fail "Inconsistent health check"+    return t++instance Binary TimeHealth where+  put = serialize+  get = deserialize++instance Serialize TimeHealth where+  put = serialize+  get = deserialize++instance Healthy TimeHealth where+  isOK TimeHealth {..} =+    age <= max++instance ToJSON TimeHealth where+  toJSON h =+    A.object+      [ "age" .= h.age,+        "max" .= h.max,+        "ok" .= isOK h+      ]++instance FromJSON TimeHealth where+  parseJSON =+    A.withObject "TimeHealth" $ \o -> do+      age <- o .: "age"+      max <- o .: "max"+      return TimeHealth {..}++data CountHealth = CountHealth+  { count :: !Int64,+    min :: !Int64+  }+  deriving (Show, Eq, Generic, NFData)++instance Serial CountHealth where+  serialize h = do+    serialize (isOK h)+    putInt64be h.count+    putInt64be h.min+  deserialize = do+    k <- deserialize+    count <- getInt64be+    min <- getInt64be+    let c = CountHealth {..}+    unless (k == isOK c) $ fail "Inconsistent health check"+    return c++instance Serialize CountHealth where+  put = serialize+  get = deserialize++instance Binary CountHealth where+  put = serialize+  get = deserialize++instance Healthy CountHealth where+  isOK CountHealth {..} = min <= count++instance ToJSON CountHealth where+  toJSON h =+    A.object+      [ "count" .= h.count,+        "min" .= h.min,+        "ok" .= isOK h+      ]++instance FromJSON CountHealth where+  parseJSON =+    A.withObject "CountHealth" $ \o -> do+      count <- o .: "count"+      min <- o .: "min"+      return CountHealth {..}++data MaxHealth = MaxHealth+  { count :: !Int64,+    max :: !Int64+  }+  deriving (Show, Eq, Generic, NFData)++instance Serial MaxHealth where+  serialize h = do+    serialize $ isOK h+    putInt64be h.count+    putInt64be h.max+  deserialize = do+    k <- deserialize+    count <- getInt64be+    max <- getInt64be+    let h = MaxHealth {..}+    unless (k == isOK h) $ fail "Inconsistent health check"+    return h++instance Binary MaxHealth where+  put = serialize+  get = deserialize++instance Serialize MaxHealth where+  put = serialize+  get = deserialize++instance Healthy MaxHealth where+  isOK MaxHealth {..} = count <= max++instance ToJSON MaxHealth where+  toJSON h =+    A.object+      [ "count" .= h.count,+        "max" .= h.max,+        "ok" .= isOK h+      ]++instance FromJSON MaxHealth where+  parseJSON =+    A.withObject "MaxHealth" $ \o -> do+      count <- o .: "count"+      max <- o .: "max"+      return MaxHealth {..}++data HealthCheck = HealthCheck+  { blocks :: !BlockHealth,+    lastBlock :: !TimeHealth,+    lastTx :: !TimeHealth,+    pendingTxs :: !MaxHealth,+    peers :: !CountHealth,+    network :: !String,+    version :: !String,+    time :: !Word64+  }+  deriving (Show, Eq, Generic, NFData)++instance Serial HealthCheck where+  serialize h = do+    serialize $ isOK h+    serialize h.blocks+    serialize h.lastBlock+    serialize h.lastTx+    serialize h.pendingTxs+    serialize h.peers+    putLengthBytes $ T.encodeUtf8 $ T.pack h.network+    putLengthBytes $ T.encodeUtf8 $ T.pack h.version+    putWord64be h.time+  deserialize = do+    k <- deserialize+    blocks <- deserialize+    lastBlock <- deserialize+    lastTx <- deserialize+    pendingTxs <- deserialize+    peers <- deserialize+    network <- T.unpack . T.decodeUtf8 <$> getLengthBytes+    version <- T.unpack . T.decodeUtf8 <$> getLengthBytes+    time <- getWord64be+    let h = HealthCheck {..}+    unless (k == isOK h) $ fail "Inconsistent health check"+    return h++instance Binary HealthCheck where+  put = serialize+  get = deserialize++instance Serialize HealthCheck where+  put = serialize+  get = deserialize++instance Healthy HealthCheck where+  isOK h =+    and+      [ isOK h.blocks,+        isOK h.lastBlock,+        isOK h.lastTx,+        isOK h.pendingTxs,+        isOK h.peers+      ]++instance ToJSON HealthCheck where+  toJSON h =+    A.object+      [ "blocks" .= h.blocks,+        "last-block" .= h.lastBlock,+        "last-tx" .= h.lastTx,+        "pending-txs" .= h.pendingTxs,+        "peers" .= h.peers,+        "net" .= h.network,+        "version" .= h.version,+        "time" .= show (posixSecondsToUTCTime $ fromIntegral h.time),+        "ok" .= isOK h+      ]++instance FromJSON HealthCheck where+  parseJSON =+    A.withObject "HealthCheck" $ \o -> do+      blocks <- o .: "blocks"+      lastBlock <- o .: "last-block"+      lastTx <- o .: "last-tx"+      pendingTxs <- o .: "pending-txs"+      peers <- o .: "peers"+      network <- o .: "net"+      version <- o .: "version"+      utcTime <- read <$> o .: "time"+      let time = round $ utcTimeToPOSIXSeconds utcTime+      return HealthCheck {..}++data Event+  = EventBlock !BlockHash+  | EventTx !TxHash+  deriving (Show, Eq, Generic, NFData)++instance Serial Event where+  serialize (EventBlock bh) = putWord8 0x00 >> serialize bh+  serialize (EventTx th) = putWord8 0x01 >> serialize th+  deserialize =+    getWord8 >>= \case+      0x00 -> EventBlock <$> deserialize+      0x01 -> EventTx <$> deserialize+      _ -> fail "Not an Event"++instance Serialize Event where+  put = serialize+  get = deserialize++instance Binary Event where+  put = serialize+  get = deserialize++instance ToJSON Event where+  toJSON (EventTx h) =+    A.object ["type" .= String "tx", "id" .= h]+  toJSON (EventBlock h) =+    A.object ["type" .= String "block", "id" .= h]+  toEncoding (EventTx h) =+    A.pairs ("type" `A.pair` A.text "tx" <> "id" `A.pair` toEncoding h)+  toEncoding (EventBlock h) =+    A.pairs ("type" `A.pair` A.text "block" <> "id" `A.pair` toEncoding 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 {get :: a}+  deriving (Show, Eq, Generic, NFData)++instance (Serial a) => Serial (GenericResult a) where+  serialize (GenericResult x) = serialize x+  deserialize = GenericResult <$> deserialize++instance (Serial a) => Serialize (GenericResult a) where+  put = serialize+  get = deserialize++instance (Serial a) => Binary (GenericResult a) where+  put = serialize+  get = deserialize++instance (ToJSON a) => ToJSON (GenericResult a) where+  toJSON (GenericResult b) = A.object ["result" .= b]+  toEncoding (GenericResult b) = A.pairs ("result" `A.pair` toEncoding b)++instance (FromJSON a) => FromJSON (GenericResult a) where+  parseJSON =+    A.withObject "GenericResult" $ \o -> GenericResult <$> o .: "result"++newtype RawResult a = RawResult {get :: a}+  deriving (Show, Eq, Generic, NFData)++instance (Serial a) => Serial (RawResult a) where+  serialize (RawResult x) = serialize x+  deserialize = RawResult <$> deserialize++instance (Serial a) => Serialize (RawResult a) where+  put = serialize+  get = deserialize++instance (Serial a) => Binary (RawResult a) where+  put = serialize+  get = deserialize++instance (Serial a) => ToJSON (RawResult a) where+  toJSON (RawResult b) =+    A.object ["result" .= String (encodeHex $ runPutS $ serialize b)]+  toEncoding (RawResult b) =+    A.pairs $ "result" `A.pair` hexEncoding (runPutL $ serialize b)++instance (Serial a) => FromJSON (RawResult a) where+  parseJSON =+    A.withObject "RawResult" $ \o -> do+      res <- o .: "result"+      let m =+            eitherToMaybe . Bytes.Get.runGetS deserialize+              =<< decodeHex res+      maybe mzero (return . RawResult) m++newtype SerialList a = SerialList {get :: [a]}+  deriving (Show, Eq, Generic, NFData)++instance Semigroup (SerialList a) where+  SerialList a <> SerialList b = SerialList (a <> b)++instance Monoid (SerialList a) where+  mempty = SerialList mempty++instance (Serial a) => Serial (SerialList a) where+  serialize (SerialList ls) = putList serialize ls+  deserialize = SerialList <$> getList deserialize++instance (ToJSON a) => ToJSON (SerialList a) where+  toJSON (SerialList ls) = toJSON ls+  toEncoding (SerialList ls) = A.list toEncoding ls++instance (FromJSON a) => FromJSON (SerialList a) where+  parseJSON = fmap SerialList . parseJSON++newtype RawResultList a = RawResultList {get :: [a]}+  deriving (Show, Eq, Generic, NFData)++instance (Serial a) => Serial (RawResultList a) where+  serialize (RawResultList xs) =+    mapM_ serialize xs+  deserialize = RawResultList <$> go+    where+      go =+        isEmpty >>= \case+          True -> return []+          False -> (:) <$> deserialize <*> go++instance (Serial a) => Serialize (RawResultList a) where+  put = serialize+  get = deserialize++instance (Serial a) => Binary (RawResultList a) where+  put = serialize+  get = deserialize++instance Semigroup (RawResultList a) where+  (RawResultList a) <> (RawResultList b) = RawResultList $ a <> b++instance Monoid (RawResultList a) where+  mempty = RawResultList mempty++instance (Serial a) => ToJSON (RawResultList a) where+  toJSON (RawResultList xs) =+    toJSON $ String . encodeHex . runPutS . serialize <$> xs+  toEncoding (RawResultList xs) =+    A.list (hexEncoding . runPutL . serialize) xs++instance (Serial a) => FromJSON (RawResultList a) where+  parseJSON =+    A.withArray "RawResultList" $ \vec ->+      RawResultList <$> mapM parseElem (toList vec)+    where+      deser = eitherToMaybe . runGetS deserialize+      parseElem =+        A.withText "RawResultListItem" $ \t ->+          maybe mzero return (decodeHex t >>= deser)++newtype TxId+  = TxId TxHash+  deriving (Show, Eq, Generic, NFData)++instance Serial TxId where+  serialize (TxId h) = serialize h+  deserialize = TxId <$> deserialize++instance Serialize TxId where+  put = serialize+  get = deserialize++instance Binary TxId where+  put = serialize+  get = deserialize++instance ToJSON TxId where+  toJSON (TxId h) = A.object ["txid" .= h]+  toEncoding (TxId h) = A.pairs ("txid" `A.pair` toEncoding h)++instance FromJSON TxId where+  parseJSON = A.withObject "txid" $ \o -> TxId <$> o .: "txid"++data Except+  = ThingNotFound+  | ServerError+  | BadRequest+  | UserError !String+  | StringError !String+  | TxIndexConflict ![TxHash]+  | ServerTimeout+  | RequestTooLarge+  deriving (Show, Eq, Ord, Generic, NFData)++instance Exception Except++instance ScottyError Except where+  stringError = StringError+  showError = LazyText.pack . show++instance ToJSON Except where+  toJSON e =+    A.object $+      case e of+        ThingNotFound ->+          [ "error" .= String "not-found-or-invalid-arg",+            "message" .= String "Item not found or argument invalid"+          ]+        ServerError ->+          [ "error" .= String "server-error",+            "message" .= String "Server error"+          ]+        BadRequest ->+          [ "error" .= String "bad-request",+            "message" .= String "Invalid request"+          ]+        UserError msg' ->+          [ "error" .= String "user-error",+            "message" .= String (cs msg')+          ]+        StringError msg' ->+          [ "error" .= String "string-error",+            "message" .= String (cs msg')+          ]+        TxIndexConflict txids ->+          [ "error" .= String "multiple-tx-index",+            "message" .= String "Multiple txs match that index",+            "txids" .= txids+          ]+        ServerTimeout ->+          [ "error" .= String "server-timeout",+            "message" .= String "Request is taking too long"+          ]+        RequestTooLarge ->+          [ "error" .= String "request-too-large",+            "message" .= String "Request body too large"+          ]++instance FromJSON Except where+  parseJSON =+    A.withObject "Except" $ \o -> do+      ctr <- o .: "error"+      msg' <- o .:? "message" .!= ""+      case ctr of+        String "not-found-or-invalid-arg" ->+          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 "multiple-tx-index" -> do+          txids <- o .: "txids"+          return $ TxIndexConflict txids+        String "server-timeout" ->+          return ServerTimeout+        String "request-too-large" ->+          return RequestTooLarge+        _ -> mzero++---------------------------------------+-- Blockchain.info API Compatibility --+---------------------------------------++toIntTxId :: TxHash -> Word64+toIntTxId h =+  let bs = runPutS (serialize h)+      Right w64 = runGetS getWord64be bs+   in w64 `shift` (-11)++data BinfoBlockId+  = BinfoBlockHash !BlockHash+  | BinfoBlockIndex !Word32+  deriving (Eq, Show, Read, Generic, NFData)++instance Parsable BinfoBlockId where+  parseParam t =+    hex <> igr+    where+      hex = case hexToBlockHash (LazyText.toStrict t) of+        Nothing -> Left "could not decode txid"+        Just h -> Right $ BinfoBlockHash h+      igr = BinfoBlockIndex <$> parseParam t++data BinfoTxId+  = BinfoTxIdHash !TxHash+  | BinfoTxIdIndex !Word64+  deriving (Eq, Show, Read, Generic, NFData)++encodeBinfoTxId :: Bool -> TxHash -> BinfoTxId+encodeBinfoTxId False = BinfoTxIdHash+encodeBinfoTxId True = BinfoTxIdIndex . toIntTxId++instance Parsable BinfoTxId where+  parseParam t =+    hex <> igr+    where+      hex =+        case hexToTxHash (LazyText.toStrict t) of+          Nothing -> Left "could not decode txid"+          Just h -> Right $ BinfoTxIdHash h+      igr = BinfoTxIdIndex <$> parseParam t++instance ToJSON BinfoTxId where+  toJSON (BinfoTxIdHash h) = toJSON h+  toJSON (BinfoTxIdIndex i) = toJSON i+  toEncoding (BinfoTxIdHash h) = toEncoding h+  toEncoding (BinfoTxIdIndex i) = A.word64 i++instance FromJSON BinfoTxId where+  parseJSON v =+    BinfoTxIdHash <$> parseJSON v+      <|> BinfoTxIdIndex <$> parseJSON v++data BinfoFilter+  = BinfoFilterAll+  | BinfoFilterSent+  | BinfoFilterReceived+  | BinfoFilterMoved+  | BinfoFilterConfirmed+  | BinfoFilterMempool+  deriving (Eq, Show, Generic, NFData)++instance Parsable BinfoFilter where+  parseParam t =+    parseParam t >>= \case+      (0 :: Int) -> return BinfoFilterAll+      1 -> return BinfoFilterSent+      2 -> return BinfoFilterReceived+      3 -> return BinfoFilterMoved+      5 -> return BinfoFilterConfirmed+      6 -> return BinfoFilterAll+      7 -> return BinfoFilterMempool+      _ -> Left "could not parse filter parameter"++data BinfoMultiAddr = BinfoMultiAddr+  { addresses :: ![BinfoBalance],+    wallet :: !BinfoWallet,+    txs :: ![BinfoTx],+    info :: !BinfoInfo,+    recommendFee :: !Bool,+    cashAddr :: !Bool+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoMultiAddr where+  marshalValue (net', ctx) m =+    A.object $+      [ "addresses" .= map (marshalValue (net, ctx)) m.addresses,+        "wallet" .= m.wallet,+        "txs" .= map (marshalValue (net, ctx)) m.txs,+        "info" .= m.info,+        "recommend_include_fee" .= m.recommendFee+      ]+        ++ ["cash_addr" .= True | m.cashAddr]+    where+      net = if not m.cashAddr && net' == bch then btc else net'++  unmarshalValue (net, ctx) =+    A.withObject "BinfoMultiAddr" $ \o -> do+      addresses <- mapM (unmarshalValue (net, ctx)) =<< o .: "addresses"+      wallet <- o .: "wallet"+      txs <- mapM (unmarshalValue (net, ctx)) =<< o .: "txs"+      info <- o .: "info"+      recommendFee <- o .: "recommend_include_fee"+      cashAddr <- o .:? "cash_addr" .!= False+      return BinfoMultiAddr {..}++  marshalEncoding (net', ctx) m =+    A.pairs $+      mconcat+        [ "addresses" `A.pair` A.list (marshalEncoding (net, ctx)) m.addresses,+          "wallet" `A.pair` toEncoding m.wallet,+          "txs" `A.pair` A.list (marshalEncoding (net, ctx)) m.txs,+          "info" `A.pair` toEncoding m.info,+          "recommend_include_fee" `A.pair` A.bool m.recommendFee,+          if m.cashAddr then "cash_addr" `A.pair` A.bool True else mempty+        ]+    where+      net = if not m.cashAddr && net' == bch then btc else net'++data BinfoRawAddr = BinfoRawAddr+  { address :: !BinfoAddr,+    balance :: !Word64,+    ntx :: !Word64,+    utxo :: !Word64,+    received :: !Word64,+    sent :: !Int64,+    txs :: ![BinfoTx]+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoRawAddr where+  marshalValue (net, ctx) r =+    A.object+      [ "hash160" .= h160,+        "address" .= address,+        "n_tx" .= r.ntx,+        "n_unredeemed" .= r.utxo,+        "total_received" .= r.received,+        "total_sent" .= r.sent,+        "final_balance" .= r.balance,+        "txs" .= map (marshalValue (net, ctx)) r.txs+      ]+    where+      address = case r.address of+        BinfoAddr a -> marshalValue net a+        BinfoXpub x -> marshalValue (net, ctx) x+      h160 =+        encodeHex . runPutS . serialize+          <$> case r.address of+            BinfoAddr a -> case a of+              PubKeyAddress h -> Just h+              ScriptAddress h -> Just h+              WitnessPubKeyAddress h -> Just h+              _ -> Nothing+            _ -> Nothing++  marshalEncoding (net, ctx) r =+    A.pairs $+      mconcat+        [ "hash160" `A.pair` fromMaybe A.null_ h160,+          "address" `A.pair` address,+          "n_tx" `A.pair` A.word64 r.ntx,+          "n_unredeemed" `A.pair` A.word64 r.utxo,+          "total_received" `A.pair` A.word64 r.received,+          "total_sent" `A.pair` A.int64 r.sent,+          "final_balance" `A.pair` A.word64 r.balance,+          "txs" `A.pair` A.list (marshalEncoding (net, ctx)) r.txs+        ]+    where+      address = case r.address of+        BinfoAddr a -> marshalEncoding net a+        BinfoXpub x -> marshalEncoding (net, ctx) x+      h160 =+        hexEncoding . runPutL . serialize+          <$> case r.address of+            BinfoAddr a -> case a of+              PubKeyAddress h -> Just h+              ScriptAddress h -> Just h+              WitnessPubKeyAddress h -> Just h+              _ -> Nothing+            _ -> Nothing++  unmarshalValue (net, ctx) =+    A.withObject "BinfoRawAddr" $ \o -> do+      addr <- o .: "address"+      address <-+        BinfoAddr <$> unmarshalValue net addr+          <|> BinfoXpub <$> unmarshalValue (net, ctx) addr+      balance <- o .: "final_balance"+      utxo <- o .: "n_unredeemed"+      ntx <- o .: "n_tx"+      received <- o .: "total_received"+      sent <- o .: "total_sent"+      txs <- mapM (unmarshalValue (net, ctx)) =<< o .: "txs"+      return BinfoRawAddr {..}++data BinfoShortBal = BinfoShortBal+  { final :: !Word64,+    ntx :: !Word64,+    received :: !Word64+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance ToJSON BinfoShortBal where+  toJSON b =+    A.object+      [ "final_balance" .= b.final,+        "n_tx" .= b.ntx,+        "total_received" .= b.received+      ]+  toEncoding b =+    A.pairs $+      mconcat+        [ "final_balance" `A.pair` A.word64 b.final,+          "n_tx" `A.pair` A.word64 b.ntx,+          "total_received" `A.pair` A.word64 b.received+        ]++instance FromJSON BinfoShortBal where+  parseJSON =+    A.withObject "BinfoShortBal" $ \o -> do+      final <- o .: "final_balance"+      ntx <- o .: "n_tx"+      received <- o .: "total_received"+      return BinfoShortBal {..}++data BinfoBalance+  = BinfoAddrBalance+      { address :: !Address,+        txs :: !Word64,+        received :: !Word64,+        sent :: !Word64,+        balance :: !Word64+      }+  | BinfoXPubBalance+      { xpub :: !XPubKey,+        txs :: !Word64,+        received :: !Word64,+        sent :: !Word64,+        balance :: !Word64,+        external :: !Word32,+        change :: !Word32+      }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoBalance where+  marshalValue (net, ctx) b@BinfoAddrBalance {} =+    A.object+      [ "address" .= marshalValue net b.address,+        "final_balance" .= b.balance,+        "n_tx" .= b.txs,+        "total_received" .= b.received,+        "total_sent" .= b.sent+      ]+  marshalValue (net, ctx) b@BinfoXPubBalance {} =+    A.object+      [ "address" .= marshalValue (net, ctx) b.xpub,+        "change_index" .= b.change,+        "account_index" .= b.external,+        "final_balance" .= b.balance,+        "n_tx" .= b.txs,+        "total_received" .= b.received,+        "total_sent" .= b.sent+      ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoBalance" $ \o -> x o <|> a o+    where+      x o = do+        xpub <- unmarshalValue (net, ctx) =<< o .: "address"+        change <- o .: "change_index"+        external <- o .: "account_index"+        balance <- o .: "final_balance"+        txs <- o .: "n_tx"+        received <- o .: "total_received"+        sent <- o .: "total_sent"+        return BinfoXPubBalance {..}+      a o = do+        address <- unmarshalValue net =<< o .: "address"+        balance <- o .: "final_balance"+        txs <- o .: "n_tx"+        received <- o .: "total_received"+        sent <- o .: "total_sent"+        return BinfoAddrBalance {..}++  marshalEncoding (net, ctx) b@BinfoAddrBalance {} =+    A.pairs $+      mconcat+        [ "address" `A.pair` marshalEncoding net b.address,+          "final_balance" `A.pair` A.word64 b.balance,+          "n_tx" `A.pair` A.word64 b.txs,+          "total_received" `A.pair` A.word64 b.received,+          "total_sent" `A.pair` A.word64 b.sent+        ]+  marshalEncoding (net, ctx) b@BinfoXPubBalance {} =+    A.pairs $+      mconcat+        [ "address" `A.pair` marshalEncoding (net, ctx) b.xpub,+          "change_index" `A.pair` A.word32 b.change,+          "account_index" `A.pair` A.word32 b.external,+          "final_balance" `A.pair` A.word64 b.balance,+          "n_tx" `A.pair` A.word64 b.txs,+          "total_received" `A.pair` A.word64 b.received,+          "total_sent" `A.pair` A.word64 b.sent+        ]++instance MarshalJSON (Network, Ctx) [BinfoBalance] where+  marshalValue (net, ctx) addrs =+    toJSON $ map (marshalValue (net, ctx)) addrs+  marshalEncoding (net, ctx) =+    A.list (marshalEncoding (net, ctx))+  unmarshalValue (net, ctx) =+    A.withArray "[BinfoBalance]" $+      fmap V.toList . mapM (unmarshalValue (net, ctx))++data BinfoWallet = BinfoWallet+  { balance :: !Word64,+    txs :: !Word64,+    filtered :: !Word64,+    received :: !Word64,+    sent :: !Word64+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoWallet where+  toJSON w =+    A.object+      [ "final_balance" .= w.balance,+        "n_tx" .= w.txs,+        "n_tx_filtered" .= w.filtered,+        "total_received" .= w.received,+        "total_sent" .= w.sent+      ]+  toEncoding w =+    A.pairs $+      mconcat+        [ "final_balance" `A.pair` A.word64 w.balance,+          "n_tx" `A.pair` A.word64 w.txs,+          "n_tx_filtered" `A.pair` A.word64 w.filtered,+          "total_received" `A.pair` A.word64 w.received,+          "total_sent" `A.pair` A.word64 w.sent+        ]++instance FromJSON BinfoWallet where+  parseJSON =+    A.withObject "BinfoWallet" $ \o -> do+      balance <- o .: "final_balance"+      txs <- o .: "n_tx"+      filtered <- o .: "n_tx_filtered"+      received <- o .: "total_received"+      sent <- o .: "total_sent"+      return BinfoWallet {..}++binfoHexValue :: Word64 -> Text+binfoHexValue w64 =+  encodeHex $+    if B.null bs || B.head bs `testBit` 7+      then B.cons 0x00 bs+      else bs+  where+    bs =+      B.dropWhile (== 0x00) $+        runPutS $+          serialize w64++data BinfoUnspent = BinfoUnspent+  { txid :: !TxHash,+    index :: !Word32,+    script :: !ByteString,+    value :: !Word64,+    confirmations :: !Int32,+    txidx :: !BinfoTxId,+    xpub :: !(Maybe BinfoXPubPath)+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoUnspent where+  marshalValue (net, ctx) u =+    A.object $+      [ "tx_hash_big_endian" .= u.txid,+        "tx_hash" .= encodeHex (runPutS (serialize u.txid.get)),+        "tx_output_n" .= u.index,+        "script" .= encodeHex u.script,+        "value" .= u.value,+        "value_hex" .= binfoHexValue u.value,+        "confirmations" .= u.confirmations,+        "tx_index" .= u.txidx+      ]+        <> [ "xpub" .= marshalValue (net, ctx) x+             | x <- maybeToList u.xpub+           ]++  marshalEncoding (net, ctx) u =+    A.pairs $+      mconcat+        [ "tx_hash_big_endian" `A.pair` toEncoding u.txid,+          "tx_hash" `A.pair` hexEncoding (runPutL (serialize u.txid.get)),+          "tx_output_n" `A.pair` A.word32 u.index,+          "script" `A.pair` hexEncoding (B.fromStrict u.script),+          "value" `A.pair` A.word64 u.value,+          "value_hex" `A.pair` A.text (binfoHexValue u.value),+          "confirmations" `A.pair` A.int32 u.confirmations,+          "tx_index" `A.pair` toEncoding u.txidx,+          maybe mempty (("xpub" `A.pair`) . marshalEncoding (net, ctx)) u.xpub+        ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoUnspent" $ \o -> do+      txid <- o .: "tx_hash_big_endian"+      index <- o .: "tx_output_n"+      script <- maybe mzero return . decodeHex =<< o .: "script"+      value <- o .: "value"+      confirmations <- o .: "confirmations"+      txidx <- o .: "tx_index"+      xpub <- mapM (unmarshalValue (net, ctx)) =<< o .:? "xpub"+      return BinfoUnspent {..}++newtype BinfoUnspents = BinfoUnspents [BinfoUnspent]+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoUnspents where+  marshalValue (net, ctx) (BinfoUnspents us) =+    A.object+      [ "notice" .= T.empty,+        "unspent_outputs" .= map (marshalValue (net, ctx)) us+      ]++  marshalEncoding (net, ctx) (BinfoUnspents us) =+    A.pairs $+      mconcat+        [ "notice" `A.pair` A.text T.empty,+          "unspent_outputs" `A.pair` A.list (marshalEncoding (net, ctx)) us+        ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoUnspents" $ \o -> do+      us <- mapM (unmarshalValue (net, ctx)) =<< o .: "unspent_outputs"+      return (BinfoUnspents us)++toBinfoBlock :: BlockData -> [BinfoTx] -> [BlockHash] -> BinfoBlock+toBinfoBlock b transactions next_blocks =+  BinfoBlock+    { hash = headerHash b.header,+      version = b.header.version,+      prev = b.header.prev,+      merkle = b.header.merkle,+      timestamp = b.header.timestamp,+      bits = b.header.bits,+      next = next_blocks,+      fee = b.fee,+      nonce = b.header.nonce,+      ntx = fromIntegral (length transactions),+      size = b.size,+      index = b.height,+      main = b.main,+      height = b.height,+      weight = b.weight,+      txs = transactions+    }++data BinfoBlock = BinfoBlock+  { hash :: !BlockHash,+    version :: !Word32,+    prev :: !BlockHash,+    merkle :: !Hash256,+    timestamp :: !Word32,+    bits :: !Word32,+    next :: ![BlockHash],+    fee :: !Word64,+    nonce :: !Word32,+    ntx :: !Word32,+    size :: !Word32,+    index :: !Word32,+    main :: !Bool,+    height :: !Word32,+    weight :: !Word32,+    txs :: ![BinfoTx]+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoBlock where+  marshalValue (net, ctx) b =+    A.object+      [ "hash" .= b.hash,+        "ver" .= b.version,+        "prev_block" .= b.prev,+        "mrkl_root" .= TxHash b.merkle,+        "time" .= b.timestamp,+        "bits" .= b.bits,+        "next_block" .= b.next,+        "fee" .= b.fee,+        "nonce" .= b.nonce,+        "n_tx" .= b.ntx,+        "size" .= b.size,+        "block_index" .= b.index,+        "main_chain" .= b.main,+        "height" .= b.height,+        "weight" .= b.weight,+        "tx" .= map (marshalValue (net, ctx)) b.txs+      ]++  marshalEncoding (net, ctx) b =+    A.pairs $+      mconcat+        [ "hash" `A.pair` toEncoding b.hash,+          "ver" `A.pair` A.word32 b.version,+          "prev_block" `A.pair` toEncoding b.prev,+          "mrkl_root" `A.pair` toEncoding (TxHash b.merkle),+          "time" `A.pair` A.word32 b.timestamp,+          "bits" `A.pair` A.word32 b.bits,+          "next_block" `A.pair` toEncoding b.next,+          "fee" `A.pair` A.word64 b.fee,+          "nonce" `A.pair` A.word32 b.nonce,+          "n_tx" `A.pair` A.word32 b.ntx,+          "size" `A.pair` A.word32 b.size,+          "block_index" `A.pair` A.word32 b.index,+          "main_chain" `A.pair` A.bool b.main,+          "height" `A.pair` A.word32 b.height,+          "weight" `A.pair` A.word32 b.weight,+          "tx" `A.pair` A.list (marshalEncoding (net, ctx)) b.txs+        ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoBlock" $ \o -> do+      hash <- o .: "hash"+      version <- o .: "ver"+      prev <- o .: "prev_block"+      merkle <- (\(TxHash h) -> h) <$> o .: "mrkl_root"+      timestamp <- o .: "time"+      bits <- o .: "bits"+      next <- o .: "next_block"+      fee <- o .: "fee"+      nonce <- o .: "nonce"+      ntx <- o .: "n_tx"+      size <- o .: "size"+      index <- o .: "block_index"+      main <- o .: "main_chain"+      height <- o .: "height"+      weight <- o .: "weight"+      txs <- o .: "tx" >>= mapM (unmarshalValue (net, ctx))+      return BinfoBlock {..}++instance MarshalJSON (Network, Ctx) [BinfoBlock] where+  marshalValue (net, ctx) blocks =+    A.object ["blocks" .= map (marshalValue (net, ctx)) blocks]++  marshalEncoding (net, ctx) blocks =+    A.pairs $ "blocks" `A.pair` A.list (marshalEncoding (net, ctx)) blocks++  unmarshalValue (net, ctx) =+    A.withObject "blocks" $ \o ->+      mapM (unmarshalValue (net, ctx)) =<< o .: "blocks"++data BinfoTx = BinfoTx+  { txid :: !TxHash,+    version :: !Word32,+    inputCount :: !Word32,+    outputCount :: !Word32,+    size :: !Word32,+    weight :: !Word32,+    fee :: !Word64,+    relayed :: !ByteString,+    locktime :: !Word32,+    index :: !BinfoTxId,+    doubleSpend :: !Bool,+    rbf :: !Bool,+    balance :: !(Maybe (Int64, Int64)),+    timestamp :: !Word64,+    blockIndex :: !(Maybe Word32),+    blockHeight :: !(Maybe Word32),+    inputs :: ![BinfoTxInput],+    outputs :: ![BinfoTxOutput]+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoTx where+  marshalValue (net, ctx) t =+    A.object $+      [ "hash" .= t.txid,+        "ver" .= t.version,+        "vin_sz" .= t.inputCount,+        "vout_sz" .= t.outputCount,+        "size" .= t.size,+        "weight" .= t.weight,+        "fee" .= t.fee,+        "relayed_by" .= T.decodeUtf8 t.relayed,+        "lock_time" .= t.locktime,+        "tx_index" .= t.index,+        "double_spend" .= t.doubleSpend,+        "time" .= t.timestamp,+        "block_index" .= t.blockIndex,+        "block_height" .= t.blockHeight,+        "inputs" .= map (marshalValue (net, ctx)) t.inputs,+        "out" .= map (marshalValue (net, ctx)) t.outputs+      ]+        ++ bal+        ++ rbf+    where+      bal =+        case t.balance of+          Nothing -> []+          Just (r, b) -> ["result" .= r, "balance" .= b]+      rbf = ["rbf" .= True | t.rbf]++  marshalEncoding (net, ctx) t =+    A.pairs $+      mconcat+        [ "hash" `A.pair` toEncoding t.txid,+          "ver" `A.pair` A.word32 t.version,+          "vin_sz" `A.pair` A.word32 t.inputCount,+          "vout_sz" `A.pair` A.word32 t.outputCount,+          "size" `A.pair` A.word32 t.size,+          "weight" `A.pair` A.word32 t.weight,+          "fee" `A.pair` A.word64 t.fee,+          "relayed_by" `A.pair` A.text (T.decodeUtf8 t.relayed),+          "lock_time" `A.pair` A.word32 t.locktime,+          "tx_index" `A.pair` toEncoding t.index,+          "double_spend" `A.pair` A.bool t.doubleSpend,+          "time" `A.pair` A.word64 t.timestamp,+          "block_index" `A.pair` toEncoding t.blockIndex,+          "block_height" `A.pair` maybe A.null_ A.word32 t.blockHeight,+          "inputs" `A.pair` A.list (marshalEncoding (net, ctx)) t.inputs,+          "out" `A.pair` A.list (marshalEncoding (net, ctx)) t.outputs,+          bal,+          rbf+        ]+    where+      bal =+        case t.balance of+          Nothing -> mempty+          Just (r, b) ->+            "result" `A.pair` A.int64 r <> "balance" `A.pair` A.int64 b+      rbf = if t.rbf then "rbf" .= True else mempty++  unmarshalValue (net, ctx) = A.withObject "BinfoTx" $ \o -> do+    txid <- o .: "hash"+    version <- o .: "ver"+    inputCount <- o .: "vin_sz"+    outputCount <- o .: "vout_sz"+    size <- o .: "size"+    weight <- o .: "weight"+    fee <- o .: "fee"+    relayed <- T.encodeUtf8 <$> o .: "relayed_by"+    locktime <- o .: "lock_time"+    index <- o .: "tx_index"+    doubleSpend <- o .: "double_spend"+    timestamp <- o .: "time"+    blockIndex <- o .: "block_index"+    blockHeight <- o .: "block_height"+    inputs <- o .: "inputs" >>= mapM (unmarshalValue (net, ctx))+    outputs <- o .: "out" >>= mapM (unmarshalValue (net, ctx))+    rbf <- o .:? "rbf" .!= False+    res <- o .:? "result"+    bal <- o .:? "balance"+    let balance = (,) <$> res <*> bal+    return BinfoTx {..}++instance MarshalJSON (Network, Ctx) [BinfoTx] where+  marshalValue (net, ctx) txs =+    toJSON $ map (marshalValue (net, ctx)) txs+  marshalEncoding (net, ctx) =+    A.list (marshalEncoding (net, ctx))+  unmarshalValue (net, ctx) =+    A.withArray "[BinfoTx]" $+      fmap V.toList . mapM (unmarshalValue (net, ctx))++data BinfoTxInput = BinfoTxInput+  { sequence :: !Word32,+    witness :: !ByteString,+    script :: !ByteString,+    index :: !Word32,+    output :: !BinfoTxOutput+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoTxInput where+  marshalValue (net, ctx) i =+    A.object+      [ "sequence" .= i.sequence,+        "witness" .= encodeHex i.witness,+        "script" .= encodeHex i.script,+        "index" .= i.index,+        "prev_out" .= marshalValue (net, ctx) i.output+      ]++  marshalEncoding (net, ctx) i =+    A.pairs $+      mconcat+        [ "sequence" `A.pair` A.word32 i.sequence,+          "witness" `A.pair` hexEncoding (B.fromStrict i.witness),+          "script" `A.pair` hexEncoding (B.fromStrict i.script),+          "index" `A.pair` A.word32 i.index,+          "prev_out" `A.pair` marshalEncoding (net, ctx) i.output+        ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoTxInput" $ \o -> do+      sequence <- o .: "sequence"+      witness <-+        maybe mzero return . decodeHex+          =<< o .: "witness"+      script <-+        maybe mzero return . decodeHex+          =<< o .: "script"+      index <- o .: "index"+      output <-+        o .: "prev_out"+          >>= unmarshalValue (net, ctx)+      return BinfoTxInput {..}++data BinfoTxOutput = BinfoTxOutput+  { typ :: !Int,+    spent :: !Bool,+    value :: !Word64,+    index :: !Word32,+    txidx :: !BinfoTxId,+    script :: !ByteString,+    spenders :: ![BinfoSpender],+    address :: !(Maybe Address),+    xpub :: !(Maybe BinfoXPubPath)+  }+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoTxOutput where+  marshalValue (net, ctx) o =+    A.object $+      [ "type" .= o.typ,+        "spent" .= o.spent,+        "value" .= o.value,+        "spending_outpoints" .= o.spenders,+        "n" .= o.index,+        "tx_index" .= o.txidx,+        "script" .= encodeHex o.script+      ]+        <> [ "addr" .= marshalValue net a+             | a <- maybeToList o.address+           ]+        <> [ "xpub" .= marshalValue (net, ctx) x+             | x <- maybeToList o.xpub+           ]++  marshalEncoding (net, ctx) o =+    A.pairs $+      mconcat $+        [ "type" `A.pair` A.int o.typ,+          "spent" `A.pair` A.bool o.spent,+          "value" `A.pair` A.word64 o.value,+          "spending_outpoints" `A.pair` A.list toEncoding o.spenders,+          "n" `A.pair` A.word32 o.index,+          "tx_index" `A.pair` toEncoding o.txidx,+          "script" `A.pair` hexEncoding (B.fromStrict o.script)+        ]+          <> [ "addr" `A.pair` marshalEncoding net a+               | a <- maybeToList o.address+             ]+          <> [ "xpub" `A.pair` marshalEncoding (net, ctx) x+               | x <- maybeToList o.xpub+             ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoTxOutput" $ \o -> do+      typ <- o .: "type"+      spent <- o .: "spent"+      value <- o .: "value"+      spenders <- o .: "spending_outpoints"+      index <- o .: "n"+      txidx <- o .: "tx_index"+      script <- maybe mzero return . decodeHex =<< o .: "script"+      address <- o .:? "addr" >>= mapM (unmarshalValue net)+      xpub <- o .:? "xpub" >>= mapM (unmarshalValue (net, ctx))+      return BinfoTxOutput {..}++data BinfoSpender = BinfoSpender+  { txidx :: !BinfoTxId,+    input :: !Word32+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoSpender where+  toJSON s =+    A.object+      [ "tx_index" .= s.txidx,+        "n" .= s.input+      ]+  toEncoding s =+    A.pairs $+      mconcat+        [ "tx_index" `A.pair` toEncoding s.txidx,+          "n" `A.pair` A.word32 s.input+        ]++instance FromJSON BinfoSpender where+  parseJSON =+    A.withObject "BinfoSpender" $ \o -> do+      txidx <- o .: "tx_index"+      input <- o .: "n"+      return BinfoSpender {..}++data BinfoXPubPath = BinfoXPubPath+  { key :: !XPubKey,+    deriv :: !SoftPath+  }+  deriving (Eq, Show, Generic, NFData)++instance Ord BinfoXPubPath where+  compare = compare `on` f+    where+      f b =+        ( b.key.parent,+          b.deriv+        )++instance MarshalJSON (Network, Ctx) BinfoXPubPath where+  marshalValue (net, ctx) p =+    A.object+      [ "m" .= marshalValue (net, ctx) p.key,+        "path" .= ("M" ++ pathToStr p.deriv)+      ]++  marshalEncoding (net, ctx) p =+    A.pairs $+      mconcat+        [ "m" `A.pair` marshalEncoding (net, ctx) p.key,+          "path" `A.pair` A.string ("M" ++ pathToStr p.deriv)+        ]++  unmarshalValue (net, ctx) =+    A.withObject "BinfoXPubPath" $ \o -> do+      key <- o .: "m" >>= unmarshalValue (net, ctx)+      deriv <- fromMaybe "bad xpub path" . parseSoft <$> o .: "path"+      return BinfoXPubPath {..}++data BinfoInfo = BinfoInfo+  { connected :: !Word32,+    conversion :: !Double,+    fiat :: !BinfoSymbol,+    crypto :: !BinfoSymbol,+    head :: !BinfoBlockInfo+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoInfo where+  toJSON i =+    A.object+      [ "nconnected" .= i.connected,+        "conversion" .= i.conversion,+        "symbol_local" .= i.fiat,+        "symbol_btc" .= i.crypto,+        "latest_block" .= i.head+      ]+  toEncoding i =+    A.pairs $+      mconcat+        [ "nconnected" `A.pair` A.word32 i.connected,+          "conversion" `A.pair` A.double i.conversion,+          "symbol_local" `A.pair` toEncoding i.fiat,+          "symbol_btc" `A.pair` toEncoding i.crypto,+          "latest_block" `A.pair` toEncoding i.head+        ]++instance FromJSON BinfoInfo where+  parseJSON =+    A.withObject "BinfoInfo" $ \o -> do+      connected <- o .: "nconnected"+      conversion <- o .: "conversion"+      fiat <- o .: "symbol_local"+      crypto <- o .: "symbol_btc"+      head <- o .: "latest_block"+      return BinfoInfo {..}++data BinfoBlockInfo = BinfoBlockInfo+  { hash :: !BlockHash,+    height :: !BlockHeight,+    timestamp :: !Word32,+    index :: !BlockHeight+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoBlockInfo where+  toJSON i =+    A.object+      [ "hash" .= i.hash,+        "height" .= i.height,+        "time" .= i.timestamp,+        "block_index" .= i.index+      ]+  toEncoding i =+    A.pairs $+      mconcat+        [ "hash" `A.pair` toEncoding i.hash,+          "height" `A.pair` A.word32 i.height,+          "time" `A.pair` A.word32 i.timestamp,+          "block_index" `A.pair` A.word32 i.index+        ]++instance FromJSON BinfoBlockInfo where+  parseJSON =+    A.withObject "BinfoBlockInfo" $ \o -> do+      hash <- o .: "hash"+      height <- o .: "height"+      timestamp <- o .: "time"+      index <- o .: "block_index"+      return BinfoBlockInfo {..}++toBinfoBlockInfo :: BlockData -> BinfoBlockInfo+toBinfoBlockInfo d =+  BinfoBlockInfo+    { hash = headerHash d.header,+      height = d.height,+      timestamp = d.header.timestamp,+      index = d.height+    }++data BinfoRate = BinfoRate+  { timestamp :: !Word64,+    price :: !Double,+    vol24 :: !Double+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoRate where+  toJSON r =+    A.object+      [ "timestamp" .= r.timestamp,+        "price" .= r.price,+        "volume24h" .= r.vol24+      ]+  toEncoding r =+    A.pairs $+      mconcat+        [ "timestamp" `A.pair` A.word64 r.timestamp,+          "price" `A.pair` A.double r.price,+          "volume24h" `A.pair` A.double r.vol24+        ]++instance FromJSON BinfoRate where+  parseJSON =+    A.withObject "BinfoRate" $ \o -> do+      timestamp <- o .: "timestamp"+      price <- o .: "price"+      vol24 <- o .: "volume24h"+      return BinfoRate {..}++data BinfoHistory = BinfoHistory+  { date :: !Text,+    time :: !Text,+    typ :: !Text,+    amount :: !Double,+    valueThen :: !Double,+    valueNow :: !Double,+    rateThen :: !Double,+    txid :: !TxHash,+    fee :: !Double+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoHistory where+  toJSON h =+    A.object+      [ "date" .= h.date,+        "time" .= h.time,+        "type" .= h.typ,+        "amount" .= h.amount,+        "value_then" .= h.valueThen,+        "value_now" .= h.valueNow,+        "exchange_rate_then" .= h.rateThen,+        "tx" .= h.txid,+        "fee" .= h.fee+      ]+  toEncoding h =+    A.pairs $+      mconcat+        [ "date" `A.pair` A.text h.date,+          "time" `A.pair` A.text h.time,+          "type" `A.pair` A.text h.typ,+          "amount" `A.pair` A.double h.amount,+          "value_then" `A.pair` A.double h.valueThen,+          "value_now" `A.pair` A.double h.valueNow,+          "exchange_rate_then" `A.pair` A.double h.rateThen,+          "tx" `A.pair` toEncoding h.txid,+          "fee" `A.pair` A.double h.fee+        ]++instance FromJSON BinfoHistory where+  parseJSON =+    A.withObject "BinfoHistory" $ \o -> do+      date <- o .: "date"+      time <- o .: "time"+      typ <- o .: "type"+      amount <- o .: "amount"+      valueThen <- o .: "value_then"+      valueNow <- o .: "value_now"+      rateThen <- o .: "exchange_rate_then"+      txid <- o .: "tx"+      fee <- o .: "fee"+      return BinfoHistory {..}++toBinfoHistory ::+  Int64 ->+  Word64 ->+  Double ->+  Double ->+  Word64 ->+  TxHash ->+  BinfoHistory+toBinfoHistory satoshi timestamp rateThen rateNow fee txid =+  BinfoHistory+    { date = T.pack $ formatTime defaultTimeLocale "%Y-%m-%d" t,+      time = T.pack $ formatTime defaultTimeLocale "%H:%M:%S GMT %Ez" t,+      typ = if satoshi <= 0 then "sent" else "received",+      amount = fromRational v,+      fee = fromRational f,+      valueThen = fromRational v1,+      valueNow = fromRational v2,+      rateThen,+      txid+    }+  where+    t = posixSecondsToUTCTime (realToFrac timestamp)+    v = toRational satoshi / (100 * 1000 * 1000)+    r1 = toRational rateThen+    r2 = toRational rateNow+    f = toRational fee / (100 * 1000 * 1000)+    v1 = v * r1+    v2 = v * r2++newtype BinfoDate = BinfoDate Word64+  deriving (Eq, Show, Read, Generic, NFData)++instance Parsable BinfoDate where+  parseParam t =+    maybeToEither "Cannot parse date"+      . fmap (BinfoDate . round . utcTimeToPOSIXSeconds)+      $ p "%d-%m-%Y" <|> p "%d/%m/%Y"+    where+      s = LazyText.unpack t+      p fmt = parseTimeM False defaultTimeLocale fmt s++data BinfoTicker = BinfoTicker+  { fifteen :: !Double,+    last :: !Double,+    buy :: !Double,+    sell :: !Double,+    symbol :: !Text+  }+  deriving (Eq, Show, Generic, NFData)++instance Default BinfoTicker where+  def =+    BinfoTicker+      { symbol = "XXX",+        fifteen = 0.0,+        last = 0.0,+        buy = 0.0,+        sell = 0.0+      }++instance ToJSON BinfoTicker where+  toJSON t =+    A.object+      [ "symbol" .= t.symbol,+        "sell" .= t.sell,+        "buy" .= t.buy,+        "last" .= t.last,+        "15m" .= t.fifteen+      ]+  toEncoding t =+    A.pairs $+      mconcat+        [ "symbol" `A.pair` A.text t.symbol,+          "sell" `A.pair` A.double t.sell,+          "buy" `A.pair` A.double t.buy,+          "last" `A.pair` A.double t.last,+          "15m" `A.pair` A.double t.fifteen+        ]++instance FromJSON BinfoTicker where+  parseJSON =+    A.withObject "BinfoTicker" $ \o -> do+      symbol <- o .: "symbol"+      fifteen <- o .: "15m"+      sell <- o .: "sell"+      buy <- o .: "buy"+      last <- o .: "last"+      return BinfoTicker {..}++data BinfoSymbol = BinfoSymbol+  { code :: !Text,+    symbol :: !Text,+    name :: !Text,+    conversion :: !Double,+    after :: !Bool,+    local :: !Bool+  }+  deriving (Eq, Show, Generic, NFData)++instance Default BinfoSymbol where+  def =+    BinfoSymbol+      { code = "XXX",+        symbol = "¤",+        name = "No currency",+        conversion = 0.0,+        after = False,+        local = True+      }++instance ToJSON BinfoSymbol where+  toJSON s =+    A.object+      [ "code" .= s.code,+        "symbol" .= s.symbol,+        "name" .= s.name,+        "conversion" .= s.conversion,+        "symbolAppearsAfter" .= s.after,+        "local" .= s.local+      ]+  toEncoding s =+    A.pairs $+      mconcat+        [ "code" `A.pair` A.text s.code,+          "symbol" `A.pair` A.text s.symbol,+          "name" `A.pair` A.text s.name,+          "conversion" `A.pair` A.double s.conversion,+          "symbolAppearsAfter" `A.pair` A.bool s.after,+          "local" `A.pair` A.bool s.local+        ]++instance FromJSON BinfoSymbol where+  parseJSON =+    A.withObject "BinfoSymbol" $ \o -> do+      code <- o .: "code"+      symbol <- o .: "symbol"+      name <- o .: "name"+      conversion <- o .: "conversion"+      after <- o .: "symbolAppearsAfter"+      local <- o .: "local"+      return BinfoSymbol {..}++relevantTxs ::+  HashSet Address ->+  Bool ->+  Transaction ->+  HashSet TxHash+relevantTxs addrs prune t =+  HashSet.fromList $ ins <> outs+  where+    p a =+      prune+        && getTxResult addrs t > 0+        && not (HashSet.member a addrs)+    f o = do+      Spender {..} <- o.spender+      a <- o.address+      guard $ p a+      return txid+    outs = mapMaybe f t.outputs+    g StoreCoinbase {} = Nothing+    g StoreInput {outpoint = OutPoint h i} = Just h+    ins = mapMaybe g t.inputs++toBinfoAddrs ::+  HashMap Address Balance ->+  HashMap XPubSpec [XPubBal] ->+  HashMap XPubSpec Word64 ->+  [BinfoBalance]+toBinfoAddrs onlyAddrs onlyXpubs xpubTxs =+  xpubBals <> addrBals+  where+    xpubBal k xs =+      let f x =+            case x.path of+              [0, _] -> x.balance.received+              _ -> 0+          g x = x.balance.confirmed + x.balance.unconfirmed+          i m x =+            case x.path 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 = HashMap.lookupDefault 0 k xpubTxs+          ax = foldl max 0 $ map (i 0) xs+          cx = foldl max 0 $ map (i 1) xs+       in BinfoXPubBalance+            { xpub = k.key,+              txs = count,+              received = received,+              sent = sent,+              balance = bal,+              external = ax,+              change = cx+            }+    xpubBals = map (uncurry xpubBal) $ HashMap.toList onlyXpubs+    addrBals =+      let f Balance {..} =+            let sent = received - balance+                balance = confirmed + unconfirmed+             in BinfoAddrBalance {..}+       in map f $ HashMap.elems onlyAddrs++toBinfoTxSimple ::+  Bool ->+  Transaction ->+  BinfoTx+toBinfoTxSimple numtxid =+  toBinfoTx numtxid HashMap.empty False 0++toBinfoTxInputs ::+  Bool ->+  HashMap Address (Maybe BinfoXPubPath) ->+  Transaction ->+  [BinfoTxInput]+toBinfoTxInputs numtxid abook t =+  zipWith f [0 ..] t.inputs+  where+    f n i =+      BinfoTxInput+        { index = n,+          sequence = i.sequence,+          script = i.script,+          witness = wit i,+          output = prev n i+        }+    wit i =+      case i.witness of+        [] -> B.empty+        ws -> runPutS (put_witness ws)+    prev = inputToBinfoTxOutput numtxid abook t+    put_witness ws = do+      putVarInt (length ws)+      mapM_ put_item ws+    put_item bs = do+      putVarInt (B.length bs)+      putByteString bs++transactionHeight :: Transaction -> Maybe BlockHeight+transactionHeight Transaction {deleted = True} = Nothing+transactionHeight Transaction {block = MemRef _} = Nothing+transactionHeight Transaction {block = BlockRef h _} = Just h++toBinfoTx ::+  Bool ->+  HashMap Address (Maybe BinfoXPubPath) ->+  Bool ->+  Int64 ->+  Transaction ->+  BinfoTx+toBinfoTx numtxid abook prune bal t =+  BinfoTx+    { version = t.version,+      weight = t.weight,+      relayed = "0.0.0.0",+      txid = txHash tx,+      index = encodeBinfoTxId numtxid (txHash tx),+      inputCount = fromIntegral $ length t.inputs,+      outputCount = fromIntegral $ length t.outputs,+      blockIndex = transactionHeight t,+      blockHeight = transactionHeight t,+      doubleSpend = t.deleted,+      balance =+        if simple+          then Nothing+          else Just (getTxResult aset t, bal),+      outputs =+        let p = prune && getTxResult aset t > 0+            f = toBinfoTxOutput numtxid abook p t+         in catMaybes $ zipWith f [0 ..] t.outputs,+      inputs = toBinfoTxInputs numtxid abook t,+      size = t.size,+      rbf = t.rbf,+      fee = t.fee,+      locktime = t.locktime,+      timestamp = t.timestamp+    }+  where+    tx = transactionData t+    aset = HashMap.keysSet abook+    simple = HashMap.null abook && bal == 0++getTxResult :: HashSet Address -> Transaction -> Int64+getTxResult aset t =+  inputSum + outputSum+  where+    inputSum = sum $ map inputValue t.inputs+    inputValue StoreCoinbase {} = 0+    inputValue StoreInput {..} =+      case address of+        Nothing -> 0+        Just a ->+          if testAddr a+            then negate $ fromIntegral value+            else 0+    testAddr a = HashSet.member a aset+    outputSum = sum $ map outValue t.outputs+    outValue StoreOutput {..} =+      case address of+        Nothing -> 0+        Just a ->+          if testAddr a+            then fromIntegral value+            else 0++toBinfoTxOutput ::+  Bool ->+  HashMap Address (Maybe BinfoXPubPath) ->+  Bool ->+  Transaction ->+  Word32 ->+  StoreOutput ->+  Maybe BinfoTxOutput+toBinfoTxOutput numtxid abook prune t index o =+  if prune && notInBook+    then Nothing+    else+      Just+        BinfoTxOutput+          { typ = 0,+            spent = isJust o.spender,+            value = o.value,+            index = index,+            txidx = encodeBinfoTxId numtxid $ txHash $ transactionData t,+            script = o.script,+            spenders = maybeToList $ toBinfoSpender numtxid <$> o.spender,+            address = o.address,+            xpub = o.address >>= join . flip HashMap.lookup abook+          }+  where+    notInBook = isNothing $ o.address >>= flip HashMap.lookup abook++toBinfoSpender :: Bool -> Spender -> BinfoSpender+toBinfoSpender numtxid s =+  BinfoSpender+    { txidx = encodeBinfoTxId numtxid s.txid,+      input = s.index+    }++inputToBinfoTxOutput ::+  Bool ->+  HashMap Address (Maybe BinfoXPubPath) ->+  Transaction ->+  Word32 ->+  StoreInput ->+  BinfoTxOutput+inputToBinfoTxOutput numtxid abook t n i =+  BinfoTxOutput+    { typ = 0,+      spent = True,+      txidx = encodeBinfoTxId numtxid i.outpoint.hash,+      value =+        case i of+          StoreCoinbase {} -> 0+          StoreInput {value} -> value,+      script =+        case i of+          StoreCoinbase {} -> B.empty+          StoreInput {pkscript} -> pkscript,+      address =+        case i of+          StoreCoinbase {} -> Nothing+          StoreInput {address} -> address,+      index = i.outpoint.index,+      spenders =+        [ BinfoSpender+            (encodeBinfoTxId numtxid (txHash (transactionData t)))+            n+        ],+      xpub = i.address >>= join . flip HashMap.lookup abook+    }++data BinfoAddr+  = BinfoAddr !Address+  | BinfoXpub !XPubKey+  deriving (Eq, Show, Generic, Hashable, NFData)++parseBinfoAddr :: Network -> Ctx -> Text -> Maybe [BinfoAddr]+parseBinfoAddr net ctx "" = Just []+parseBinfoAddr net ctx s =+  mapM f $+    filter (not . T.null) $+      concatMap (T.splitOn ",") (T.splitOn "|" s)+  where+    f x =+      BinfoAddr <$> textToAddr net x+        <|> BinfoXpub <$> xPubImport net ctx x++data BinfoHeader = BinfoHeader+  { hash :: !BlockHash,+    timestamp :: !Timestamp,+    index :: !Word32,+    height :: !BlockHeight,+    txids :: ![BinfoTxId]+  }+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoHeader where+  toJSON h =+    A.object+      [ "hash" .= h.hash,+        "time" .= h.timestamp,+        "block_index" .= h.index,+        "height" .= h.height,+        "txIndexes" .= h.txids+      ]+  toEncoding h =+    A.pairs $+      mconcat+        [ "hash" `A.pair` toEncoding h.hash,+          "time" `A.pair` A.word32 h.timestamp,+          "block_index" `A.pair` A.word32 h.index,+          "height" `A.pair` A.word32 h.height,+          "txIndexes" `A.pair` A.list toEncoding h.txids+        ]++instance FromJSON BinfoHeader where+  parseJSON =+    A.withObject "BinfoHeader" $ \o -> do+      hash <- o .: "hash"+      timestamp <- o .: "time"+      index <- o .: "block_index"+      height <- o .: "height"+      txids <- o .: "txIndexes"+      return BinfoHeader {..}++newtype BinfoMempool = BinfoMempool {get :: [BinfoTx]}+  deriving (Eq, Show, Generic, NFData)++instance MarshalJSON (Network, Ctx) BinfoMempool where+  marshalValue (net, ctx) (BinfoMempool txs) =+    A.object ["txs" .= map (marshalValue (net, ctx)) txs]++  marshalEncoding (net, ctx) (BinfoMempool txs) =+    A.pairs $ A.pair "txs" $ A.list (marshalEncoding (net, ctx)) txs++  unmarshalValue (net, ctx) =+    A.withObject "BinfoMempool" $ \o ->+      BinfoMempool <$> (mapM (unmarshalValue (net, ctx)) =<< o .: "txs")++newtype BinfoBlockInfos = BinfoBlockInfos {get :: [BinfoBlockInfo]}+  deriving (Eq, Show, Generic, NFData)++instance ToJSON BinfoBlockInfos where+  toJSON b = A.object ["blocks" .= b.get]+  toEncoding b =+    A.pairs $ A.pair "blocks" $ A.list toEncoding b.get++instance FromJSON BinfoBlockInfos where+  parseJSON =+    A.withObject "BinfoBlockInfos" $ \o ->+      BinfoBlockInfos <$> o .: "blocks"
src/Haskoin/Store/WebClient.hs view
@@ -1,8 +1,12 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}  module Haskoin.Store.WebClient   ( ApiConfig (..),@@ -67,8 +71,8 @@ import Control.Exception import Control.Lens ((.~), (?~), (^.)) import Control.Monad.Except-import qualified Data.Aeson as A-import qualified Data.ByteString.Lazy as BL+import Data.Aeson qualified as A+import Data.ByteString.Lazy qualified as BL import Data.Bytes.Get import Data.Bytes.Put import Data.Bytes.Serial@@ -76,17 +80,17 @@ import Data.Monoid (Endo (..), appEndo) import Data.String.Conversions (cs) import Data.Text (Text)-import qualified Data.Text as Text-import Haskoin.Constants-import Haskoin.Data-import qualified Haskoin.Store.Data as Store+import Data.Text qualified as Text+import Haskoin.Crypto (Ctx)+import Haskoin.Network+import Haskoin.Store.Data qualified as Store import Haskoin.Store.WebCommon import Haskoin.Transaction import Haskoin.Util import Network.HTTP.Client (Request (..)) import Network.HTTP.Types (StdMethod (..)) import Network.HTTP.Types.Status-import qualified Network.Wreq as HTTP+import Network.Wreq qualified as HTTP import Network.Wreq.Types (ResponseChecker) import Numeric.Natural (Natural) @@ -95,40 +99,41 @@ -- -- @ -- ApiConfig--- { configNetwork = bch--- , configHost = "https://api.haskoin.com/"+-- { net = bch+-- , host = "https://api.haskoin.com/" -- } -- @ data ApiConfig = ApiConfig-  { configNetwork :: !Network,-    configHost :: !String+  { net :: !Network,+    host :: !String   }   deriving (Eq, Show)  instance Default ApiConfig where   def =     ApiConfig-      { configNetwork = bch,-        configHost = "https://api.haskoin.com/"+      { net = bch,+        host = "https://api.haskoin.com/"       }  -- | Make a call to the haskoin-store API. -- -- Usage (default options): ----- > apiCall def $ GetAddrsTxs addrs def+-- > apiCall ctx def $ GetAddrsTxs addrs def -- -- With options: ----- > apiCall def $ GetAddrsUnspent addrs def{ paramLimit = Just 10 }+-- > apiCall ctx def $ GetAddrsUnspent addrs def{ paramLimit = Just 10 } apiCall ::   (ApiResource a b, MonadIO m, MonadError Store.Except m) =>+  Ctx ->   ApiConfig ->   a ->   m b-apiCall (ApiConfig net apiHost) res = do-  args <- liftEither $ toOptions net res-  let url = apiHost <> getNetworkName net <> cs (queryPath net res)+apiCall ctx (ApiConfig net apiHost) res = do+  args <- liftEither $ toOptions net ctx res+  let url = apiHost <> net.name <> cs (queryPath net ctx res)   case resourceMethod $ asProxy res of     GET -> liftEither =<< liftIO (getBinary args url)     POST ->@@ -143,11 +148,13 @@ -- > apiBatch 20 def (GetAddrsTxs addrs def) apiBatch ::   (Batchable a b, MonadIO m, MonadError Store.Except m) =>+  Ctx ->   Natural ->   ApiConfig ->   a ->   m b-apiBatch i conf res = mconcat <$> mapM (apiCall conf) (resourceBatch i res)+apiBatch ctx i conf res =+  mconcat <$> mapM (apiCall ctx conf) (resourceBatch i res)  class (ApiResource a b, Monoid b) => Batchable a b where   resourceBatch :: Natural -> a -> [a]@@ -190,22 +197,33 @@ ------------------  toOptions ::-  ApiResource a b => Network -> a -> Either Store.Except (Endo HTTP.Options)-toOptions net res =+  (ApiResource a b) =>+  Network ->+  Ctx ->+  a ->+  Either Store.Except (Endo HTTP.Options)+toOptions net ctx res =   mconcat <$> mapM f (snd $ queryParams res)   where-    f (ParamBox p) = toOption net p+    f (ParamBox p) = toOption net ctx p -toOption :: Param a => Network -> a -> Either Store.Except (Endo HTTP.Options)-toOption net a = do-  res <- maybeToEither (Store.UserError "Invalid Param") $ encodeParam net a+toOption ::+  (Param a) =>+  Network ->+  Ctx ->+  a ->+  Either Store.Except (Endo HTTP.Options)+toOption net ctx a = do+  res <-+    maybeToEither (Store.UserError "Invalid Param") $+      encodeParam net ctx a   return $ applyOpt (paramLabel a) res  applyOpt :: Text -> [Text] -> Endo HTTP.Options applyOpt p t = Endo $ HTTP.param p .~ [Text.intercalate "," t]  getBinary ::-  Serial a =>+  (Serial a) =>   Endo HTTP.Options ->   String ->   IO (Either Store.Except a)@@ -239,11 +257,11 @@   | statusIsSuccessful status = return ()   | isHealthPath && code == 503 = return () -- Ignore health checks   | otherwise = do-    e <- A.decodeStrict <$> res ^. HTTP.responseBody-    throwIO $-      case e of-        Just except -> except :: Store.Except-        Nothing -> Store.StringError "could not decode error"+      e <- A.decodeStrict <$> res ^. HTTP.responseBody+      throwIO $+        case e of+          Just except -> except :: Store.Except+          Nothing -> Store.StringError "could not decode error"   where     code = res ^. HTTP.responseStatus . HTTP.statusCode     message = res ^. HTTP.responseStatus . HTTP.statusMessage@@ -258,4 +276,4 @@ chunksOf n xs   | null xs = []   | otherwise =-    uncurry (:) $ second (chunksOf n) $ splitAt (fromIntegral n) xs+      uncurry (:) $ second (chunksOf n) $ splitAt (fromIntegral n) xs
src/Haskoin/Store/WebCommon.hs view
@@ -1,10 +1,14 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}  module Haskoin.Store.WebCommon where @@ -16,7 +20,7 @@ import Data.String (IsString (..)) import Data.String.Conversions (cs) import Data.Text (Text)-import qualified Data.Text as T+import Data.Text qualified as T import Haskoin.Address import Haskoin.Block   ( Block,@@ -24,22 +28,21 @@     blockHashToHex,     hexToBlockHash,   )-import Haskoin.Constants-import Haskoin.Crypto (Hash256)-import Haskoin.Data-import Haskoin.Keys-import qualified Haskoin.Store.Data as Store+import Haskoin.Crypto (Ctx, Hash256)+import Haskoin.Crypto.Keys+import Haskoin.Network.Data+import Haskoin.Store.Data qualified 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 Web.Scotty.Trans qualified as Scotty  ------------------- -- API Resources -- ------------------- -class Serial b => ApiResource a b | a -> b where+class (Serial b) => ApiResource a b | a -> b where   resourceMethod :: Proxy a -> StdMethod   resourceMethod _ = GET   resourcePath :: Proxy a -> ([Text] -> Text)@@ -50,11 +53,11 @@   resourceBody :: a -> Maybe PostBox   resourceBody = const Nothing -data PostBox = forall s. Serial s => PostBox !s+data PostBox = forall s. (Serial s) => PostBox !s  data ParamBox = forall p. (Eq p, Param p) => ParamBox !p -data ProxyBox = forall p. Param p => ProxyBox !(Proxy p)+data ProxyBox = forall p. (Param p) => ProxyBox !(Proxy p)  -------------------- -- Resource Paths --@@ -352,7 +355,7 @@ -- Network -- ------------- -instance ApiResource GetPeers (Store.SerialList Store.PeerInformation) where+instance ApiResource GetPeers (Store.SerialList Store.PeerInfo) where   resourcePath _ _ = "/peers"  instance ApiResource GetHealth Store.HealthCheck where@@ -386,23 +389,23 @@ asProxy :: a -> Proxy a asProxy = const Proxy -queryPath :: ApiResource a b => Network -> a -> Text-queryPath net a = f $ encParam <$> fst (queryParams a)+queryPath :: (ApiResource a b) => Network -> Ctx -> a -> Text+queryPath net ctx a = f $ encParam <$> fst (queryParams a)   where     f = resourcePath $ asProxy a     encParam (ParamBox p) =-      case encodeParam net p of+      case encodeParam net ctx p of         Just [res] -> res         _ -> error "Invalid query param" -capturePath :: ApiResource a b => Proxy a -> Scotty.RoutePattern+capturePath :: (ApiResource a b) => Proxy a -> Scotty.RoutePattern capturePath proxy =   fromString $ cs $ f $ toLabel <$> captureParams proxy   where     f = resourcePath proxy     toLabel (ProxyBox p) = ":" <> proxyLabel p -paramLabel :: Param p => p -> Text+paramLabel :: (Param p) => p -> Text paramLabel = proxyLabel . asProxy  -------------@@ -411,35 +414,29 @@  class Param a where   proxyLabel :: Proxy a -> Text-  encodeParam :: Network -> a -> Maybe [Text]-  parseParam :: Network -> [Text] -> Maybe a+  encodeParam :: Network -> Ctx -> a -> Maybe [Text]+  parseParam :: Network -> Ctx -> [Text] -> Maybe a  instance Param Address where   proxyLabel = const "address"-  encodeParam net a = (: []) <$> addrToText net a-  parseParam net [a] = textToAddr net a-  parseParam _ _ = Nothing+  encodeParam net ctx a = (: []) <$> addrToText net a+  parseParam net ctx [a] = textToAddr net a+  parseParam net ctx _ = Nothing  instance Param [Address] where   proxyLabel = const "addresses"-  encodeParam = mapM . addrToText-  parseParam = mapM . textToAddr+  encodeParam net ctx = mapM (addrToText net)+  parseParam net ctx = mapM (textToAddr net)  data StartParam-  = StartParamHash-      { startParamHash :: Hash256-      }-  | StartParamHeight-      { startParamHeight :: Natural-      }-  | StartParamTime-      { startParamTime :: Store.UnixTime-      }+  = StartParamHash {hash :: Hash256}+  | StartParamHeight {height :: Natural}+  | StartParamTime {time :: Store.UnixTime}   deriving (Eq, Show)  instance Param StartParam where   proxyLabel = const "height"-  encodeParam _ p =+  encodeParam net ctx p =     case p of       StartParamHash h -> return [txHashToHex (TxHash h)]       StartParamHeight h -> do@@ -448,7 +445,8 @@       StartParamTime t -> do         guard $ t > 1230768000         return [cs $ show t]-  parseParam _ [s] = parseHash <|> parseHeight <|> parseUnix+  parseParam net ctx [s] =+    parseHash <|> parseHeight <|> parseUnix     where       parseHash = do         guard (T.length s == 32 * 2)@@ -462,11 +460,9 @@         x <- readMaybe $ cs s         guard $ x > 1230768000         return $ StartParamTime x-  parseParam _ _ = Nothing+  parseParam net ctx _ = Nothing -newtype OffsetParam = OffsetParam-  { getOffsetParam :: Natural-  }+newtype OffsetParam = OffsetParam {get :: Natural}   deriving (Eq, Show, Read, Enum, Ord, Num, Real, Integral)  instance Default OffsetParam where@@ -474,84 +470,74 @@  instance Param OffsetParam where   proxyLabel = const "offset"-  encodeParam _ (OffsetParam o) = Just [cs $ show o]-  parseParam _ [s] = OffsetParam <$> readMaybe (cs s)-  parseParam _ _ = Nothing+  encodeParam net ctx (OffsetParam o) = Just [cs $ show o]+  parseParam net ctx [s] = OffsetParam <$> readMaybe (cs s)+  parseParam net ctx _ = Nothing -newtype LimitParam = LimitParam-  { getLimitParam :: Natural-  }+newtype LimitParam = LimitParam {get :: Natural}   deriving (Eq, Show, Read, Enum, Ord, Num, Real, Integral)  instance Param LimitParam where   proxyLabel = const "limit"-  encodeParam _ (LimitParam l) = Just [cs $ show l]-  parseParam _ [s] = LimitParam <$> readMaybe (cs s)-  parseParam _ _ = Nothing+  encodeParam net ctx (LimitParam l) = Just [cs $ show l]+  parseParam net ctx [s] = LimitParam <$> readMaybe (cs s)+  parseParam net ctx _ = Nothing  data LimitsParam = LimitsParam-  { paramLimit :: Maybe LimitParam, -- 0 means maximum-    paramOffset :: OffsetParam,-    paramStart :: Maybe StartParam+  { limit :: Maybe LimitParam, -- 0 means maximum+    offset :: OffsetParam,+    start :: Maybe StartParam   }   deriving (Eq, Show)  instance Default LimitsParam where   def = LimitsParam Nothing def Nothing -newtype HeightParam = HeightParam-  { getHeightParam :: Natural-  }+newtype HeightParam = HeightParam {get :: Natural}   deriving (Eq, Show, Read, Enum, Ord, Num, Real, Integral)  instance Param HeightParam where   proxyLabel = const "height"-  encodeParam _ (HeightParam h) = Just [cs $ show h]-  parseParam _ [s] = HeightParam <$> readMaybe (cs s)-  parseParam _ _ = Nothing+  encodeParam net ctx (HeightParam h) = Just [cs $ show h]+  parseParam net ctx [s] = HeightParam <$> readMaybe (cs s)+  parseParam net ctx _ = Nothing -newtype HeightsParam = HeightsParam-  { getHeightsParam :: [Natural]-  }+newtype HeightsParam = HeightsParam {get :: [Natural]}   deriving (Eq, Show, Read)  instance Param HeightsParam where   proxyLabel = const "heights"-  encodeParam _ (HeightsParam hs) = Just $ cs . show <$> hs-  parseParam _ xs = HeightsParam <$> mapM (readMaybe . cs) xs+  encodeParam net ctx (HeightsParam hs) = Just $ cs . show <$> hs+  parseParam net ctx xs = HeightsParam <$> mapM (readMaybe . cs) xs -newtype TimeParam = TimeParam-  { getTimeParam :: Store.UnixTime-  }+newtype TimeParam = TimeParam {get :: Store.UnixTime}   deriving (Eq, Show, Read, Enum, Ord, Num, Real, Integral)  instance Param TimeParam where   proxyLabel = const "time"-  encodeParam _ (TimeParam t) = Just [cs $ show t]-  parseParam _ [s] = TimeParam <$> readMaybe (cs s)-  parseParam _ _ = Nothing+  encodeParam net ctx (TimeParam t) = Just [cs $ show t]+  parseParam net ctx [s] = TimeParam <$> readMaybe (cs s)+  parseParam net ctx _ = Nothing  instance Param XPubKey where   proxyLabel = const "xpub"-  encodeParam net p = Just [xPubExport net p]-  parseParam net [s] = xPubImport net s-  parseParam _ _ = Nothing+  encodeParam net ctx p = Just [xPubExport net ctx p]+  parseParam net ctx [s] = xPubImport net ctx s+  parseParam net ctx _ = Nothing  instance Param Store.DeriveType where   proxyLabel = const "derive"-  encodeParam net p = do-    guard (getSegWit net || p == Store.DeriveNormal)+  encodeParam net ctx p = do+    guard (net.segWit || p == Store.DeriveNormal)     Just [Store.deriveTypeToText p]-  parseParam net d = do+  parseParam net ctx d = do     res <- case d of       [x] -> Store.textToDeriveType x       _ -> Nothing-    guard (getSegWit net || res == Store.DeriveNormal)+    guard (net.segWit || res == Store.DeriveNormal)     return res -newtype NoCache = NoCache-  { getNoCache :: Bool-  }+newtype NoCache = NoCache {get :: Bool}   deriving (Eq, Show, Read)  instance Default NoCache where@@ -559,16 +545,14 @@  instance Param NoCache where   proxyLabel = const "nocache"-  encodeParam _ (NoCache True) = Just ["true"]-  encodeParam _ (NoCache False) = Just ["false"]-  parseParam _ = \case+  encodeParam net ctx (NoCache True) = Just ["true"]+  encodeParam net ctx (NoCache False) = Just ["false"]+  parseParam net ctx = \case     ["true"] -> Just $ NoCache True     ["false"] -> Just $ NoCache False     _ -> Nothing -newtype NoTx = NoTx-  { getNoTx :: Bool-  }+newtype NoTx = NoTx {get :: Bool}   deriving (Eq, Show, Read)  instance Default NoTx where@@ -576,31 +560,31 @@  instance Param NoTx where   proxyLabel = const "notx"-  encodeParam _ (NoTx True) = Just ["true"]-  encodeParam _ (NoTx False) = Just ["false"]-  parseParam _ = \case+  encodeParam net ctx (NoTx True) = Just ["true"]+  encodeParam net ctx (NoTx False) = Just ["false"]+  parseParam net ctx = \case     ["true"] -> Just $ NoTx True     ["false"] -> Just $ NoTx False     _ -> Nothing  instance Param BlockHash where   proxyLabel = const "block"-  encodeParam _ b = Just [blockHashToHex b]-  parseParam _ [s] = hexToBlockHash s-  parseParam _ _ = Nothing+  encodeParam net ctx b = Just [blockHashToHex b]+  parseParam net ctx [s] = hexToBlockHash s+  parseParam net ctx _ = Nothing  instance Param [BlockHash] where   proxyLabel = const "blocks"-  encodeParam _ bs = Just $ blockHashToHex <$> bs-  parseParam _ = mapM hexToBlockHash+  encodeParam net ctx bs = Just $ blockHashToHex <$> bs+  parseParam net ctx = mapM hexToBlockHash  instance Param TxHash where   proxyLabel = const "txid"-  encodeParam _ t = Just [txHashToHex t]-  parseParam _ [s] = hexToTxHash s-  parseParam _ _ = Nothing+  encodeParam net ctx t = Just [txHashToHex t]+  parseParam net ctx [s] = hexToTxHash s+  parseParam net ctx _ = Nothing  instance Param [TxHash] where   proxyLabel = const "txids"-  encodeParam _ ts = Just $ txHashToHex <$> ts-  parseParam _ = mapM hexToTxHash+  encodeParam net ctx ts = Just $ txHashToHex <$> ts+  parseParam net ctx = mapM hexToTxHash
test/Haskoin/Store/DataSpec.hs view
@@ -1,29 +1,33 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NoFieldSelectors #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Haskoin.Store.DataSpec-  ( spec,-  )-where+module Haskoin.Store.DataSpec (spec) where +import Control.Arrow (second) import Control.Monad (forM_) import Data.Aeson (FromJSON (..))-import qualified Data.ByteString as B+import Data.ByteString qualified as B import Data.String.Conversions (cs) import Haskoin import Haskoin.Store.Data+import Haskoin.Util import Haskoin.Util.Arbitrary-import Test.Hspec (Spec, describe)+import Test.Hspec import Test.QuickCheck -serialVals :: [SerialBox]-serialVals =+serialVals :: Ctx -> [SerialBox]+serialVals ctx =   [ SerialBox (arbitrary :: Gen DeriveType),-    SerialBox (arbitrary :: Gen XPubSpec),+    SerialBox (arbitraryXPubSpec ctx :: Gen XPubSpec),     SerialBox (arbitrary :: Gen BlockRef),     SerialBox (arbitrary :: Gen TxRef),     SerialBox (arbitrary :: Gen Balance),@@ -33,7 +37,7 @@     SerialBox (arbitrary :: Gen Spender),     SerialBox (arbitrary :: Gen StoreOutput),     SerialBox (arbitrary :: Gen Prev),-    SerialBox (arbitrary :: Gen TxData),+    SerialBox (arbitraryTxData ctx :: Gen TxData),     SerialBox (arbitrary :: Gen Transaction),     SerialBox (arbitrary :: Gen XPubBal),     SerialBox (arbitrary :: Gen XPubUnspent),@@ -41,7 +45,7 @@     SerialBox (arbitrary :: Gen HealthCheck),     SerialBox (arbitrary :: Gen Event),     SerialBox (arbitrary :: Gen TxId),-    SerialBox (arbitrary :: Gen PeerInformation),+    SerialBox (arbitrary :: Gen PeerInfo),     SerialBox (arbitrary :: Gen (GenericResult BlockData)),     SerialBox (arbitrary :: Gen (RawResult BlockData)),     SerialBox (arbitrary :: Gen (RawResultList BlockData))@@ -56,7 +60,7 @@     JsonBox (arbitrary :: Gen HealthCheck),     JsonBox (arbitrary :: Gen Event),     JsonBox (arbitrary :: Gen TxId),-    JsonBox (arbitrary :: Gen PeerInformation),+    JsonBox (arbitrary :: Gen PeerInfo),     JsonBox (arbitrary :: Gen (GenericResult XPubSummary)),     JsonBox (arbitrary :: Gen (RawResult BlockData)),     JsonBox (arbitrary :: Gen (RawResultList BlockData)),@@ -72,135 +76,138 @@     JsonBox (arbitrary :: Gen BinfoShortBal),     JsonBox (arbitrary :: Gen BinfoHistory),     JsonBox (arbitrary :: Gen BinfoHeader),-    JsonBox (arbitrary :: Gen BinfoBlocks)+    JsonBox (arbitrary :: Gen BinfoBlockInfos)   ] -netVals :: [NetBox]-netVals =+netVals :: Ctx -> [NetBox]+netVals ctx =   [ NetBox-      ( balanceToJSON,-        balanceToEncoding,-        balanceParseJSON,-        arbitraryNetData+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,+        arbitraryNetData :: Gen (Network, Balance)       ),     NetBox-      ( storeOutputToJSON,-        storeOutputToEncoding,-        storeOutputParseJSON,-        arbitraryNetData+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,+        arbitraryNetData :: Gen (Network, StoreOutput)       ),     NetBox-      ( unspentToJSON,-        unspentToEncoding,-        unspentParseJSON,-        arbitraryNetData+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,+        arbitraryNetData :: Gen (Network, Unspent)       ),     NetBox-      ( xPubBalToJSON,-        xPubBalToEncoding,-        xPubBalParseJSON,-        arbitraryNetData+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,+        arbitraryNetData :: Gen (Network, XPubBal)       ),     NetBox-      ( xPubUnspentToJSON,-        xPubUnspentToEncoding,-        xPubUnspentParseJSON,-        arbitraryNetData+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,+        arbitraryNetData :: Gen (Network, XPubUnspent)       ),     NetBox-      ( storeInputToJSON,-        storeInputToEncoding,-        storeInputParseJSON,+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,         arbitraryStoreInputNet       ),     NetBox-      ( blockDataToJSON,-        blockDataToEncoding,-        const parseJSON,+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,         arbitraryBlockDataNet       ),     NetBox-      ( transactionToJSON,-        transactionToEncoding,-        transactionParseJSON,-        arbitraryNetData+      ( marshalValue,+        marshalEncoding,+        unmarshalValue,+        arbitraryNetData :: Gen (Network, Transaction)       ),     NetBox-      ( binfoMultiAddrToJSON,-        binfoMultiAddrToEncoding,-        binfoMultiAddrParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoMultiAddr ctx)       ),     NetBox-      ( binfoBalanceToJSON,-        binfoBalanceToEncoding,-        binfoBalanceParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoBalance ctx)       ),     NetBox-      ( binfoBlockToJSON,-        binfoBlockToEncoding,-        binfoBlockParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoBlock ctx)       ),     NetBox-      ( binfoTxToJSON,-        binfoTxToEncoding,-        binfoTxParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoTx ctx)       ),     NetBox-      ( binfoTxInputToJSON,-        binfoTxInputToEncoding,-        binfoTxInputParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoTxInput ctx)       ),     NetBox-      ( binfoTxOutputToJSON,-        binfoTxOutputToEncoding,-        binfoTxOutputParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoTxOutput ctx)       ),     NetBox-      ( binfoXPubPathToJSON,-        binfoXPubPathToEncoding,-        binfoXPubPathParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoXPubPath ctx)       ),     NetBox-      ( binfoUnspentToJSON,-        binfoUnspentToEncoding,-        binfoUnspentParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoUnspent ctx)       ),     NetBox-      ( binfoBlocksToJSON,-        binfoBlocksToEncoding,-        binfoBlocksParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (listOf (arbitraryBinfoBlock ctx))       ),     NetBox-      ( binfoRawAddrToJSON,-        binfoRawAddrToEncoding,-        binfoRawAddrParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoRawAddr ctx)       ),     NetBox-      ( binfoMempoolToJSON,-        binfoMempoolToEncoding,-        binfoMempoolParseJSON,-        arbitraryNetData+      ( marshalValue . (,ctx),+        marshalEncoding . (,ctx),+        unmarshalValue . (,ctx),+        genNetData (arbitraryBinfoMempool ctx)       )   ]  spec :: Spec-spec = do+spec = prepareContext $ \ctx -> do   describe "Binary Encoding" $-    forM_ serialVals $ \(SerialBox g) -> testSerial g+    forM_ (serialVals ctx) $+      \(SerialBox g) -> testSerial g   describe "JSON Encoding" $-    forM_ jsonVals $ \(JsonBox g) -> testJson g+    forM_ jsonVals $+      \(JsonBox g) -> testJson g   describe "JSON Encoding with Network" $-    forM_ netVals $ \(NetBox (j, e, p, g)) -> testNetJson j e p g+    forM_ (netVals ctx) $+      \(NetBox (j, e, p, g)) -> testNetJson j e p g  instance Arbitrary BlockRef where   arbitrary =@@ -209,16 +216,16 @@ instance Arbitrary Prev where   arbitrary = Prev <$> arbitraryBS1 <*> arbitrary -instance Arbitrary TxData where-  arbitrary =-    TxData-      <$> arbitrary-      <*> arbitraryTx btc-      <*> arbitrary-      <*> arbitrary-      <*> arbitrary-      <*> arbitrary-      <*> arbitrary+arbitraryTxData :: Ctx -> Gen TxData+arbitraryTxData ctx =+  TxData+    <$> arbitrary+    <*> arbitraryTx btc ctx+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary  instance Arbitrary StoreInput where   arbitrary =@@ -243,9 +250,12 @@   net <- arbitraryNetwork   store <- arbitrary   let res-        | getSegWit net = store-        | otherwise = store {inputWitness = []}+        | net.segWit = store+        | otherwise = witless store   return (net, res)+  where+    witless StoreInput {..} = StoreInput {witness = [], ..}+    witless StoreCoinbase {..} = StoreCoinbase {witness = [], ..}  instance Arbitrary Spender where   arbitrary = Spender <$> arbitraryTxHash <*> arbitrary@@ -274,9 +284,9 @@       <*> arbitrary       <*> arbitrary -instance Arbitrary PeerInformation where+instance Arbitrary PeerInfo where   arbitrary =-    PeerInformation+    PeerInfo       <$> (cs <$> listOf arbitraryUnicodeChar)       <*> listOf arbitraryPrintableChar       <*> arbitrary@@ -318,6 +328,7 @@       <*> arbitrary       <*> arbitrary       <*> arbitrary+      <*> arbitrary  instance Arbitrary RejectCode where   arbitrary =@@ -332,8 +343,8 @@         RejectCheckpoint       ] -instance Arbitrary XPubSpec where-  arbitrary = XPubSpec <$> (snd <$> arbitraryXPubKey) <*> arbitrary+arbitraryXPubSpec :: Ctx -> Gen XPubSpec+arbitraryXPubSpec ctx = XPubSpec <$> (snd <$> arbitraryXPubKey ctx) <*> arbitrary  instance Arbitrary DeriveType where   arbitrary = elements [DeriveNormal, DeriveP2SH, DeriveP2WPKH]@@ -380,19 +391,19 @@ arbitraryBlockDataNet :: Gen (Network, BlockData) arbitraryBlockDataNet = do   net <- arbitraryNetwork-  dat <- arbitrary+  dat@BlockData {..} <- arbitrary   let res-        | getSegWit net = dat-        | otherwise = dat {blockDataWeight = 0}+        | net.segWit = dat+        | otherwise = BlockData {weight = 0, ..}   return (net, res) -instance Arbitrary a => Arbitrary (GenericResult a) where+instance (Arbitrary a) => Arbitrary (GenericResult a) where   arbitrary = GenericResult <$> arbitrary -instance Arbitrary a => Arbitrary (RawResult a) where+instance (Arbitrary a) => Arbitrary (RawResult a) where   arbitrary = RawResult <$> arbitrary -instance Arbitrary a => Arbitrary (RawResultList a) where+instance (Arbitrary a) => Arbitrary (RawResultList a) where   arbitrary = RawResultList <$> arbitrary  instance Arbitrary XPubBal where@@ -441,157 +452,156 @@         BinfoTxIdIndex <$> arbitrary       ] -instance Arbitrary BinfoMultiAddr where-  arbitrary = do-    getBinfoMultiAddrAddresses <- arbitrary-    getBinfoMultiAddrWallet <- arbitrary-    getBinfoMultiAddrTxs <- resize 10 arbitrary-    getBinfoMultiAddrInfo <- arbitrary-    getBinfoMultiAddrRecommendFee <- arbitrary-    getBinfoMultiAddrCashAddr <- arbitrary-    return BinfoMultiAddr {..}+arbitraryBinfoMultiAddr :: Ctx -> Gen BinfoMultiAddr+arbitraryBinfoMultiAddr ctx = do+  addresses <- listOf1 $ arbitraryBinfoBalance ctx+  wallet <- arbitrary+  txs <- listOf $ arbitraryBinfoTx ctx+  info <- arbitrary+  recommendFee <- arbitrary+  cashAddr <- arbitrary+  return BinfoMultiAddr {..} -instance Arbitrary BinfoRawAddr where-  arbitrary = do-    binfoRawAddr <--      oneof-        [ BinfoAddr <$> arbitraryAddress,-          BinfoXpub . snd <$> arbitraryXPubKey-        ]-    binfoRawBalance <- arbitrary-    binfoRawTxCount <- arbitrary-    binfoRawUnredeemed <- arbitrary-    binfoRawReceived <- arbitrary-    binfoRawSent <- arbitrary-    binfoRawTxs <- arbitrary-    return $ BinfoRawAddr {..}+arbitraryBinfoRawAddr :: Ctx -> Gen BinfoRawAddr+arbitraryBinfoRawAddr ctx = do+  address <-+    oneof+      [ BinfoAddr <$> arbitraryAddress,+        BinfoXpub . snd <$> arbitraryXPubKey ctx+      ]+  balance <- arbitrary+  ntx <- arbitrary+  utxo <- arbitrary+  received <- arbitrary+  sent <- arbitrary+  txs <- listOf $ arbitraryBinfoTx ctx+  return $ BinfoRawAddr {..}  instance Arbitrary BinfoShortBal where   arbitrary = BinfoShortBal <$> arbitrary <*> arbitrary <*> arbitrary -instance Arbitrary BinfoBalance where-  arbitrary = do-    getBinfoAddress <- arbitraryAddress-    getBinfoAddrTxCount <- arbitrary-    getBinfoAddrReceived <- arbitrary-    getBinfoAddrSent <- arbitrary-    getBinfoAddrBalance <- arbitrary-    getBinfoXPubKey <- snd <$> arbitraryXPubKey-    getBinfoXPubAccountIndex <- arbitrary-    getBinfoXPubChangeIndex <- arbitrary-    elements [BinfoAddrBalance {..}, BinfoXPubBalance {..}]+arbitraryBinfoBalance :: Ctx -> Gen BinfoBalance+arbitraryBinfoBalance ctx = do+  address <- arbitraryAddress+  txs <- arbitrary+  received <- arbitrary+  sent <- arbitrary+  balance <- arbitrary+  xpub <- snd <$> arbitraryXPubKey ctx+  external <- arbitrary+  change <- arbitrary+  elements [BinfoAddrBalance {..}, BinfoXPubBalance {..}]  instance Arbitrary BinfoWallet where   arbitrary = do-    getBinfoWalletBalance <- arbitrary-    getBinfoWalletTxCount <- arbitrary-    getBinfoWalletFilteredCount <- arbitrary-    getBinfoWalletTotalReceived <- arbitrary-    getBinfoWalletTotalSent <- arbitrary+    balance <- arbitrary+    txs <- arbitrary+    filtered <- arbitrary+    received <- arbitrary+    sent <- arbitrary     return BinfoWallet {..} -instance Arbitrary BinfoBlock where-  arbitrary = do-    getBinfoBlockHash <- arbitraryBlockHash-    getBinfoBlockVer <- arbitrary-    getBinfoPrevBlock <- arbitraryBlockHash-    getBinfoMerkleRoot <- getTxHash <$> arbitraryTxHash-    getBinfoBlockTime <- arbitrary-    getBinfoBlockBits <- arbitrary-    getBinfoNextBlock <- listOf arbitraryBlockHash-    getBinfoBlockTxCount <- arbitrary-    getBinfoBlockFee <- arbitrary-    getBinfoBlockNonce <- arbitrary-    getBinfoBlockSize <- arbitrary-    getBinfoBlockIndex <- arbitrary-    getBinfoBlockMain <- arbitrary-    getBinfoBlockHeight <- arbitrary-    getBinfoBlockWeight <- arbitrary-    getBinfoBlockTx <- resize 5 arbitrary-    return BinfoBlock {..}+arbitraryBinfoBlock :: Ctx -> Gen BinfoBlock+arbitraryBinfoBlock ctx = do+  hash <- arbitraryBlockHash+  version <- arbitrary+  prev <- arbitraryBlockHash+  merkle <- (.get) <$> arbitraryTxHash+  timestamp <- arbitrary+  bits <- arbitrary+  next <- listOf arbitraryBlockHash+  ntx <- arbitrary+  fee <- arbitrary+  nonce <- arbitrary+  size <- arbitrary+  index <- arbitrary+  main <- arbitrary+  height <- arbitrary+  weight <- arbitrary+  txs <- resize 5 $ listOf $ arbitraryBinfoTx ctx+  return BinfoBlock {..} -instance Arbitrary BinfoTx where-  arbitrary = do-    getBinfoTxHash <- arbitraryTxHash-    getBinfoTxVer <- arbitrary-    getBinfoTxInputs <- resize 5 $ listOf1 arbitrary-    getBinfoTxOutputs <- resize 5 $ 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-    getBinfoTxRBF <- arbitrary-    getBinfoTxTime <- arbitrary-    getBinfoTxBlockIndex <- arbitrary-    getBinfoTxBlockHeight <- arbitrary-    getBinfoTxResultBal <- arbitrary-    return BinfoTx {..}+arbitraryBinfoTx :: Ctx -> Gen BinfoTx+arbitraryBinfoTx ctx = do+  txid <- arbitraryTxHash+  version <- arbitrary+  inputs <- resize 5 $ listOf1 $ arbitraryBinfoTxInput ctx+  outputs <- resize 5 $ listOf1 $ arbitraryBinfoTxOutput ctx+  let inputCount = fromIntegral $ length inputs+      outputCount = fromIntegral $ length outputs+  size <- arbitrary+  weight <- arbitrary+  fee <- arbitrary+  relayed <- cs <$> listOf arbitraryUnicodeChar+  locktime <- arbitrary+  index <- arbitrary+  doubleSpend <- arbitrary+  rbf <- arbitrary+  timestamp <- arbitrary+  blockIndex <- arbitrary+  blockHeight <- arbitrary+  balance <- 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 {..}+arbitraryBinfoTxInput :: Ctx -> Gen BinfoTxInput+arbitraryBinfoTxInput ctx = do+  sequence <- arbitrary+  witness <- B.pack <$> listOf arbitrary+  script <- B.pack <$> listOf arbitrary+  index <- arbitrary+  output <- arbitraryBinfoTxOutput ctx+  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 {..}+arbitraryBinfoTxOutput :: Ctx -> Gen BinfoTxOutput+arbitraryBinfoTxOutput ctx = do+  typ <- arbitrary+  spent <- arbitrary+  value <- arbitrary+  index <- arbitrary+  txidx <- arbitrary+  script <- B.pack <$> listOf arbitrary+  spenders <- arbitrary+  address <- arbitraryMaybe arbitraryAddress+  xpub <- arbitraryMaybe $ arbitraryBinfoXPubPath ctx+  return BinfoTxOutput {..}  instance Arbitrary BinfoSpender where   arbitrary = do-    getBinfoSpenderTxIndex <- arbitrary-    getBinfoSpenderIndex <- arbitrary+    txidx <- arbitrary+    input <- arbitrary     return BinfoSpender {..} -instance Arbitrary BinfoXPubPath where-  arbitrary = do-    getBinfoXPubPathKey <- snd <$> arbitraryXPubKey-    getBinfoXPubPathDeriv <- arbitrarySoftPath-    return BinfoXPubPath {..}+arbitraryBinfoXPubPath :: Ctx -> Gen BinfoXPubPath+arbitraryBinfoXPubPath ctx = do+  key <- snd <$> arbitraryXPubKey ctx+  deriv <- arbitrarySoftPath+  return BinfoXPubPath {..}  instance Arbitrary BinfoInfo where   arbitrary = do-    getBinfoConnected <- arbitrary-    getBinfoConversion <- arbitrary-    getBinfoLocal <- arbitrary-    getBinfoBTC <- arbitrary-    getBinfoLatestBlock <- arbitrary+    connected <- arbitrary+    conversion <- arbitrary+    fiat <- arbitrary+    crypto <- arbitrary+    head <- arbitrary     return BinfoInfo {..}  instance Arbitrary BinfoBlockInfo where   arbitrary = do-    getBinfoBlockInfoHash <- arbitraryBlockHash-    getBinfoBlockInfoHeight <- arbitrary-    getBinfoBlockInfoTime <- arbitrary-    getBinfoBlockInfoIndex <- arbitrary+    hash <- arbitraryBlockHash+    height <- arbitrary+    timestamp <- arbitrary+    index <- 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+    code <- cs <$> listOf1 arbitraryUnicodeChar+    symbol <- cs <$> listOf1 arbitraryUnicodeChar+    name <- cs <$> listOf1 arbitraryUnicodeChar+    conversion <- arbitrary+    after <- arbitrary+    local <- arbitrary     return BinfoSymbol {..}  instance Arbitrary BinfoRate where@@ -599,39 +609,40 @@  instance Arbitrary BinfoTicker where   arbitrary = do-    binfoTicker15m <- arbitrary-    binfoTickerSell <- arbitrary-    binfoTickerBuy <- arbitrary-    binfoTickerLast <- arbitrary-    binfoTickerSymbol <- cs <$> listOf1 arbitraryUnicodeChar+    fifteen <- arbitrary+    sell <- arbitrary+    buy <- arbitrary+    last <- arbitrary+    symbol <- cs <$> listOf1 arbitraryUnicodeChar     return BinfoTicker {..}  instance Arbitrary BinfoHistory where   arbitrary = do-    binfoHistoryDate <- cs <$> listOf1 arbitraryUnicodeChar-    binfoHistoryTime <- cs <$> listOf1 arbitraryUnicodeChar-    binfoHistoryType <- cs <$> listOf1 arbitraryUnicodeChar-    binfoHistoryAmount <- arbitrary-    binfoHistoryValueThen <- arbitrary-    binfoHistoryValueNow <- arbitrary-    binfoHistoryExchangeRateThen <- arbitrary-    binfoHistoryTx <- arbitraryTxHash-    binfoHistoryFee <- arbitrary+    date <- cs <$> listOf1 arbitraryUnicodeChar+    time <- cs <$> listOf1 arbitraryUnicodeChar+    typ <- cs <$> listOf1 arbitraryUnicodeChar+    amount <- arbitrary+    valueThen <- arbitrary+    valueNow <- arbitrary+    rateThen <- arbitrary+    txid <- arbitraryTxHash+    fee <- arbitrary     return BinfoHistory {..} -instance Arbitrary BinfoUnspent where-  arbitrary = do-    getBinfoUnspentHash <- arbitraryTxHash-    getBinfoUnspentOutputIndex <- arbitrary-    getBinfoUnspentScript <- B.pack <$> listOf arbitrary-    getBinfoUnspentValue <- arbitrary-    getBinfoUnspentConfirmations <- arbitrary-    getBinfoUnspentTxIndex <- arbitrary-    getBinfoUnspentXPub <- arbitrary-    return BinfoUnspent {..}+arbitraryBinfoUnspent :: Ctx -> Gen BinfoUnspent+arbitraryBinfoUnspent ctx = do+  txid <- arbitraryTxHash+  index <- arbitrary+  script <- B.pack <$> listOf arbitrary+  value <- arbitrary+  confirmations <- arbitrary+  txidx <- arbitrary+  xpub <- arbitraryMaybe $ arbitraryBinfoXPubPath ctx+  return BinfoUnspent {..} -instance Arbitrary BinfoUnspents where-  arbitrary = BinfoUnspents <$> arbitrary+arbitraryBinfoUnspents :: Ctx -> Gen BinfoUnspents+arbitraryBinfoUnspents ctx =+  fmap BinfoUnspents $ listOf $ arbitraryBinfoUnspent ctx  instance Arbitrary BinfoHeader where   arbitrary =@@ -642,8 +653,9 @@       <*> arbitrary       <*> arbitrary -instance Arbitrary BinfoMempool where-  arbitrary = BinfoMempool <$> arbitrary+arbitraryBinfoMempool :: Ctx -> Gen BinfoMempool+arbitraryBinfoMempool ctx =+  fmap BinfoMempool $ listOf $ arbitraryBinfoTx ctx -instance Arbitrary BinfoBlocks where-  arbitrary = BinfoBlocks <$> arbitrary+instance Arbitrary BinfoBlockInfos where+  arbitrary = BinfoBlockInfos <$> arbitrary
test/Haskoin/Store/WebCommonSpec.hs view
@@ -1,14 +1,13 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Haskoin.Store.WebCommonSpec-  ( spec,-  )-where+module Haskoin.Store.WebCommonSpec (spec) where  import Control.Monad (forM_) import Data.Proxy@@ -18,6 +17,7 @@ import Haskoin.Store.Data import Haskoin.Store.DataSpec () import Haskoin.Store.WebCommon+import Haskoin.Util import Haskoin.Util.Arbitrary import Test.Hspec import Test.Hspec.QuickCheck@@ -25,8 +25,8 @@  data GenBox = forall p. (Show p, Eq p, Param p) => GenBox (Gen p) -params :: [GenBox]-params =+params :: Ctx -> [GenBox]+params ctx =   [ GenBox arbitraryAddress,     GenBox (listOf arbitraryAddress),     GenBox (arbitrary :: Gen StartParam),@@ -35,7 +35,7 @@     GenBox (arbitrarySizedNatural :: Gen HeightParam),     GenBox (arbitrary :: Gen HeightsParam),     GenBox (arbitrarySizedNatural :: Gen TimeParam),-    GenBox (snd <$> arbitraryXPubKey :: Gen XPubKey),+    GenBox (snd <$> arbitraryXPubKey ctx :: Gen XPubKey),     GenBox (arbitrary :: Gen DeriveType),     GenBox (NoCache <$> arbitrary :: Gen NoCache),     GenBox (NoTx <$> arbitrary :: Gen NoTx),@@ -46,16 +46,17 @@   ]  spec :: Spec-spec =+spec = prepareContext $ \ctx ->   describe "Parameter encoding" $-    forM_ params $ \(GenBox g) -> testParam g+    forM_ (params ctx) $+      \(GenBox g) -> testParam ctx g -testParam :: (Eq a, Show a, Param a) => Gen a -> Spec-testParam pGen =+testParam :: (Eq a, Show a, Param a) => Ctx -> Gen a -> Spec+testParam ctx pGen =   prop ("encodeParam/parseParam identity for parameter " <> name) $     forAll pGen $ \p ->-      case encodeParam btc p of-        Just txts -> parseParam btc txts `shouldBe` Just p+      case encodeParam btc ctx p of+        Just txts -> parseParam btc ctx txts `shouldBe` Just p         _ -> expectationFailure "Param encoding failed"   where     name = cs $ proxyLabel $ proxy pGen