packages feed

haskoin-node 2.5.0 → 2.5.1

raw patch · 7 files changed

+77/−19 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Haskoin.Node.PeerMgr: instance GHC.Classes.Eq Haskoin.Node.PeerMgr.Connected
+ Haskoin.Node.PeerMgr: instance GHC.Internal.Show.Show Haskoin.Node.PeerMgr.Connected

Files

CHANGELOG.md view
@@ -6,6 +6,22 @@  ## [Unreleased] +## [2.5.1] - 2026-11-11++### Changed++- Log levels say who has the problem. An error is now something wrong with+  this node, and nothing a peer does is one:+  - a peer that closes its connection is logged for debugging, and one+    that sends something invalid as a warning; both were errors;+  - headers that do not connect are logged once, as the warning that+    disconnects the peer, and no longer as an error before it;+  - connecting to a peer, its connection and its end are logged for+    debugging, where they were information;+  - the number of connected peers is logged for debugging at every change,+    as a warning when the node loses its last peer, and as information+    when it has peers again.+ ## [2.5.0] - 2026-11-10  This release fixes the findings of a security review. Several fixes change
README.md view
@@ -169,7 +169,7 @@  ```yaml extra-deps:-  - haskoin-node-2.5.0+  - haskoin-node-2.5.1   - haskoin-core-1.6.1   - secp256k1-haskell-2.0.1   - base16-2.0@@ -198,7 +198,7 @@    stack run    ``` -   Within a few seconds it logs connections and starts printing header+   Within a few seconds it has peers and starts printing header    heights. A full header sync of Bitcoin usually takes minutes and on the    order of a hundred megabytes of disk; it resumes where it stopped if you    interrupt it.@@ -494,6 +494,15 @@ `withLogger level` logs to standard error, one line per message, such as `[Info#Chain] Best block header at height 870000`. Build a `Logger` with your own `push` function to log elsewhere.++| Level | What it is for |+| --- | --- |+| Debug | Everything: each peer that connects or leaves, each message |+| Info | Milestones: the best block, headers in sync, peers again after none |+| Warn | Something unusual that the node dealt with: a misbehaving peer, no peers left |+| Error | Something wrong with this node. Nothing that a peer does is an error |++An alert on errors is then an alert about the node, not about the network.  ## Configuration 
haskoin-node.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           haskoin-node-version:        2.5.0+version:        2.5.1 synopsis:       P2P library for Bitcoin and Bitcoin Cash description:    Connect to Bitcoin (BTC) and Bitcoin Cash (BCH) peers, keep them healthy, and                 sync block headers into a RocksDB database. Blocks and transactions can be
src/Haskoin/Node/Chain.hs view
@@ -470,7 +470,8 @@     chainEvent ch (ChainBestBlock after)   case result of     Left e -> do-      logError ch.config.logger ("connectBlocks: " <> cs e)+      -- the caller disconnects the peer and says why, with a warning+      logDebug ch.config.logger ("connectBlocks: " <> cs e)       return (Left e)     Right [] -> return (Left "no headers")     Right (lst : _) -> do
src/Haskoin/Node/Logger.hs view
@@ -32,13 +32,17 @@  -- | Severity of a log message, from least to most severe. data LogLevel-  = -- | detailed tracing, useful when debugging+  = -- | everything, for debugging: each peer that connects or leaves, each+    -- message     LevelDebug-  | -- | normal operation, such as peers connecting and blocks arriving+  | -- | what an operator wants in a quiet log: the best block, headers in+    -- sync, peers again after having none     LevelInfo-  | -- | unexpected events that the node recovers from+  | -- | something unusual that the node dealt with: a peer that sent+    -- something invalid and was disconnected, no peers left     LevelWarn-  | -- | failures+  | -- | something wrong with this node, that someone should look at.+    -- Nothing that a peer does is an error.     LevelError   deriving (Eq, Ord, Show) 
src/Haskoin/Node/Peer.hs view
@@ -279,7 +279,7 @@   yield m   return True dispatchMessage PeerConfig {label, logger} KillPeer = do-  liftIO $ logInfo logger (label <> " disconnecting")+  liftIO $ logDebug logger (label <> " disconnecting")   return False  -- | Internal conduit to parse messages coming from peer. The header is checked@@ -333,7 +333,13 @@       _ -> return ()     bad :: PeerException -> ConduitT i o IO a     bad e = liftIO $ do-      logError logger (label <> ": " <> cs (show e))+      -- a peer that hangs up is the ordinary end of a connection; one that+      -- sends what it should not is worth a look, and is not an error of+      -- this node either+      let level = case e of+            ConnectionClosed -> logDebug+            _ -> logWarn+      level logger (label <> ": " <> cs (show e))       throwIO e     -- The reason is on the first line of the parser's error text. The lines     -- after it are call stack noise.
src/Haskoin/Node/PeerMgr.hs view
@@ -171,9 +171,18 @@     -- | addresses to connect to     addresses :: !(TVar (AddressBook SockAddr)),     -- | open connections, fastest peers first-    peers :: !(TVar [OnlinePeer])+    peers :: !(TVar [OnlinePeer]),+    -- | how well connected the log last said the node was ('Connected')+    told :: !(TVar Connected)   } +-- | How well connected the node is, as its log says at the level of+-- information. Peers come and go all the time, and a line for each change+-- buries the ones that matter: that the node lost its last peer, and that+-- it has peers again.+data Connected = NoPeers | SomePeers | AllPeers+  deriving (Eq, Show)+ -- | Messages handled by the peer manager process. data PeerMgrMessage   = -- | connect to a peer address@@ -275,6 +284,7 @@     best <- newTVarIO 0     addresses <- newTVarIO emptyBook     peers <- newTVarIO []+    told <- newTVarIO NoPeers     let mgr =           PeerMgr             { config = cfg,@@ -282,7 +292,8 @@               mailbox = inboxToMailbox inbox,               best = best,               addresses = addresses,-              peers = peers+              peers = peers,+              told = told             }     withAsync (peerManager mgr inbox) $ \a ->       withConnectLoop mgr (link a >> action mgr)@@ -346,7 +357,7 @@       ReplyVerAck -> MVerAck `sendMessage` p       Announce -> do         atomically $ modifyTVar' mgr.addresses (markConnected o.address)-        logInfo mgr.config.logger ("Connected to peer " <> p.label)+        logDebug mgr.config.logger ("Connected to peer " <> p.label)         publish (PeerConnected p) mgr.config.pub         logConnectedPeers mgr       SendPing n -> MPing (Ping n) `sendMessage` p@@ -405,7 +416,7 @@   case gone of     Nothing -> logError mgr.config.logger "Unknown peer process stopped"     Just (o, outcome) -> do-      logInfo mgr.config.logger $+      logDebug mgr.config.logger $         "Connection to peer "           <> o.mailbox.label           <> " ended: "@@ -437,13 +448,24 @@       fromException x         <|> (fromException x >>= \(ExceptionInLinkedThread _ inner) -> fromException inner) --- | Log how many peers are online out of the maximum.+-- | Log how many peers are online out of the maximum: for debugging at+-- every change, and at a level that an operator reads when the node loses+-- its last peer or has one again ('Connected'). logConnectedPeers :: PeerMgr -> IO () logConnectedPeers mgr = do   l <- length <$> getPeers mgr-  logInfo-    mgr.config.logger-    ("Peers connected: " <> cs (show l) <> "/" <> cs (show mgr.config.maxPeers))+  let now+        | l == 0 = NoPeers+        | l >= mgr.config.maxPeers = AllPeers+        | otherwise = SomePeers+      count = "Peers connected: " <> cs (show l) <> "/" <> cs (show mgr.config.maxPeers)+  before <- atomically (swapTVar mgr.told now)+  if+    | now == before -> logDebug mgr.config.logger count+    | now == NoPeers -> logWarn mgr.config.logger count+    | before == NoPeers -> logInfo mgr.config.logger count+    -- a node that rotates a peer is as well connected as it was+    | otherwise -> logDebug mgr.config.logger count  -- | Next address to connect to. The configured peers and, if discovery is on, -- the seeds are loaded when there is nothing to try and they were not loaded@@ -499,7 +521,7 @@     | source /= Just Configured && netGroup sa `elem` usedGroups ops ->         giveBack "network group in use"     | otherwise -> do-        logInfo mgr.config.logger ("Connecting to peer " <> cs (show sa))+        logDebug mgr.config.logger ("Connecting to peer " <> cs (show sa))         nonce <- randomIO         height <- readTVarIO mgr.best         now <- getCurrentTime