Fix mempool.get_info, testmempoolaccept and outpoint.subscribe with the Palladium daemon
- mempool.get_info: the daemon has no incrementalrelayfee in getmempoolinfo, read incrementalfee from getnetworkinfo - testmempoolaccept: the daemon does not report wtxid - outpoint.subscribe: refuse the subscription (Coin.DAEMON_HAS_TXO_SPENDER_INDEX) instead of closing the session at the next block, when the daemon errors Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
27ef7cbabf
commit
b32548fd01
9 files changed
+41
-13
No files matched your search
@@ -41,7 +41,7 @@ Pipeline (see `docs/architecture.rst`): `Env` → `Controller` (`server/controll
|
||||
|
||||
Coin abstraction: `lib/coins.py` defines `Coin` and the `Palladium`, `PalladiumTestnet`, `PalladiumRegtest` subclasses. A coin class selects behavior through class attributes: `DESERIALIZER` (block/tx format, in `lib/tx.py`, only the segwit one remains), `DAEMON`, `BLOCK_PROCESSOR`, `SESSIONCLS`, address verbytes, `GENESIS_HASH`, `REORG_LIMIT`, `RPC_PORT`, `PEERS`, header handling, etc. Coins are discovered by walking `Coin` subclasses (`lookup_coin_class` matches `NAME` + `NET`; `COIN` defaults to `Palladium`). Concrete classes must define `TX_COUNT`, `TX_COUNT_HEIGHT`, `TX_PER_BLOCK` (used only for sync ETAs) or lookup raises.
|
||||
|
||||
Daemon: Palladium Core has segwit and taproot (BIP341/342, mainnet height 405000; the server is script-type agnostic, indexing by script hash) and bech32/bech32m addresses, but no `getindexinfo`, `txospenderindex` or `submitpackage` RPCs. So `MIN_REQUIRED_DAEMON_VERSION`/`REQUIRED_DAEMON_INDEXES` (see `Coin`) are unset, and `blockchain.outpoint.subscribe` / `blockchain.transaction.broadcast_package` return a daemon error to clients. The daemon needs `txindex=1`.
|
||||
Daemon: Palladium Core has segwit and taproot (BIP341/342, mainnet height 405000; the server is script-type agnostic, indexing by script hash) and bech32/bech32m addresses, but no `getindexinfo`, `txospenderindex` or `submitpackage` RPCs. So `MIN_REQUIRED_DAEMON_VERSION`/`REQUIRED_DAEMON_INDEXES` (see `Coin`) are unset, and `blockchain.outpoint.subscribe` is refused with an explicit error (`Coin.DAEMON_HAS_TXO_SPENDER_INDEX = False`: otherwise the subscription is accepted and the daemon error at the next block closes the session) while `blockchain.transaction.broadcast_package` returns the daemon error to clients. The daemon needs `txindex=1`.
|
||||
|
||||
DB schema changed in 2.0 with no migration; changing on-disk format requires a full resync.
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ From another machine, `nc -zv <public IP> 50001` must succeed if the server is m
|
||||
| log: connection refused / cannot connect to `palladiumd:2332` | the node is stopped or still starting; check that the node container is running |
|
||||
| log: `401`/authorization errors | wrong `PLM_RPC_USER` / `PLM_RPC_PASSWORD` in `.env`; the credentials must be the ones in the node's `palladium.conf` |
|
||||
| errors mentioning `txindex` | `txindex=1` is missing in `palladium.conf` (an index added later is built while the node runs, which takes time) |
|
||||
| `blockchain.outpoint.subscribe` or package broadcast return a daemon error | expected: Palladium Core does not have the RPCs they need (`gettxspendingprevout`, `submitpackage`) |
|
||||
| `blockchain.outpoint.subscribe` is refused, or package broadcast returns a daemon error | expected: Palladium Core does not have the RPCs they need (`gettxspendingprevout`, `submitpackage`) |
|
||||
| the server stays at an old height | the node is not synced; ElectrumX only follows the node |
|
||||
| clients cannot connect from outside | port not published/forwarded, firewall, or `SERVICES` does not list that protocol |
|
||||
| container stops at once, log says `ERROR: ... is not writable` | the data directory belongs to another user (or to root, if Docker created it): `chown -R $(id -u):$(id -g) ~/.plm-electrumx`, or set `PLM_UID`/`PLM_GID` |
|
||||
|
||||
@@ -91,8 +91,8 @@ When merging upstream, resolve modify/delete conflicts on these files by keeping
|
||||
bech32m for taproot) are only read by the local `electrumx_rpc query` command.
|
||||
- Palladium Core has no `getindexinfo`, `txospenderindex` (`gettxspendingprevout`) or `submitpackage` RPCs,
|
||||
so the daemon version/index checks are disabled for `COIN=Palladium`, and
|
||||
`blockchain.outpoint.subscribe` and `blockchain.transaction.broadcast_package` return a daemon
|
||||
error to clients.
|
||||
`blockchain.outpoint.subscribe` is refused with an explicit error (`Coin.DAEMON_HAS_TXO_SPENDER_INDEX`)
|
||||
and `blockchain.transaction.broadcast_package` returns the daemon error to clients.
|
||||
- The daemon must run with `txindex=1`.
|
||||
- Same genesis blocks as Bitcoin (mainnet, testnet3, regtest), 80-byte headers, sha256d PoW.
|
||||
- Parameters (address prefixes, ports, genesis hashes) were taken from the Palladium Core chain parameters.
|
||||
|
||||
+6
-2
@@ -13,8 +13,12 @@ First release of the Palladium fork. Based on a snapshot of upstream ElectrumX 2
|
||||
* Segwit and taproot outputs are supported (the server indexes by script hash); ``Coin.BECH32_HRP`` and a
|
||||
bech32/bech32m decoder let ``electrumx_rpc query`` read ``plm1``, ``tplm1`` and ``rplm1`` addresses.
|
||||
* Palladium Core has no ``getindexinfo``, ``txospenderindex`` or ``submitpackage`` RPCs: the daemon version/index
|
||||
checks are disabled, ``txindex=1`` is required, and ``blockchain.outpoint.subscribe`` /
|
||||
``blockchain.transaction.broadcast_package`` return a daemon error to clients.
|
||||
checks are disabled, ``txindex=1`` is required, ``blockchain.outpoint.subscribe`` is refused with an explicit
|
||||
error (``Coin.DAEMON_HAS_TXO_SPENDER_INDEX``) and ``blockchain.transaction.broadcast_package`` returns the
|
||||
daemon error to clients.
|
||||
* fixed: ``mempool.get_info`` (protocol 1.6) failed with the Palladium daemon, whose ``getmempoolinfo`` has no
|
||||
``incrementalrelayfee`` (it is read from ``getnetworkinfo``), and ``blockchain.transaction.testmempoolaccept``
|
||||
failed because the daemon does not report ``wtxid``.
|
||||
* new: integration tests (``tests/integration``) against a Palladium node in regtest mode, run in Docker, with
|
||||
a ``integration`` job in CI.
|
||||
* removed: all other coins, their transaction deserializers, session/daemon/block processor classes, tests,
|
||||
|
||||
@@ -100,6 +100,8 @@ class Coin:
|
||||
NET: str | None
|
||||
MIN_REQUIRED_DAEMON_VERSION: Optional[str] = None
|
||||
REQUIRED_DAEMON_INDEXES: Sequence[str] = tuple()
|
||||
# whether the daemon can tell which tx spends an outpoint (gettxspendingprevout); needed to subscribe to outpoints
|
||||
DAEMON_HAS_TXO_SPENDER_INDEX = True
|
||||
|
||||
# only used for initial db sync ETAs:
|
||||
TX_COUNT_HEIGHT: int # at a given snapshot of the chain,
|
||||
@@ -268,8 +270,9 @@ class Palladium(Coin):
|
||||
so no special handling is needed for either).
|
||||
|
||||
Its daemon has no 'txospenderindex', 'getindexinfo' or 'submitpackage', so
|
||||
'blockchain.outpoint.subscribe' and 'blockchain.transaction.broadcast_package' will
|
||||
return daemon errors to clients. The daemon needs 'txindex=1'.
|
||||
'blockchain.outpoint.subscribe' is refused (DAEMON_HAS_TXO_SPENDER_INDEX) and
|
||||
'blockchain.transaction.broadcast_package' will return a daemon error to clients.
|
||||
The daemon needs 'txindex=1'.
|
||||
"""
|
||||
NAME = "Palladium"
|
||||
SHORTNAME = "PLM"
|
||||
@@ -282,6 +285,7 @@ class Palladium(Coin):
|
||||
'4ff763ae46a2a6c172b3f1b60a8ce26f')
|
||||
MEMPOOL_HISTOGRAM_REFRESH_SECS = 120
|
||||
DESERIALIZER = lib_tx.DeserializerSegWit
|
||||
DAEMON_HAS_TXO_SPENDER_INDEX = False
|
||||
RPC_PORT = 2332
|
||||
# snapshot (UpdateTip tx= count), only used for sync ETAs
|
||||
TX_COUNT = 661_421
|
||||
|
||||
@@ -336,6 +336,10 @@ class Daemon:
|
||||
|
||||
async def mempool_info(self) -> dict[str, float]:
|
||||
mempool_info = await self.getmempoolinfo()
|
||||
incrementalrelayfee = mempool_info.get('incrementalrelayfee')
|
||||
if incrementalrelayfee is None:
|
||||
# the Palladium daemon only reports it in getnetworkinfo, as 'incrementalfee'
|
||||
incrementalrelayfee = (await self.getnetworkinfo())['incrementalfee']
|
||||
return {
|
||||
# Dynamic minimum fee rate in PLM/kvB for tx to be accepted.
|
||||
# Is the maximum of minrelaytxfee and minimum mempool fee
|
||||
@@ -344,7 +348,7 @@ class Daemon:
|
||||
'minrelaytxfee': mempool_info['minrelaytxfee'],
|
||||
# Minimum fee rate increment for mempool limiting or replacement in PLM/kvB,
|
||||
# operator-configurable, static.
|
||||
'incrementalrelayfee': mempool_info['incrementalrelayfee'],
|
||||
'incrementalrelayfee': incrementalrelayfee,
|
||||
}
|
||||
|
||||
async def getrawtransaction(self, txid_hum: str, verbose=False):
|
||||
|
||||
@@ -1876,6 +1876,11 @@ class ElectrumX(SessionBase):
|
||||
spk_hint: scriptPubKey corresponding to the outpoint. Might be used by
|
||||
other servers, but we don't need and hence ignore it.
|
||||
'''
|
||||
if not self.env.coin.DAEMON_HAS_TXO_SPENDER_INDEX:
|
||||
# the status of a subscribed outpoint is re-checked with the daemon at every block: without
|
||||
# support the subscription would be accepted now and then close the session
|
||||
raise RPCError(BAD_REQUEST, 'outpoint subscriptions are not supported: the daemon cannot look up '
|
||||
'the spender of an outpoint')
|
||||
txid_rev = assert_txid_hum(tx_hash)
|
||||
txout_idx = non_negative_integer(txout_idx)
|
||||
assert_hex_str(spk_hint)
|
||||
@@ -2273,10 +2278,9 @@ class ElectrumX(SessionBase):
|
||||
|
||||
response: list[dict] = []
|
||||
for orig_item in daemon_result: # one item for each tx
|
||||
new_item = {
|
||||
"txid": orig_item["txid"],
|
||||
"wtxid": orig_item["wtxid"],
|
||||
}
|
||||
new_item = {"txid": orig_item["txid"]}
|
||||
if "wtxid" in orig_item: # not reported by older daemons (Palladium Core)
|
||||
new_item["wtxid"] = orig_item["wtxid"]
|
||||
# optional: "allowed" field
|
||||
if orig_item.get("allowed") in (True, False):
|
||||
new_item["allowed"] = orig_item["allowed"]
|
||||
|
||||
@@ -46,6 +46,7 @@ def test_daemon_requirements_disabled():
|
||||
for coin, *_ in PARAMS:
|
||||
assert coin.MIN_REQUIRED_DAEMON_VERSION is None
|
||||
assert not coin.REQUIRED_DAEMON_INDEXES
|
||||
assert coin.DAEMON_HAS_TXO_SPENDER_INDEX is False
|
||||
|
||||
|
||||
def bech32_encode_v0(hrp, program):
|
||||
|
||||
@@ -261,6 +261,17 @@ async def test_mempool_info(daemon):
|
||||
assert await daemon.mempool_info() == expected_result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mempool_info_incrementalfee_from_networkinfo(daemon):
|
||||
# Palladium Core: getmempoolinfo has no 'incrementalrelayfee', getnetworkinfo has 'incrementalfee'
|
||||
daemon.session = ClientSessionGood(
|
||||
('getmempoolinfo', [], {'mempoolminfee': 0.00001, 'minrelaytxfee': 0.00002, 'other': 'cruft'}),
|
||||
('getnetworkinfo', [], {'incrementalfee': 0.00003, 'other': 'cruft'}),
|
||||
)
|
||||
assert await daemon.mempool_info() == {
|
||||
'mempoolminfee': 0.00001, 'minrelaytxfee': 0.00002, 'incrementalrelayfee': 0.00003}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mempool_txids_hum(daemon):
|
||||
hashes = ['hex_hash1', 'hex_hash2']
|
||||
|
||||
Reference in new issue
Block a user