{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Network.Haskoin.Store.Types where import Control.Applicative import Control.Exception import Control.Monad.Reader import Data.Aeson as A import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.Function import Data.Int import Data.Maybe import Data.Serialize as S import Data.String.Conversions import Data.Word import Database.RocksDB (DB) import Database.RocksDB.Query as R import Haskoin import Haskoin.Node import NQE -- | Reasons why a transaction may not get imported. data TxException = DoubleSpend -- ^ outputs already spent by another transaction | OverSpend -- ^ outputs larger than inputs | OrphanTx -- ^ inputs unknown | NonStandard -- ^ non-standard transaction rejected by peer | LowFee -- ^ pony up | Dust -- ^ an output is too small | NoPeers -- ^ no peers to send the transaction to | InvalidTx -- ^ transaction is invalid in some other way | CouldNotImport -- ^ could not import for an unknown reason | PeerIsGone -- ^ the peer that got the transaction disconnected | AlreadyImported -- ^ the transaction is already in the database | PublishTimeout -- ^ some timeout was reached while publishing | PeerRejectOther -- ^ peer rejected transaction for unknown reason | NotAtHeight -- ^ this node is not yet synchronized deriving (Eq) instance Show TxException where show InvalidTx = "invalid" show DoubleSpend = "double-spend" show OverSpend = "not enough funds" show OrphanTx = "orphan" show AlreadyImported = "already imported" show NoPeers = "no peers" show NonStandard = "non-standard" show LowFee = "low fee" show Dust = "dust" show PeerIsGone = "peer disconnected" show CouldNotImport = "could not import" show PublishTimeout = "publish timeout" show PeerRejectOther = "peer rejected for unknown reason" show NotAtHeight = "not at height" instance Exception TxException -- | Wrapper for an transaction that can be deserialized from a JSON object. newtype NewTx = NewTx { newTx :: Tx } deriving (Show, Eq, Ord) -- | Configuration for a block store. data BlockConfig = BlockConfig { blockConfMailbox :: !BlockStore -- ^ block store mailbox , blockConfManager :: !Manager -- ^ peer manager from running node , blockConfChain :: !Chain -- ^ chain from a running node , blockConfListener :: !(Listen StoreEvent) -- ^ listener for store events , blockConfDB :: !DB -- ^ RocksDB database handle , blockConfNet :: !Network -- ^ network constants } -- | Event that the store can generate. data StoreEvent = BestBlock !BlockHash -- ^ new best block | MempoolNew !TxHash -- ^ new mempool transaction | TxException !TxHash !TxException -- ^ published tx could not be imported | PeerConnected !Peer -- ^ new peer connected | PeerDisconnected !Peer -- ^ peer has disconnected | PeerPong !Peer !Word64 -- ^ peer responded 'Ping' -- | Messages that a 'BlockStore' can accept. data BlockMessage = BlockChainNew !BlockNode -- ^ new block header in chain | BlockPeerConnect !Peer -- ^ new peer connected | BlockPeerDisconnect !Peer -- ^ peer disconnected | BlockReceived !Peer !Block -- ^ new block received from a peer | BlockNotReceived !Peer !BlockHash -- ^ peer could not deliver a block | TxReceived !Peer !Tx -- ^ transaction received from a peer | TxAvailable !Peer ![TxHash] -- ^ peer has transactions available | TxPublished !Tx -- ^ transaction has been published successfully | PongReceived !Peer !Word64 -- ^ peer responded to a 'Ping' -- | Mailbox for block store. type BlockStore = Inbox BlockMessage -- | Database key for an address transaction. data AddrTxKey = AddrTxKey { addrTxKey :: !Address , addrTxHeight :: !(Maybe BlockHeight) , addrTxPos :: !(Maybe Word32) , addrTxHash :: !TxHash } -- ^ key for a transaction affecting an address | ShortAddrTxKey { addrTxKey :: !Address } | ShortAddrTxKeyHeight { addrTxKey :: !Address , addrTxHeight :: !(Maybe BlockHeight)} -- ^ short key that matches all entries deriving (Show, Eq) -- | Database key for an address output. data AddrOutKey = AddrOutKey { addrOutputAddress :: !Address , addrOutputHeight :: !(Maybe BlockHeight) , addrOutputPos :: !(Maybe Word32) , addrOutPoint :: !OutPoint } -- ^ full key | ShortAddrOutKey { addrOutputAddress :: !Address } -- ^ short key for all spent or unspent outputs | ShortAddrOutKeyHeight { addrOutputAddress :: !Address , addrOutputHeight :: !(Maybe BlockHeight) } -- ^ short key for all outputs at a given height deriving (Show, Eq) instance Ord AddrOutKey where compare = compare `on` f where f AddrOutKey {..} = ( fromMaybe maxBound addrOutputHeight , fromMaybe maxBound addrOutputPos , outPointIndex addrOutPoint) f _ = undefined -- | Database value for a block entry. data BlockValue = BlockValue { blockValueHeight :: !BlockHeight -- ^ height of the block in the chain , blockValueWork :: !BlockWork -- ^ accumulated work in that block , blockValueHeader :: !BlockHeader -- ^ block header , blockValueSize :: !Word32 -- ^ size of the block including witnesses , blockValueTxs :: ![TxHash] -- ^ block transactions } deriving (Show, Eq, Ord) -- | Reference to a block where a transaction is stored. data BlockRef = BlockRef { blockRefHash :: !BlockHash -- ^ block header hash , blockRefHeight :: !BlockHeight -- ^ block height in the chain , blockRefPos :: !Word32 -- ^ position of transaction within the block } deriving (Show, Eq) instance Ord BlockRef where compare = compare `on` f where f BlockRef {..} = (blockRefHeight, blockRefPos) -- | Detailed transaction information. data DetailedTx = DetailedTx { detailedTxData :: !Tx -- ^ 'Tx' object , detailedTxFee :: !Word64 -- ^ transaction fees paid to miners in satoshi , detailedTxInputs :: ![DetailedInput] -- ^ transaction inputs , detailedTxOutputs :: ![DetailedOutput] -- ^ transaction outputs , detailedTxBlock :: !(Maybe BlockRef) -- ^ block information for this transaction } deriving (Show, Eq) -- | Input information. data DetailedInput = DetailedCoinbase { detInOutPoint :: !OutPoint -- ^ output being spent (should be zeroes) , detInSequence :: !Word32 -- ^ sequence , detInSigScript :: !ByteString -- ^ input script data (not valid script) , detInNetwork :: !Network -- ^ network constants } -- ^ coinbase input details | DetailedInput { detInOutPoint :: !OutPoint -- ^ output being spent , detInSequence :: !Word32 -- ^ sequence , detInSigScript :: !ByteString -- ^ signature (input) script , detInPkScript :: !ByteString -- ^ pubkey (output) script from previous tx , detInValue :: !Word64 -- ^ amount in satoshi being spent spent , detInBlock :: !(Maybe BlockRef) -- ^ block where this input is found , detInNetwork :: !Network -- ^ network constants } -- ^ regular input details deriving (Show, Eq) data PeerInformation = PeerInformation { userAgent :: !ByteString , address :: !ByteString , connected :: !Bool , version :: !Word32 , services :: !Word64 , relay :: !Bool , block :: !BlockHash , height :: !BlockHeight , nonceLocal :: !Word64 , nonceRemote :: !Word64 } deriving (Show, Eq) isCoinbase :: DetailedInput -> Bool isCoinbase DetailedCoinbase {} = True isCoinbase _ = False -- | Output information. data DetailedOutput = DetailedOutput { detOutValue :: !Word64 -- ^ amount in satoshi , detOutScript :: !ByteString -- ^ pubkey (output) script , detOutSpender :: !(Maybe Spender) -- ^ input spending this transaction , detOutNetwork :: !Network -- ^ network constants } deriving (Show, Eq) -- | Address balance information. data AddressBalance = AddressBalance { addressBalAddress :: !Address -- ^ address balance , addressBalConfirmed :: !Word64 -- ^ confirmed balance , addressBalUnconfirmed :: !Int64 -- ^ unconfirmed balance (can be negative) , addressUtxoCount :: !Word64 -- ^ number of unspent outputs } deriving (Show, Eq) -- | Transaction record in database. data TxRecord = TxRecord { txValueBlock :: !(Maybe BlockRef) -- ^ block information , txValue :: !Tx -- ^ transaction data , txValuePrevOuts :: [(OutPoint, PrevOut)] -- ^ previous output information } deriving (Show, Eq, Ord) -- | Output key in database. newtype OutputKey = OutputKey { outPoint :: OutPoint } deriving (Show, Eq, Ord) -- | Previous output data. data PrevOut = PrevOut { prevOutValue :: !Word64 -- ^ value of output in satoshi , prevOutBlock :: !(Maybe BlockRef) -- ^ block information for spent output , prevOutScript :: !ByteString -- ^ pubkey (output) script } deriving (Show, Eq, Ord) -- | Output data. data Output = Output { outputValue :: !Word64 -- ^ value of output in satoshi , outBlock :: !(Maybe BlockRef) -- ^ block infromation for output , outScript :: !ByteString -- ^ pubkey (output) script , outSpender :: !(Maybe Spender) -- ^ input spending this output } deriving (Show, Eq, Ord) -- | Prepare previous output. outputToPrevOut :: Output -> PrevOut outputToPrevOut Output {..} = PrevOut { prevOutValue = outputValue , prevOutBlock = outBlock , prevOutScript = outScript } -- | Convert previous output to unspent output. prevOutToOutput :: PrevOut -> Output prevOutToOutput PrevOut {..} = Output { outputValue = prevOutValue , outBlock = prevOutBlock , outScript = prevOutScript , outSpender = Nothing } -- | Information about input spending output. data Spender = Spender { spenderHash :: !TxHash -- ^ input transaction hash , spenderIndex :: !Word32 -- ^ input position in transaction , spenderBlock :: !(Maybe BlockRef) -- ^ block information } deriving (Show, Eq, Ord) -- | Aggregate key for transactions and outputs. data MultiTxKey = MultiTxKey !TxKey -- ^ key for transaction | MultiTxOutKey !OutputKey -- ^ key for output | ShortMultiTxKey !TxHash -- ^ short key that matches all deriving (Show, Eq, Ord) -- | Aggregate database key for transactions and outputs. data MultiTxValue = MultiTx !TxRecord -- ^ transaction record | MultiTxOutput !Output -- ^ records for all outputs deriving (Show, Eq, Ord) -- | Transaction database key. newtype TxKey = TxKey TxHash deriving (Show, Eq, Ord) -- | Mempool transaction database key. data MempoolKey = MempoolKey TxHash -- ^ key for a mempool transaction | ShortMempoolKey -- ^ short key that matches all deriving (Show, Eq, Ord) -- | Orphan transaction database key. data OrphanKey = OrphanKey TxHash -- ^ key for an orphan transaction | ShortOrphanKey -- ^ short key that matches all deriving (Show, Eq, Ord) -- | Block entry database key. newtype BlockKey = BlockKey BlockHash deriving (Show, Eq, Ord) -- | Block height database key. newtype HeightKey = HeightKey BlockHeight deriving (Show, Eq, Ord) -- | Address balance database key. newtype BalanceKey = BalanceKey { balanceAddress :: Address } deriving (Show, Eq) -- | Address balance database value. data Balance = Balance { balanceValue :: !Word64 -- ^ balance in satoshi , balanceUnconfirmed :: !Int64 -- ^ unconfirmed balance in satoshi (can be negative) , balanceUtxoCount :: !Word64 -- ^ number of unspent outputs } deriving (Show, Eq, Ord) -- | Default balance for an address. emptyBalance :: Balance emptyBalance = Balance { balanceValue = 0 , balanceUnconfirmed = 0 , balanceUtxoCount = 0 } -- | Key for best block in database. data BestBlockKey = BestBlockKey deriving (Show, Eq, Ord) -- | Address output. data AddrOutput = AddrOutput { addrOutputKey :: !AddrOutKey , addrOutput :: !Output } deriving (Eq, Show) instance Ord AddrOutput where compare = compare `on` addrOutputKey -- | Serialization format for addresses in database. newtype StoreAddress = StoreAddress Address deriving (Show, Eq) instance Key BestBlockKey -- 0x00 instance Key BlockKey -- 0x01 · BlockHash instance Key TxKey -- 0x02 · TxHash · 0x00 instance Key OutputKey -- 0x02 · TxHash · 0x01 · OutputIndex instance Key MultiTxKey -- 0x02 · TxHash -- 0x02 · TxHash · 0x00 -- 0x02 · TxHash · 0x01 · OutputIndex instance Key HeightKey -- 0x03 · InvBlockHeight instance Key BalanceKey -- 0x04 · Storeaddress instance Key AddrTxKey -- 0x05 · StoreAddress · InvBlockHeight · InvBlockPos · TxHash -- 0x05 · StoreAddress · InvBlockHeight -- 0x05 · StoreAddress instance Key AddrOutKey -- 0x06 · StoreAddress · InvBlockHeight · InvBlockPos -- 0x06 · StoreAddress · InvBlockHeight -- 0x06 · StoreAddress instance Key MempoolKey -- 0x07 · TxHash -- 0x07 instance Key OrphanKey -- 0x08 · TxHash -- 0x08 instance R.KeyValue BestBlockKey BlockHash instance R.KeyValue BlockKey BlockValue instance R.KeyValue TxKey TxRecord instance R.KeyValue AddrOutKey Output instance R.KeyValue MultiTxKey MultiTxValue instance R.KeyValue HeightKey BlockHash instance R.KeyValue BalanceKey Balance instance R.KeyValue AddrTxKey () instance R.KeyValue OutputKey Output instance R.KeyValue MempoolKey () instance R.KeyValue OrphanKey Tx instance Serialize MempoolKey where put (MempoolKey h) = do putWord8 0x07 put h put ShortMempoolKey = putWord8 0x07 get = do guard . (== 0x07) =<< getWord8 MempoolKey <$> get instance Serialize OrphanKey where put (OrphanKey h) = do putWord8 0x08 put h put ShortOrphanKey = putWord8 0x08 get = do guard . (== 0x08) =<< getWord8 OrphanKey <$> get instance Serialize BalanceKey where put BalanceKey {..} = do putWord8 0x04 put (StoreAddress balanceAddress) get = do guard . (== 0x04) =<< getWord8 StoreAddress balanceAddress <- get return BalanceKey {..} instance Serialize Balance where put Balance {..} = do put balanceValue put balanceUnconfirmed put balanceUtxoCount get = do balanceValue <- get balanceUnconfirmed <- get balanceUtxoCount <- get return Balance {..} instance Serialize AddrTxKey where put AddrTxKey {..} = do putWord8 0x05 put $ StoreAddress addrTxKey put (maybe 0 (maxBound -) addrTxHeight) put (maybe 0 (maxBound -) addrTxPos) put addrTxHash put ShortAddrTxKey {..} = do putWord8 0x05 put $ StoreAddress addrTxKey put ShortAddrTxKeyHeight {..} = do putWord8 0x05 put $ StoreAddress addrTxKey put (maybe 0 (maxBound -) addrTxHeight) get = do guard . (== 0x05) =<< getWord8 StoreAddress addrTxKey <- get h <- (maxBound -) <$> get let addrTxHeight | h == 0 = Nothing | otherwise = Just h p <- (maxBound -) <$> get let addrTxPos | p == 0 = Nothing | otherwise = Just p addrTxHash <- get return AddrTxKey {..} -- | Beginning of address output database key. addrKeyStart :: Address -> Put addrKeyStart a = put (StoreAddress a) instance Serialize AddrOutKey where put AddrOutKey {..} = do putWord8 0x06 put $ StoreAddress addrOutputAddress put (maybe 0 (maxBound -) addrOutputHeight) put (maybe 0 (maxBound -) addrOutputPos) put addrOutPoint put ShortAddrOutKey {..} = do putWord8 0x06 put $ StoreAddress addrOutputAddress put ShortAddrOutKeyHeight {..} = do putWord8 0x06 put $ StoreAddress addrOutputAddress put (maybe 0 (maxBound -) addrOutputHeight) get = do guard . (== 0x06) =<< getWord8 StoreAddress addrOutputAddress <- get record addrOutputAddress where record addrOutputAddress = do h <- (maxBound -) <$> get let addrOutputHeight | h == 0 = Nothing | otherwise = Just h p <- (maxBound -) <$> get let addrOutputPos | p == 0 = Nothing | otherwise = Just p addrOutPoint <- get return AddrOutKey {..} instance Serialize MultiTxKey where put (MultiTxKey k) = put k put (MultiTxOutKey k) = put k put (ShortMultiTxKey k) = putWord8 0x02 >> put k get = MultiTxKey <$> get <|> MultiTxOutKey <$> get instance Serialize MultiTxValue where put (MultiTx v) = put v put (MultiTxOutput v) = put v get = MultiTx <$> get <|> MultiTxOutput <$> get instance Serialize Spender where put Spender {..} = do put spenderHash put spenderIndex put spenderBlock get = do spenderHash <- get spenderIndex <- get spenderBlock <- get return Spender {..} instance Serialize OutputKey where put OutputKey {..} = do putWord8 0x02 put (outPointHash outPoint) putWord8 0x01 put (outPointIndex outPoint) get = do guard . (== 0x02) =<< getWord8 outPointHash <- get guard . (== 0x01) =<< getWord8 outPointIndex <- get let outPoint = OutPoint {..} return OutputKey {..} instance Serialize PrevOut where put PrevOut {..} = do put prevOutValue put prevOutBlock put (B.length prevOutScript) putByteString prevOutScript get = do prevOutValue <- get prevOutBlock <- get prevOutScript <- getByteString =<< get return PrevOut {..} instance Serialize Output where put Output {..} = do putWord8 0x01 put outputValue put outBlock put outScript put outSpender get = do guard . (== 0x01) =<< getWord8 outputValue <- get outBlock <- get outScript <- get outSpender <- get return Output {..} instance Serialize BlockRef where put BlockRef {..} = do put blockRefHash put blockRefHeight put blockRefPos get = do blockRefHash <- get blockRefHeight <- get blockRefPos <- get return BlockRef {..} instance Serialize TxRecord where put TxRecord {..} = do putWord8 0x00 put txValueBlock put txValue put txValuePrevOuts get = do guard . (== 0x00) =<< getWord8 txValueBlock <- get txValue <- get txValuePrevOuts <- get return TxRecord {..} instance Serialize BestBlockKey where put BestBlockKey = put (B.replicate 32 0x00) get = do guard . (== B.replicate 32 0x00) =<< getBytes 32 return BestBlockKey instance Serialize BlockValue where put BlockValue {..} = do put blockValueHeight put blockValueWork put blockValueHeader put blockValueSize put blockValueTxs get = do blockValueHeight <- get blockValueWork <- get blockValueHeader <- get blockValueSize <- get blockValueTxs <- get return BlockValue {..} -- | Byte identifying network for an address. netByte :: Network -> Word8 netByte net | net == btc = 0x00 | net == btcTest = 0x01 | net == btcRegTest = 0x02 | net == bch = 0x04 | net == bchTest = 0x05 | net == bchRegTest = 0x06 | otherwise = 0xff -- | Network from its corresponding byte. byteNet :: Word8 -> Maybe Network byteNet 0x00 = Just btc byteNet 0x01 = Just btcTest byteNet 0x02 = Just btcRegTest byteNet 0x04 = Just bch byteNet 0x05 = Just bchTest byteNet 0x06 = Just bchRegTest byteNet _ = Nothing -- | Deserializer for network byte. getByteNet :: Get Network getByteNet = byteNet <$> getWord8 >>= \case Nothing -> fail "Could not decode network byte" Just net -> return net instance Serialize StoreAddress where put (StoreAddress addr) = case addr of PubKeyAddress h net -> do putWord8 0x01 putWord8 (netByte net) put h ScriptAddress h net -> do putWord8 0x02 putWord8 (netByte net) put h WitnessPubKeyAddress h net -> do putWord8 0x03 putWord8 (netByte net) put h WitnessScriptAddress h net -> do putWord8 0x04 putWord8 (netByte net) put h get = fmap StoreAddress $ pk <|> sa <|> wa <|> ws where pk = do guard . (== 0x01) =<< getWord8 net <- getByteNet h <- get return (PubKeyAddress h net) sa = do guard . (== 0x02) =<< getWord8 net <- getByteNet h <- get return (ScriptAddress h net) wa = do guard . (== 0x03) =<< getWord8 net <- getByteNet h <- get return (WitnessPubKeyAddress h net) ws = do guard . (== 0x04) =<< getWord8 net <- getByteNet h <- get return (WitnessScriptAddress h net) -- | JSON serialization for 'BlockValue'. blockValuePairs :: A.KeyValue kv => BlockValue -> [kv] blockValuePairs BlockValue {..} = [ "hash" .= headerHash blockValueHeader , "height" .= blockValueHeight , "previous" .= prevBlock blockValueHeader , "time" .= blockTimestamp blockValueHeader , "version" .= blockVersion blockValueHeader , "bits" .= blockBits blockValueHeader , "nonce" .= bhNonce blockValueHeader , "size" .= blockValueSize , "tx" .= blockValueTxs ] instance ToJSON BlockValue where toJSON = object . blockValuePairs toEncoding = pairs . mconcat . blockValuePairs instance ToJSON Spender where toJSON = object . spenderPairs toEncoding = pairs . mconcat . spenderPairs -- | JSON serialization for 'BlockRef'. blockRefPairs :: A.KeyValue kv => BlockRef -> [kv] blockRefPairs BlockRef {..} = [ "hash" .= blockRefHash , "height" .= blockRefHeight , "position" .= blockRefPos ] -- | JSON serialization for 'Spender'. spenderPairs :: A.KeyValue kv => Spender -> [kv] spenderPairs Spender {..} = ["txid" .= spenderHash, "input" .= spenderIndex, "block" .= spenderBlock] -- | JSON serialization for a 'DetailedOutput'. detailedOutputPairs :: A.KeyValue kv => DetailedOutput -> [kv] detailedOutputPairs DetailedOutput {..} = [ "address" .= scriptToAddressBS detOutNetwork detOutScript , "pkscript" .= String (cs (encodeHex detOutScript)) , "value" .= detOutValue , "spent" .= isJust detOutSpender , "spender" .= detOutSpender ] instance ToJSON DetailedOutput where toJSON = object . detailedOutputPairs toEncoding = pairs . mconcat . detailedOutputPairs -- | JSON serialization for 'PeerInformation'. peerInformationPairs :: A.KeyValue kv => PeerInformation -> [kv] peerInformationPairs PeerInformation {..} = [ "useragent" .= String (cs userAgent) , "address" .= String (cs address) , "connected" .= connected , "version" .= version , "services" .= services , "relay" .= relay , "block" .= block , "height" .= height , "noncelocal" .= nonceLocal , "nonceremote" .= nonceRemote ] instance ToJSON PeerInformation where toJSON = object . peerInformationPairs toEncoding = pairs . mconcat . peerInformationPairs -- | JSON serialization for 'DetailedInput'. detailedInputPairs :: A.KeyValue kv => DetailedInput -> [kv] detailedInputPairs DetailedInput {..} = [ "txid" .= outPointHash detInOutPoint , "output" .= outPointIndex detInOutPoint , "coinbase" .= False , "sequence" .= detInSequence , "sigscript" .= String (cs (encodeHex detInSigScript)) , "pkscript" .= String (cs (encodeHex detInPkScript)) , "address" .= scriptToAddressBS detInNetwork detInPkScript , "value" .= detInValue , "block" .= detInBlock ] detailedInputPairs DetailedCoinbase {..} = [ "txid" .= outPointHash detInOutPoint , "output" .= outPointIndex detInOutPoint , "coinbase" .= True , "sequence" .= detInSequence , "sigscript" .= String (cs (encodeHex detInSigScript)) , "pkscript" .= Null , "address" .= Null , "value" .= Null , "block" .= Null ] instance ToJSON DetailedInput where toJSON = object . detailedInputPairs toEncoding = pairs . mconcat . detailedInputPairs -- | JSON serialization for 'DetailedTx'. detailedTxPairs :: A.KeyValue kv => DetailedTx -> [kv] detailedTxPairs DetailedTx {..} = [ "txid" .= txHash detailedTxData , "size" .= B.length (S.encode detailedTxData) , "version" .= txVersion detailedTxData , "locktime" .= txLockTime detailedTxData , "fee" .= detailedTxFee , "inputs" .= detailedTxInputs , "outputs" .= detailedTxOutputs , "hex" .= String (cs (encodeHex (S.encode detailedTxData))) , "block" .= detailedTxBlock ] instance ToJSON DetailedTx where toJSON = object . detailedTxPairs toEncoding = pairs . mconcat . detailedTxPairs instance ToJSON BlockRef where toJSON = object . blockRefPairs toEncoding = pairs . mconcat . blockRefPairs -- | JSON serialization for 'AddrOutput'. addrOutputPairs :: A.KeyValue kv => AddrOutput -> [kv] addrOutputPairs AddrOutput {..} = [ "address" .= addrOutputAddress , "txid" .= outPointHash addrOutPoint , "index" .= outPointIndex addrOutPoint , "block" .= outBlock , "output" .= dout ] where Output {..} = addrOutput AddrOutKey {..} = addrOutputKey dout = DetailedOutput { detOutValue = outputValue , detOutScript = outScript , detOutSpender = outSpender , detOutNetwork = getAddrNet addrOutputAddress } instance ToJSON AddrOutput where toJSON = object . addrOutputPairs toEncoding = pairs . mconcat . addrOutputPairs -- | JSON serialization for 'AddressBalance'. addressBalancePairs :: A.KeyValue kv => AddressBalance -> [kv] addressBalancePairs AddressBalance {..} = [ "address" .= addressBalAddress , "confirmed" .= addressBalConfirmed , "unconfirmed" .= addressBalUnconfirmed , "utxo" .= addressUtxoCount ] instance FromJSON NewTx where parseJSON = withObject "transaction" $ \v -> NewTx <$> v .: "transaction" instance ToJSON AddressBalance where toJSON = object . addressBalancePairs toEncoding = pairs . mconcat . addressBalancePairs instance Serialize HeightKey where put (HeightKey height) = do putWord8 0x03 put (maxBound - height) put height get = do guard . (== 0x03) =<< getWord8 iheight <- get return (HeightKey (maxBound - iheight)) instance Serialize BlockKey where put (BlockKey hash) = do putWord8 0x01 put hash get = do guard . (== 0x01) =<< getWord8 BlockKey <$> get instance Serialize TxKey where put (TxKey hash) = do putWord8 0x02 put hash putWord8 0x00 get = do guard . (== 0x02) =<< getWord8 hash <- get guard . (== 0x00) =<< getWord8 return (TxKey hash) -- | Configuration for a 'Store'. data StoreConfig = StoreConfig { storeConfMaxPeers :: !Int -- ^ max peers to connect to , storeConfInitPeers :: ![HostPort] -- ^ static set of peers to connect to , storeConfDiscover :: !Bool -- ^ discover new peers? , storeConfDB :: !DB -- ^ RocksDB database handler , storeConfNetwork :: !Network -- ^ network constants } -- | Store mailboxes. data Store = Store { storeManager :: !Manager -- ^ peer manager mailbox , storeChain :: !Chain -- ^ chain header process mailbox , storeBlock :: !BlockStore -- ^ block storage mailbox , storePublisher :: !(Publisher StoreEvent) -- ^ store event publisher mailbox }