# 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).
## [2.4.0] - 2026-11-09
### Added
- `chainBlocksAnnounced`: the node passes blocks announced in inventory
messages to the chain, which asks the peer for headers if it does not know
them.
- Pure state machines, each with property tests: the life of a connection
(`Haskoin.Node.PeerMgr.Lifecycle`) and its version handshake
(`Haskoin.Node.PeerMgr.Handshake`), the address book
(`Haskoin.Node.PeerMgr.Addresses`), reserving a peer
(`Haskoin.Node.Peer.Lock`), choosing which peer to ask for headers
(`Haskoin.Node.Chain.Sync`), and receiving requested blocks and transactions
(`Haskoin.Node.Peer.Request`).
- `isSynced` says whether the header chain counts as synced, as a pure
function.
- `newOwner`, `reservePeer`, `releasePeer` and `peerHolder` reserve a peer for
an owner, so that only that owner can release it.
- `PeerException` says why a peer connection ended, instead of an `error`
message.
- `Haskoin.Node.Address` and `Haskoin.Node.Chain.Database` hold code split
out of the peer manager, the node and the chain, which still re-export it.
Every module is exposed, and `PeerMessage` is exported.
- Unit tests of the chain, peer and peer manager processes against in-memory
mock peers.
- A regtest suite that runs the node against Bitcoin Core 32.1 and Bitcoin Cash
Node 29.2.0 in Podman, from official binaries with checked hashes, and a CI
job that runs it.
- `cabal.project`, so the package builds with Cabal too.
- `chainRunHeaders` runs header functions of haskoin-core, such as
`getAncestor` or `blockLocator`, against the headers of a chain, and
`runHeaders` does the same for a `ChainDB`.
### Changed
- If no headers are requested or received for `timeout` seconds, every
connected peer is asked for headers again. This bounds how long any stall of
the header chain can last.
- The chain checks for timeouts at intervals of at most a quarter of
`timeout`, between 0.1 and 20 seconds, instead of every 2 to 20 seconds.
- Breaking: `OnlinePeer` holds the state of the connection in a `state` field
instead of the `verack`, `online`, `version`, `ping`, `pings`, `connected`
and `tickled` fields. These are still available as computed fields, so
`o.version` and `o.online` keep working, but code that builds or pattern
matches `OnlinePeer` records must change.
- Breaking: the busy flag of a `Peer` is a lock held by an owner, so `wrapPeer`
takes a `TVar (Lock Owner)`. `setBusy` and `setFree` are deprecated: they act
for a single owner shared by all their callers, and `setFree` no longer
releases a peer that another owner reserved. The chain reserves peers as its
own owner.
- A peer told to disconnect is no longer returned by `getPeers`, and its
messages are no longer routed, even before its connection closes.
- A peer that does not complete the handshake within `timeout` seconds is
disconnected, even if it keeps sending other messages.
- An address whose connection fails waits before it is tried again, twice as
long after each failure up to ten minutes, and a relayed address whose peer
broke the protocol waits a day. Before, failing addresses were retried
within seconds forever. The configured peers and seeds are resolved again at
most every five minutes.
- Breaking: `ChainT` and `runChainT` are removed, along with the
`BlockHeaders` instances for `ReaderT Chain m` and `ReaderT ChainConfig m`.
Use `chainRunHeaders` instead. The package no longer uses monad
transformers outside `Haskoin.Node.Chain.Database`, where a `ReaderT` carries
the database for haskoin-core's header functions; `withNode` nests its
resources directly instead of through `ContT`.
- Breaking: every function runs in `IO`. The `MonadIO` and `MonadUnliftIO`
constraints are gone, so code in another monad must call the node with
`liftIO`. The library depends on async and stm instead of unliftio.
- The package uses the GHC2024 language edition, strict warnings and upper
bounds on its dependencies. `Setup.hs`, `hie.yaml` and `.hindent.yaml` were
removed.
- `stack.yaml` builds against secp256k1-haskell 2.0.1. The README's Stack
example names the same version.
### Fixed
- Peers were ranked by the newest ping and the ten fastest ever measured,
instead of the last eleven: slow recent pings were dropped. A peer that
became slow kept ranking as fast.
- The header chain stopped following the network when a peer announced new
blocks with an inventory message instead of headers, which peers do when
many blocks are found at once. The chain stayed at the old height until the
peer reconnected.
- A peer that announced headers that did not connect to the chain was
disconnected. It is now asked for headers.
- A syncing peer that timed out kept the chain waiting until its disconnection
arrived. The chain now moves on at once.
- A full batch of headers from a peer that was not being synced against was
not followed up.
- `getData`, `getBlocks` and `getTxs` failed when the peer sent an unrelated
message, or a notfound message for items that were not requested, before all
the requested items arrived.
## [2.3.1] - 2026-11-07
### Changed
- Documentation: the README explains the purpose and design of the node, with
installation steps, configuration, security notes and a tutorial, and the
Haddock of every module and function says why it works the way it does.
- Build against nqe 0.6.9, rocksdb-haskell-jprupp 2.6.1, rocksdb-query 0.6.1
and Stackage nightly-2026-11-07.
## [2.3.0] - 2026-11-06
### Changed
- This package requires haskoin-core 1.6.0, the release with the fixes of
the November 2026 security review.
- Breaking: the `version`, `timestamp` and `startHeight` fields of the
`Version` of an `OnlinePeer` are signed numbers, as haskoin-core 1.6.0
reads them. `buildVersion` takes the same arguments as before.
- The headers are not reported as synced before the best header has the
`minimumChainWork` of the network. This holds for the `ChainSynced` event
and for `chainIsSynced`. A recent header on a chain with less work used to
be enough.
- Once the best header has the `minimumChainWork` of the network, a peer
whose headers fork from far below the tip, on a chain with less work, is
disconnected. haskoin-core 1.6.0 refuses such headers.
- A peer that sends a header the node already has at or below a checkpoint
is no longer disconnected. haskoin-core 1.6.0 returns the stored header.
- Transactions from Bitcoin Cash peers are read in the format without
witness only, as haskoin-core 1.6.0 reads them on a network without SegWit.
- The log line for an invalid payload gives the reason the parser refused it.
- The payload size limit is the `maxPayloadSize` of the command on the
network instead of the network's `maxMessageSize`. On Bitcoin Cash that is
2 MiB for every message but `block`, `cmpctblock` and `blocktxn`, which may
be twice the block size.
### Fixed
- Peers that send more than 50000 entries in an `inv` or `getdata` message,
or a block locator of more than 101 hashes, are disconnected. haskoin-core
1.6.0 refuses such a message as soon as it reads the count.
## [2.2.0] - 2026-11-04
### Changed
- Breaking: `Peer` has a `pending` field, `wrapPeer` takes its `TVar Int`,
and `peer` takes the `Peer` that `wrapPeer` built instead of the busy
`TVar`.
- Messages from a peer apply backpressure. A peer may have at most
`maxPendingMessages` messages waiting for the node; after that the node
stops reading its socket until it catches up. Address, header, version,
verack, ping and pong messages are handled to completion before the next
one is taken. A fast peer could fill the node's unbounded queues before.
- Outgoing messages are logged by command at debug level, not in full.
### Fixed
- Bitcoin Cash headers sync again. This package now requires haskoin-core
1.5.1, which evaluates the ASERT difficulty rule at the parent block. With
haskoin-core 1.5.0 the node stopped at mainnet block 975899 with
`Incorrect difficulty bits`, because that release checked the bits of each
header with a formula that used the header's own timestamp.
- Network magic and payload size are checked right after the message header,
before the payload is read. The size limit is the network's
`maxMessageSize` instead of a fixed 32 MiB. An oversized message used to be
logged and its payload then parsed as the next header.
- Peers that send more than 2000 headers or more than 1000 addresses in one
message are disconnected before the message is decoded.
- Address and header messages from a peer are ignored until it completes the
handshake.
- A second version or verack from a peer disconnects it. Each one used to
announce the peer as connected again.
- Relayed addresses that are not public unicast addresses (loopback,
private, link-local, multicast, documentation and similar ranges, or port
0) are not added to the address pool. `isRoutable` is exported.
- A peer that does not answer a ping within the timeout is disconnected, even
if it keeps sending other messages.
- Only headers from the syncing peer reset its sync timeout. Another peer's
headers could keep a silent syncing peer from ever timing out.
- The peer manager rechecks `maxPeers` when it handles a queued connection,
so the connect loop can no longer go over the limit while the manager is
busy.
- Purging an old chain database with a column family deleted the same keys
from the default column family too. The purge now deletes keys as it walks
them instead of building a list of all of them in memory.
## [2.1.2] - 2026-11-04
### Fixed
- An empty headers message from the syncing peer ends the header sync. The
node used to take it as a reason to ask the same peer again, and kept
sending getheaders in a tight loop, thousands per second, whenever its
best header was the peer's tip after a full batch of 2000.
## [2.1.1] - 2026-11-04
### Changed
- The loop that parses messages from a peer uses `loopForever` too, like
every other long-running loop.
- Tests use IPv4 only: the mock peer is at `127.0.0.1` instead of `[::1]`,
and the address property test generates IPv4 addresses. On a host
without IPv6 the mock peer could not be resolved and the peer and chain
tests hung.
## [2.1.0] - 2026-11-03
- Update to haskoin-core-1.5.0.
## [2.0.0] - 2026-10-31
### Changed
- Replace monad-logger with fast-logger. Configuration records take a `Logger`,
and functions need only `MonadIO` or `MonadUnliftIO`.
- Require haskoin-core 1.5.0, whose `buildMerkleRoot` returns
`Either String MerkleRoot`.
## [1.4.4] - 2026-08-31
### Added
- Log `connectBlocks` error message.
### Changed
- Peer connection logged incorrectly as warn is now logged as info.
### Fixed
- Correct documentation for `importHeaders` function.
## [1.4.3] - 2026-08-13
### Fixed
- Fix regression with peer disconnection.
## [1.4.2] - 2026-08-13
### Fixed
- Fix regression with peer selection.
## [1.4.1] - 2026-08-13
### Removed
- Remove PeerException type which is no longer used.
## [1.4.0] - 2026-08-13
### Changed
- Add monad transformers and monad-logger again because logging is too slow.
## [1.3.1] - 2026-08-12
### Added
- Add monad transformers to support haskoin-store.
## [1.3.0] - 2026-08-12
### Changed
- Simplify modules to make compatible with upstream changes.
## [1.2.0] - 2026-08-12
### Changed
- Upstream changes on multiple libraries.
## [1.1.4] - 2025-05-08
### Changed
- Move to personal repository.
## [1.1.3] - 2024-05-23
### Fixed
- Tickle a peer whenever a message is received.
## [1.1.2] - 2024-05-23
### Fixed
- Correct log messages from peer manager.
## [1.1.1] - 2024-05-23
### Fixed
- Peers timeout more aggressively.
## [1.1.0] - 2024-03-14
### Changed
- Update LTS Haskell & upstream dependencies.
## [1.0.1] - 2023-08-03
### Changed
- Add compatibility with latest LTS Haskell.
## [1.0.0] - 2023-07-28
### Changed
- Make compatible with latest haskoin-core.
- Use DuplicateRecordFields and OverloadedRecordDot.
- Simplify pub/sub queues.
- Multiple refactoring passes.
## [0.18.1] - 2022-07-27
### Fixed
- Set default port for peers where it is unset.
## [0.18.0] - 2022-07-27
### Added
- Support setting up and connecting to IPv6 peers.
## [0.17.14] - 2021-08-14
### Fixed
- Reduce verbosity on incoming header decode test.
- Show appropriate error message upon receiving empty headers.
## [0.17.13] - 2021-08-14
### Added
- Display more details about invalid incoming headers.
## [0.17.12] - 2021-05-17
### Fixed
- Do not connect to more than the maximum number of peers.
## [0.17.11] - 2021-05-17
### Added
- Display message command that disconnects a peer.
## [0.17.10] - 2021-05-17
### Fixed
- Correct disconnect timeout algorithm bug.
## [0.17.9] - 2021-05-17
### Fixed
- Add randomised timeouts to avoid disconnecting all peers.
## [0.17.2] - 2021-03-09
### Fixed
- Do not start chain actor until database initialized.
## [0.17.1] - 2021-01-08
### Changed
- Depend on haskoin-core-0.17.3.
## [0.17.0] - 2020-10-21
### Added
- Support for Bitcoin Cash November 2020 hard fork.
- BlockHeaders instance for ReaderT Chain.
## [0.16.0] - 2020-07-23
### Changed
- Add support for column families.
## [0.15.0] - 2020-07-20
### Changed
- Use new Haskell bindings for RocksDB.
## [0.14.1] - 2020-06-19
### Fixed
- Correct flawed peer locking logic in Chain actor.
## [0.14.0] - 2020-06-18
### Changed
- Massively refactor everything in a non-backwards-compatible way.
- Use MIT license.
- Bump haskoin-core.
- Bump secp256k1-haskell.
### Fixed
- Fix getting stuck on a single peer.
## [0.13.0] - 2020-05-08
### Changed
- Depend on Haskoin Store 0.13.3.
- Better code organisation.
## [0.12.0] - 2020-05-06
### Changed
- Add a test suite that simulates network instead of connecting to real one.
## [0.11.3] - 2020-05-03
### Changed
- Revert including multiline decoding error text in logs.
## [0.11.2] - 2020-05-03
### Changed
- Include header decoding error text in logs.
## [0.11.1] - 2020-05-03
### Changed
- Improve logging.
## [0.11.0] - 2020-05-03
### Changed
- Set peer too old time
## [0.10.1] - 2020-05-03
### Changed
- Disconnect old peers after 48 hours instead of 30 minutes.
## [0.10.0] - 2020-05-03
### Changed
- Move modules out of Network.Haskoin namespace.
- Add better and more logging.
- Change Manager module name and related values to PeerManager.
## [0.9.21] - 2020-04-07
### Removed
- Remove unnecessary logging.
## [0.9.20] - 2020-04-07
### Changed
- Better log messages.
- Less verbose debug logging.
## [0.9.19] - 2020-04-07
### Changed
- Better log messages.
## [0.9.18] - 2020-04-07
### Added
- More aggressive peer discovery.
## [0.9.17] - 2020-04-07
### Added
- Peers are disconnected automatically after awhile.
## [0.9.16] - 2020-02-08
### Added
- Lower bound versions for some dependencies.
## [0.9.15] - 2020-01-15
### Changed
- Update to support new `NetworkAddress` data structure from `haskoin-core`.
## [0.9.14] - 2019-12-10
### Removed
- No longer support storing peers in db as performance tradeoff doesn't justify it.
## [0.9.13] - 2019-10-08
### Changed
- Really store peers in db.
## [0.9.12] - 2019-10-08
### Changed
- Demote some logging to debug level.
## [0.9.11] - 2019-10-02
### Added
- Add `-O2` optimisations to GHC.
## [0.9.10] - 2019-04-19
### Added
- Increase debugging information where application freezes.
## [0.9.9] - 2019-04-12
### Added
- Increase debugging information.
## [0.9.8] - 2019-04-12
### Changed
- Increase version of haskoin-core to 0.9.0.
- Fix some tests.
## [0.9.7] - 2019-04-12
### Added
- More debugging.
### Changed
- Be defensive against duplicate peers.
- Increase interval between housekeeping pings.
- Replace peers in database atomically.
## [0.9.6] - 2019-04-01
### Changed
- Randomize known peers instead of keeping scores.
- Simplify peer management code to avoid freezes.
- Merge logic for chain and manager.
## [0.9.5] - 2018-11-14
### Changed
- Do not record new peers in database when peer discovery is disabled.
## [0.9.4] - 2018-11-01
### Changed
- Don't spam best block events.
## [0.9.3] - 2018-10-22
### Changed
- Correct display of milliseconds in log.
- Correct bug when receiving headers from unknown peer.
- Simplify chain syncing code.
## [0.9.2] - 2018-10-18
### Changed
- Peer dies immediately when receiving a bad message.
## [0.9.1] - 2018-10-18
### Changed
- Keep track of last synced header from a peer to avoid endless loops on large reorgs.
## [0.9.0] - 2018-10-17
### Changed
- Use an STM listener instead of a publisher for the node API.
## [0.8.2] - 2018-10-17
### Added
- Expose `ChainMessage` and `ManagerMessage` types from `Haskoin.Node` module.
## [0.8.1] - 2018-10-11
### Changed
- Corrected documentation for `killPeer` function.
- Leave time out of logic code.
## [0.8.0] - 2018-10-09
### Changed
- Peers are now killed directly instead of through peer manager.
### Removed
- Chain no longer needs peer manager.
## [0.7.2] - 2018-10-09
### Added
- Compatibility with base 4.12.
### Changed
- Update base to 4.9.
## [0.7.1] - 2018-10-09
### Added
- Allow to easily obtain a peer's publisher.
## [0.7.0] - 2018-10-09
### Added
- Versioning for chain and peer database.
- Automatic purging of chain and peer database when version changes.
- Add extra timers.
- Add publishers to every peer.
### Changed
- Full reimplementation of node API.
- Simplify peer selection and management.
- Merge manager and peer events.
- Rename configuration variables for node.
- Separate logic from actors for peer manager and chain.
### Removed
- Remove irrelevant fields from peer information.
- Remove unreliable peer block head tracking.
- Remove dependency on deprecated binary conduits.
- Remove Bloom filter support from manager.
- Remove unreliable peer request tracking code.
- Remove separate manager events.
## [0.6.1] - 2018-09-14
### Changed
- Fix bug where peer height did not update in certain cases.
## [0.6.0] - 2018-09-14
### Added
- Documentation everywhere.
### Changed
- Make compatible with NQE 0.5.
- Use supervisor only in peer manager.
- API quality of life changes.
- Exposed module is now only `Haskoin.Node`.
### Removed
- No more direct access to internals.
## [0.5.2] - 2018-09-10
### Changed
- Improve dependency definitions.
## [0.5.1] - 2018-09-10
### Changed
- Dependency `sec256k1` changes to `secp256k1-haskell`.
## [0.5.0] - 2018-09-09
### Added
- New `CHANGELOG.md` file.
- Use `nqe` for concurrency.
- Peer discovery.
- RocksDB peer and block header storage.
- Support for Merkle blocks.
### Changed
- Split out of former `haskoin` repository.
- Use hpack and `package.yaml`.
- Old `haskoin-node` package now renamed to `old-haskoin-node` and deprecated.
### Removed
- Removed Old Haskoin Node package completely.
- Removed Stylish Haskell configuration file.
- Remvoed `haskoin-core` and `haskoin-wallet` packages from this repository.