I successfully synced libbitcoin with enabled address indexing, can successfully connect Sparrowwallet to the Electrum RPC, but am unable to broadcast a transaction.
Summary
blockchain.estimatefee consistently returns -1 (unknown) for all block targets on a fully synced libbitcoin-server v4.0.0 mainnet node with an active mempool. This is the only fee estimation method available via the Electrum protocol interface. Wallet clients like Sparrow Wallet interpret -1 as a failure, exhaust retries, close the connection, and then cannot broadcast transactions or subscribe to wallet history.
Environment
- libbitcoin-server: v4.0.0 (built from master, commit
6c402cc)
- Network: Bitcoin mainnet, fully synced at block ~955,969
- Container: LXC, 6 cores, 32GB RAM, Ubuntu 24.04
- Mempool: Active (P2P connections established, receiving transactions from peers)
- Electrum protocol version: 1.4.2 (negotiated with Sparrow Wallet)
Reproduction
- Start
bs with a fully synced mainnet store
- Connect via Electrum protocol (clear port 50001 or TLS port 50002)
- Send
blockchain.estimatefee for various block targets:
import socket, json, ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(socket.socket(), server_hostname="127.0.0.1")
s.settimeout(10)
s.connect(("127.0.0.1", 50002))
for blocks in [1, 2, 3, 4, 5, 6, 10, 25]:
s.sendall((json.dumps({"id":1,"method":"blockchain.estimatefee","params":[blocks]}) + "\n").encode())
data = b""
while b"\n" not in data:
data += s.recv(65536)
resp = json.loads(data.decode())
print(f"estimatefee({blocks}): {resp['result']}")
Actual output
estimatefee(1): -1
estimatefee(2): -1
estimatefee(3): -1
estimatefee(4): -1
estimatefee(5): -1
estimatefee(6): -1
estimatefee(10): -1
estimatefee(25): -1
Expected output
A non-negative fee rate (BTC/kB or sat/vB depending on protocol version), similar to what bitcoind estimatesmartfee returns. For example:
estimatefee(1): 0.00012345
estimatefee(6): 0.00009876
Or, per the Electrum protocol spec, a float fee rate per byte. Returning -1 should only occur when the node truly has no fee data (e.g., freshly started with no blocks processed).
Related: mempool.get_fee_histogram returns empty
{"jsonrpc":"1.0","id":1,"error":null,"result":[]}
The fee histogram is also empty, even with an active mempool. This may be related — if the fee estimator doesn't have mempool data, it can't compute estimates.
Impact on Electrum wallet clients
Sparrow Wallet (v2.x)
Sparrow sends blockchain.estimatefee for block targets 1, 2, 3, 4, 5, 10, 25 on connection. When all return -1:
- Sparrow logs:
WARN Failed to retrieve fee rate for target blocks: N (Retries exhausted)
- After exhausting retries for all targets, Sparrow closes the SSL socket
- Subsequent operations fail with
java.net.SocketException: Socket is closed
- Wallet history subscription fails:
Failed to subscribe to path: m/0/0
- Transaction broadcast fails:
Error broadcasting transaction: Retries exhausted
This effectively makes libbitcoin-server unusable as an Electrum backend for Sparrow Wallet, even though:
server.version works
blockchain.headers.subscribe works
blockchain.scripthash.get_history works (with address_buckets=100000000)
blockchain.transaction.broadcast works at the protocol level (tested with dummy txs, returns proper validation errors)
blockchain.relayfee works (returns 0.0)
Comparison with bitcoind + Fulcrum
A bitcoind + Fulcrum stack does not exhibit this issue because Fulcrum proxies estimatefee to bitcoind's estimatesmartfee, which uses historical block fee data (last 1008 blocks) and returns meaningful estimates even with an empty mempool.
Root cause analysis
The unit test test/protocols/electrum/electrum_fees.cpp confirms that -1 is the expected return value when the fee estimator has insufficient data:
BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__zero_basic__negative_one)
{
// Trigger node chaser event to initialize fee estimator.
notify(node::chase::block, { 9_u32 });
const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[0,"basic"]})" "\n");
// None of the first 10 blocks have fees, so no estimate is obtained.
BOOST_REQUIRE_EQUAL(response.at("result").as_int64(), -1);
}
This suggests the fee estimator requires block fee data to be populated via the node chaser event system. On a fully synced mainnet node, this data should be available from historical blocks, but it appears the estimator is not being properly initialized or is unable to access the historical fee data.
Possible causes
- Fee estimator not initialized: The node chaser may not be triggering fee estimator updates on a synced node (no new blocks to chase)
- Historical fee data not populated: The estimator may only use recent block data, and if the node has been running for less than N blocks, it has insufficient data
- Mempool fee tracking not working: The
mempool.get_fee_histogram returning [] suggests mempool fee tracking may also be broken, which would affect fee estimation
Configuration
[database]
path = /home/libbitcoin/store/bitcoin
address_buckets = 100000000
[node]
threads = 8
delay_inbound = False
maximum_concurrency = 2000
[electrum]
bind = 0.0.0.0:50001
connections = 100
cert_path = /home/libbitcoin/certs/electrum.pem
key_path = /home/libbitcoin/certs/electrum.pem
safe = 0.0.0.0:50002
Server logs
After restart, estimatefee requests are received but responses are only ~37 bytes (consistent with {"result":-1}):
2026-06-29T19:09:57Z.0 Rpc request : (97) bytes [192.168.8.173:49102] blockchain.estimatefee(...).
2026-06-29T19:09:57Z.0 Rpc response: (37) bytes [192.168.8.173:49102]
2026-06-29T19:09:57Z.0 Rpc request : (97) bytes [192.168.8.173:49102] blockchain.estimatefee(...).
2026-06-29T19:09:57Z.0 Rpc response: (37) bytes [192.168.8.173:49102]
...
2026-06-29T19:09:58Z.0 Rpc request : (97) bytes [192.168.8.173:49102] mempool.get_fee_histogram(...).
2026-06-29T19:09:58Z.0 Rpc response: (38) bytes [192.168.8.173:49102]
Suggested fix
- Ensure the fee estimator is initialized with historical block fee data on startup (not just from new blocks via the node chaser)
- Or, implement a fallback that uses mempool fee data when historical block fee data is unavailable
- Or, document this as a known limitation and return a proper error (per Electrum protocol) instead of
-1, so wallet clients can gracefully fall back to external fee sources
Related issues
- #778 — Verbose
blockchain.transaction.get response disagrees with peer Electrum implementations (another Sparrow compatibility issue)
I successfully synced libbitcoin with enabled address indexing, can successfully connect Sparrowwallet to the Electrum RPC, but am unable to broadcast a transaction.
Summary
blockchain.estimatefeeconsistently returns-1(unknown) for all block targets on a fully synced libbitcoin-server v4.0.0 mainnet node with an active mempool. This is the only fee estimation method available via the Electrum protocol interface. Wallet clients like Sparrow Wallet interpret-1as a failure, exhaust retries, close the connection, and then cannot broadcast transactions or subscribe to wallet history.Environment
6c402cc)Reproduction
bswith a fully synced mainnet storeblockchain.estimatefeefor various block targets:Actual output
Expected output
A non-negative fee rate (BTC/kB or sat/vB depending on protocol version), similar to what
bitcoind estimatesmartfeereturns. For example:Or, per the Electrum protocol spec, a float fee rate per byte. Returning
-1should only occur when the node truly has no fee data (e.g., freshly started with no blocks processed).Related:
mempool.get_fee_histogramreturns empty{"jsonrpc":"1.0","id":1,"error":null,"result":[]}The fee histogram is also empty, even with an active mempool. This may be related — if the fee estimator doesn't have mempool data, it can't compute estimates.
Impact on Electrum wallet clients
Sparrow Wallet (v2.x)
Sparrow sends
blockchain.estimatefeefor block targets 1, 2, 3, 4, 5, 10, 25 on connection. When all return-1:WARN Failed to retrieve fee rate for target blocks: N (Retries exhausted)java.net.SocketException: Socket is closedFailed to subscribe to path: m/0/0Error broadcasting transaction: Retries exhaustedThis effectively makes libbitcoin-server unusable as an Electrum backend for Sparrow Wallet, even though:
server.versionworksblockchain.headers.subscribeworksblockchain.scripthash.get_historyworks (withaddress_buckets=100000000)blockchain.transaction.broadcastworks at the protocol level (tested with dummy txs, returns proper validation errors)blockchain.relayfeeworks (returns0.0)Comparison with bitcoind + Fulcrum
A bitcoind + Fulcrum stack does not exhibit this issue because Fulcrum proxies
estimatefeeto bitcoind'sestimatesmartfee, which uses historical block fee data (last 1008 blocks) and returns meaningful estimates even with an empty mempool.Root cause analysis
The unit test
test/protocols/electrum/electrum_fees.cppconfirms that-1is the expected return value when the fee estimator has insufficient data:This suggests the fee estimator requires block fee data to be populated via the node chaser event system. On a fully synced mainnet node, this data should be available from historical blocks, but it appears the estimator is not being properly initialized or is unable to access the historical fee data.
Possible causes
mempool.get_fee_histogramreturning[]suggests mempool fee tracking may also be broken, which would affect fee estimationConfiguration
Server logs
After restart,
estimatefeerequests are received but responses are only ~37 bytes (consistent with{"result":-1}):Suggested fix
-1, so wallet clients can gracefully fall back to external fee sourcesRelated issues
blockchain.transaction.getresponse disagrees with peer Electrum implementations (another Sparrow compatibility issue)