Skip to content

Upgrade to ElectrumX 2.0.0 and port the Doichain coin classes (green CI, replaces #11) - #12

Merged
NiKrause merged 485 commits into
masterfrom
feat/electrumx-2.0-r3
Sep 16, 2026
Merged

NiKrause merged 485 commits into
masterfrom
feat/electrumx-2.0-r3

Conversation

@NiKrause

Copy link
Copy Markdown
Contributor

Same upgrade as #11, with the failing unit test fixed. Merging this one is enough — it contains everything from #10 and #11.

What was red

tests/test_blocks.py::test_all_coins_are_covered. Upstream requires every mainnet coin class to ship at least one block fixture, and Doichain had none. 1.15.0 carried no such test, so the gap only surfaces with 2.0. pycodestyle and build_docs were green throughout.

The fix

Two fixtures taken from a synced node:

block contents why
431,320 2 transactions, the second a name_doi the block the name index was verified against — the fixture now exercises the operation that distinguishes Doichain from Namecoin, through DeserializerAuxPowSegWit
100,000 single coinbase the plain path

test_block deserialises each with the coin class and checks the header hash, the previous hash and every txid. Both pass.

The four remaining failures in that file are ModuleNotFoundError: No module named 'blake256' on unrelated coins (Decred and friends); they occur identically without these files — verified by removing them and re-running: 5 failures without, 4 with, the difference being exactly the coverage test.

Everything else is unchanged from #11

Upgrade from ElectrumX 1.15.0 to upstream 2.0.0 with the Doichain coin classes ported onto it, master merged in and the conflicts resolved — including the one that produced no conflict marker: git treated electrumx/lib/coins.py → src/electrumx/lib/coins.py as a rename and merged the 1.15-era classes into the already-ported file, leaving every class defined twice. Details in #11.

Please close #10 and #11 in favour of this one.

🤖 Generated with Claude Code

SomberNight and others added 30 commits June 17, 2025 10:28
docs: fix lib/coins.py link in environment.rst
to help distinguish logs from kyuupichan/electrumx

ref spesmilo/electrumx#306
This prevents the DoS attack vector caused by hitting the max number of sessions.
Prior to this change if the max number of sessions was reached, the server would stop listening for new connections until ALL existing connections were closed.
Now, the listening port is closed, the existing connections remain live and when the lower threshold is reached, the listening socket is reopened.
…implementation

fix: prevent incorrect session limit DoS attack vector
Adds the `mempool.get_info` rpc to fetch mempoolminfee, minrelaytxfee
and incrementalrelayfee from the daemons getmempoolinfo rpc for protocol
version 1.6.
Updates the relayfee comments as the fee returned by
`blockchain.relayfee` doesn't guarantee to get into the mempool as
previously stated in the docstrings.
The parsing of the version number returned from 'getnetworkinfo' was not
adapted to the changed bitcoin core version number scheme since core went
from beta to stable. The banner incorrectly showed 0.29.0 instead of
29.0.0.
Checks the daemon version on startup against a minimum allowed daemon
version hardcoded in the corresponding `Coin`. This allows ensure that
the connected daemon is running a version that supports all the rpc
calls electrumx requires.
Guards the new `blockchain.transaction.broadcast_package` and
`mempool.get_info` methods of protocol 1.6 behind this versions ptuple
in `set_request_handlers` so they are only exposed in protocol versions
>= 1.6. Also stops exposing the `blockchain.relayfee` endpoint for
version >= 1.6 as it got removed in 1.6.
implements mempool.get_info and clarifies comments of relayfee
check daemon version on startup, fix version in banner
Removes the incorrect and unnecessary txid calculation in
`ElectrumX.package_broadcast()`, the daemon will complain anyways if the
transactions are invalid.
Make mode uppercase if passed as neither the protocol nor core do
specify any requirement here.
session: estimatefee: normalize mode arg
session: broadcast_package: followup #288
session.py: sanitize inputs for tx broadcast RPCs
was just sending back a response with value 1, instead of a jsonrpc error

ref spesmilo/electrumx@860ff53
session: fix ReplyAndDisconnect. was broken for PaddedRSTransport
Adds a 1024 (unpadded) byte sending budget to the PaddedRSTransport below which
messages are instantly flushed down the socket before the transport will
beginn waiting for the buffer to reach MIN_PACKET_SIZE (1024).
This allows to get the first couple of messages sent quickly when
a wallet connects to improve its ux.
add warmup budget to PaddedRSTransport
We already enforce that server.version must be the first received message
in a session, but we also need to ensure that the server finishes processing
that message and sets up the correct protocol version before starting to
process further messages.
protocol 1.6: "server.version" must be first msg, must tolerate unknown args
Note that this is a soft fork: the server can apply it even for past
protocol versions.

Previously, with the order being undefined, if an address had multiple
mempool transactions touching it, switching between different servers
could result in a change in address status simply as a result of these
servers ordering mempool txs differently. This would result in the client
re-requesting the whole history of the address.

-----

D/i | interface.[electrum.blockstream.info:60002] | <-- ('blockchain.scripthash.subscribe', ['660b44502503064f9d5feee48726287c0973e25bc531b4b8a072f57f143d5cd0']) {} (id: 12)
D/i | interface.[electrum.blockstream.info:60002] | --> 9da27f9df91e3f860212f65b736fa20a539ba6e3d509f6370367ee7f10a4d5b0 (id: 12)
D/i | interface.[electrum.blockstream.info:60002] | <-- ('blockchain.scripthash.get_history', ['660b44502503064f9d5feee48726287c0973e25bc531b4b8a072f57f143d5cd0']) {} (id: 13)
D/i | interface.[electrum.blockstream.info:60002] | --> [
{'fee': 200, 'height': 0, 'tx_hash': '3ee6d6e26291ce360127fe039b816470fce6eeea19b5c9d10829a1e4efc2d0c7'},
{'fee': 239, 'height': 0, 'tx_hash': '9e050f09b676b9b0ee26aa02ccee623fae585a85d6a5e24ecedd6f8d6d2d3b1d'},
{'fee': 178, 'height': 0, 'tx_hash': 'fb80adbf8274190418cb3fb0385d82fe9d47a844d9913684fa5fb3d48094b35a'},
{'fee': 200, 'height': 0, 'tx_hash': '713933c50b7c43f606dad5749ea46e3bc6622657e9b13ace9d639697da266e8b'}
] (id: 13)

D/i | interface.[testnet.hsmiths.com:53012] | <-- ('blockchain.scripthash.subscribe', ['660b44502503064f9d5feee48726287c0973e25bc531b4b8a072f57f143d5cd0']) {} (id: 12)
D/i | interface.[testnet.hsmiths.com:53012] | --> f7ef7237d2d62a3280acae05616200b96ad9dd85fd0473c29152a4a41e05686c (id: 12)
D/i | interface.[testnet.hsmiths.com:53012] | <-- ('blockchain.scripthash.get_history', ['660b44502503064f9d5feee48726287c0973e25bc531b4b8a072f57f143d5cd0']) {} (id: 13)
D/i | interface.[testnet.hsmiths.com:53012] | --> [
{'tx_hash': '9e050f09b676b9b0ee26aa02ccee623fae585a85d6a5e24ecedd6f8d6d2d3b1d', 'height': 0, 'fee': 239},
{'tx_hash': 'fb80adbf8274190418cb3fb0385d82fe9d47a844d9913684fa5fb3d48094b35a', 'height': 0, 'fee': 178},
{'tx_hash': '3ee6d6e26291ce360127fe039b816470fce6eeea19b5c9d10829a1e4efc2d0c7', 'height': 0, 'fee': 200},
{'tx_hash': '713933c50b7c43f606dad5749ea46e3bc6622657e9b13ace9d639697da266e8b', 'height': 0, 'fee': 200}
] (id: 13)
…ed hex string

Array headers: fix type error

Array headers: move variable initialization
SomberNight and others added 26 commits June 29, 2026 14:13
utxo db: incrementally clear excess undo infos as we go
reverts parts of
e1ccd4b
ead66dd

self.txs and self.hashXs and self.txo_to_spender are accessed
from the external interface, on the asyncio thread, and they are
assumed to be consistent with each other.
mempool: partially revert threading _process_mempool
protocol 1.7: revert adding chaintip to responses
also,
- don't pin python patch version
- use default env var values
- "docker stop" seems cleaner, more intuitive, than kill
dockerfile: switch to rocksdb, and show how to build free-threaded python
Our fleet runs ElectrumX 1.15.0 (2020). Upstream 2.0.0 moved the tree to
src/, merged the name and AuxPoW mixins into NameIndexAuxPoWMixin (which
carries SESSIONCLS), turned P2SH_VERBYTES and NAME_OPERATIONS into tuples,
added NAME_EXPIRATION, and dropped XPUB/XPRV/WIF entirely -- they have no
remaining use anywhere in the tree.

Values come from the chain and the consensus rules rather than being carried
over unchanged:

  RPC_PORT 8339 / 18339       8338 is the P2P port since Core 31, so the old
                              value pointed ElectrumX at the wrong socket
  TX_COUNT 2,742,220          getchaintxstats at height 431,763
  NAME_EXPIRATION 36,000 / 30 Consensus::MainNetConsensus::NameExpirationDepth
                              (36,000 above height 48,000; regtest 30)

Testnet and regtest inherit from Doichain now instead of repeating every
declaration, matching how upstream keeps the Namecoin classes.

Verified with the package installed: ElectrumX resolves all three classes,
with SESSIONCLS NameIndexAuxPoWElectrumX, DESERIALIZER DeserializerAuxPowSegWit,
BLOCK_PROCESSOR NameIndexBlockProcessor, four name operations including
name_doi, and REQUIRED_DAEMON_INDEXES empty -- so no extra daemon index is
required. tests/lib/test_coins.py: 16 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Resolves the conflicts the upgrade branch had against master, which still carries
ElectrumX 1.15.0 plus our seven commits.

- .travis.yml: upstream deleted it in 2.0 (CI moved to GitHub Actions); master's
  last edit to it is dropped with the file.
- .gitignore: upstream's version, which already covers the src/ layout and both
  egg-info names, plus our /certs entry.
- contrib/systemd/electrumx.service: merged cleanly, Restart=always and
  RuntimeMaxSec=604800 are kept.
- electrumX.egg-info/: a build artefact committed by accident in 2021; removed,
  and .gitignore now covers it.

The one that needed care: git detected electrumx/lib/coins.py -> src/electrumx/
lib/coins.py as a rename and merged master's 121 lines of Doichain classes into
the file that already carries the ported ones -- **without a conflict marker**.
That left every class defined twice, and in Python the second definition wins, so
the server would silently have run the old classes again: no SESSIONCLS, hence no
name RPC, RPC port 8338, no NAME_EXPIRATION. The 1.15-era block is removed.

Verified after the resolution: exactly three Doichain classes; ElectrumX resolves
all three with SESSIONCLS NameIndexAuxPoWElectrumX, RPC ports 8339/18339/18332,
NAME_EXPIRATION 36,000 (regtest 30) and four name operations including name_doi;
tests/lib/test_coins.py 16 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI failed on tests/test_blocks.py::test_all_coins_are_covered: upstream requires
every mainnet coin class to come with at least one block fixture, and Doichain
had none. 1.15.0 had no such test, so the gap only appears with 2.0.

Two blocks, taken from a synced node:

- **431,320** -- two transactions, the second a `name_doi`. This is the block the
  name index was verified against, so the fixture now covers the operation that
  distinguishes Doichain from Namecoin, through DeserializerAuxPowSegWit.
- **100,000** -- an ordinary single-coinbase block, for the plain path.

test_block checks the header hash, the previous hash and every txid against the
deserialised block; both pass. The remaining failures in that file are
`ModuleNotFoundError: No module named 'blake256'` on unrelated coins, which occur
with or without these files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@NiKrause
NiKrause merged commit 11877d7 into master Sep 16, 2026
3 checks passed
NiKrause added a commit to Doichain/doichain-install that referenced this pull request Sep 16, 2026
The build context pointed at 9afddfec, a commit that only ever lived on the
upgrade branch. Doichain/electrumx#12 was squash-merged, so that commit is not an
ancestor of master and survives only as long as nobody deletes the branch -- the
pin held by habit rather than by guarantee.

It now points at 11877d7b, the commit carrying the signed tag v2.0.0-doi1. The
commit is pinned rather than the tag name, because a tag can be moved and a
commit cannot; the tag is named in the comment next to it.

The image doichain/electrumx:v2.0.0-doi1 was rebuilt from that commit, so the
source tag and the image tag now describe the same state; the image records the
commit in /electrumx-source-commit. Also corrects the ElectrumX timing in the
compose comment to the value measured with the 2.0 image (~22 min).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants