diff --git a/SECURITY.md b/SECURITY.md index a56d213..dc87116 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,6 @@ Please do not report security issues in public issues or pull requests. -Report them privately to the maintainers of this repository, using GitHub's private vulnerability reporting +Report them privately, either with GitHub's private vulnerability reporting (the "Report a vulnerability" button in the Security tab of -https://github.com/palladium-coin/plm-electrumx). - -For problems in the upstream ElectrumX code that are not specific to this fork, see also -https://github.com/spesmilo/electrum/blob/master/SECURITY.md +https://github.com/palladium-coin/plm-electrumx) or by email to contact@palladium-coin.com. diff --git a/docs/environment.rst b/docs/environment.rst index 2f1ef9f..8174ed4 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -237,7 +237,7 @@ These environment variables are optional: + ``$DAEMON_VERSION`` is replaced with the daemon's version as a dot-separated string. For example ``0.12.1``. + ``$DAEMON_SUBVERSION`` is replaced with the daemon's user agent - string. For example, ``/BitcoinUnlimited:0.12.1(EB16; AD4)/``. + string. For example, ``/Palladium:2.0.0/``. + ``$DONATION_ADDRESS`` is replaced with the address from the :envvar:`DONATION_ADDRESS` environment variable. @@ -306,7 +306,7 @@ raise them. .. envvar:: MAX_RECV The maximum size of an incoming message in bytes, the default is 1,000,000 bytes. - Note that the smallest sane/safe value for Bitcoin is ~800,100 bytes, + Note that the smallest sane/safe value for Palladium is ~800,100 bytes, as the largest standard tx can have a weight of 400K but the protocol hex-encodes that, plus there is a few bytes of protocol overhead. Setting this to lower than that would preclude clients from broadcasting txs that could propagate over the network. @@ -329,7 +329,7 @@ raise them. Also note that the largest raw transaction you will be able to serve to a client is just under half of :envvar:`MAX_SEND`, as each raw byte becomes 2 hexadecimal - ASCII characters on the wire. For Bitcoin, the consensus limit for tx size is + ASCII characters on the wire. For Palladium, the consensus limit for tx size is 4 M weight units, while the standardness (policy) limit is 400 K weight units. When serialized as bytes, 4 M weight units is 4 MB, and when encoded as hex that is 8 MB. The default was chosen so that such a tx could still be served. diff --git a/src/electrumx/lib/coins.py b/src/electrumx/lib/coins.py index 4998b5c..18ae936 100644 --- a/src/electrumx/lib/coins.py +++ b/src/electrumx/lib/coins.py @@ -77,7 +77,7 @@ class Coin: # max byte-size of single jsonrpc message # note: we should probably be able to serve a client asking for the largest consensus-valid tx. - # For Bitcoin, that is 4 M weight units, i.e. 4 MB on the p2p wire. + # For Palladium (segwit, as Bitcoin), that is 4 M weight units, i.e. 4 MB on the p2p wire. # Double that due to our JSON-RPC hex-encoding, plus overhead, we need to send 8+ MB. # When receiving from a client, e.g. to broadcast, such a tx would be non-standard anyway. DEFAULT_MAX_SEND = 8_100_000 @@ -244,7 +244,7 @@ class Coin: '''Return the number of standard coin units as a Decimal given a quantity of smallest units. - For example 1 BTC is returned for 100 million satoshis. + For example 1 PLM is returned for 100 million satoshis. ''' return Decimal(value) / cls.VALUE_PER_COIN diff --git a/src/electrumx/server/block_processor.py b/src/electrumx/server/block_processor.py index a9d488b..4b573a0 100644 --- a/src/electrumx/server/block_processor.py +++ b/src/electrumx/server/block_processor.py @@ -278,7 +278,7 @@ class BlockProcessor: await self.reorg_chain() else: # It is probably possible but extremely rare that what - # bitcoind returns doesn't form a chain because it + # the daemon returns doesn't form a chain because it # reorg-ed the chain as it was processing the batched # block hash requests. Should this happen it's simplest # just to reset the prefetcher and try again. diff --git a/src/electrumx/server/daemon.py b/src/electrumx/server/daemon.py index d093c08..74a0f76 100644 --- a/src/electrumx/server/daemon.py +++ b/src/electrumx/server/daemon.py @@ -63,7 +63,7 @@ class Daemon: self.urls = [] self.set_url(url) # Limit concurrent RPC calls to this number. - # See DEFAULT_HTTP_WORKQUEUE in bitcoind, which is typically 16 + # See DEFAULT_HTTP_WORKQUEUE in the daemon, which is typically 16 self.workqueue_semaphore = asyncio.Semaphore(value=max_workqueue) self.init_retry = init_retry self.max_retry = max_retry @@ -102,7 +102,7 @@ class Daemon: daemon_rev = ni_version % 100 daemon_version = (daemon_major, daemon_minor, daemon_rev) if daemon_version < required_version: - raise RuntimeError(f"Bitcoin Core {daemon_version=} < {required_version=}.") + raise RuntimeError(f"Daemon version {daemon_version=} < {required_version=}.") async def check_daemon_indexes(self): assert self.session is not None and self.coin is not None, f"{self.session=}, {self.coin=}" @@ -112,11 +112,11 @@ class Daemon: for req_index in self.coin.REQUIRED_DAEMON_INDEXES: if req_index not in index_info: raise RuntimeError( - f"bitcoind missing required index: {req_index}. " - f"You should set {req_index}=1 in your bitcoin.conf config file, and reindex.") + f"the daemon is missing required index: {req_index}. " + f"You should set {req_index}=1 in the daemon's config file, and reindex.") if not index_info[req_index]["synced"]: - # Should we raise? Not raising allows syncing a fresh bitcoind and e-x "in parallel". - self.logger.warning(f"bitcoind required index {req_index!r} is still syncing!") + # Should we raise? Not raising allows syncing a fresh daemon and e-x "in parallel". + self.logger.warning(f"the daemon's required index {req_index!r} is still syncing!") def set_url(self, url: str) -> None: '''Set the URLS to the given list, and switch to the first one.''' @@ -330,19 +330,19 @@ class Daemon: async def relayfee(self): """Same as getmempoolinfo['minrelaytxfee']. The minimum fee required for a transaction to be relayed on by the daemon to the - bitcoin network. Doesn't guarantee mempool acceptance.""" + network. Doesn't guarantee mempool acceptance.""" network_info = await self.getnetworkinfo() return network_info['relayfee'] async def mempool_info(self) -> dict[str, float]: mempool_info = await self.getmempoolinfo() return { - # Dynamic minimum fee rate in BTC/kvB for tx to be accepted. + # Dynamic minimum fee rate in PLM/kvB for tx to be accepted. # Is the maximum of minrelaytxfee and minimum mempool fee 'mempoolminfee': mempool_info['mempoolminfee'], - # Minimum relay fee for transactions in BTC/kvB, operator-configurable, static. + # Minimum relay fee for transactions in PLM/kvB, operator-configurable, static. 'minrelaytxfee': mempool_info['minrelaytxfee'], - # Minimum fee rate increment for mempool limiting or replacement in BTC/kvB, + # Minimum fee rate increment for mempool limiting or replacement in PLM/kvB, # operator-configurable, static. 'incrementalrelayfee': mempool_info['incrementalrelayfee'], } diff --git a/src/electrumx/server/mempool.py b/src/electrumx/server/mempool.py index 7fe0385..9d6ed70 100644 --- a/src/electrumx/server/mempool.py +++ b/src/electrumx/server/mempool.py @@ -66,11 +66,11 @@ class MemPoolAPI(ABC): @abstractmethod async def height(self) -> int: - '''Query bitcoind for its height.''' + '''Query the daemon for its height.''' @abstractmethod def cached_height(self) -> Optional[int]: - '''Return the height of bitcoind the last time it was queried, + '''Return the height of the daemon the last time it was queried, for any reason, without actually querying it. ''' @@ -88,12 +88,12 @@ class MemPoolAPI(ABC): @abstractmethod async def mempool_txids_hum(self) -> Sequence[str]: - '''Query bitcoind for the txids of all transactions in its + '''Query the daemon for the txids of all transactions in its mempool, returned as a list.''' @abstractmethod async def raw_transactions(self, txids_hum: Iterable[str]) -> Sequence[bytes | None]: - '''Query bitcoind for the serialized raw transactions with the given + '''Query the daemon for the serialized raw transactions with the given txids. Missing transactions are returned as None. txids_hum is an iterable of hexadecimal hash strings.''' @@ -356,7 +356,7 @@ class MemPool: prev_mempool_height = height touched_hashxs = set() touched_outpoints = set() - # poll bitcoind's whole mempool every few seconds; or instantly after the DB height changes + # poll the daemon's whole mempool every few seconds; or instantly after the DB height changes async with ignore_after(self.refresh_secs): async with OldTaskGroup(wait=any) as group: await group.spawn(self.api.db_height_changed()) @@ -408,7 +408,7 @@ class MemPool: t0 = time.monotonic() new_hashes = await run_in_thread(lambda: list(all_txids_rev.difference(txs))) if new_hashes: - # 2.1. fetch raw txs from bitcoin daemon + # 2.1. fetch raw txs from the daemon group = OldTaskGroup() for hashes in chunks(new_hashes, 200): coro = self._fetch_raw_txs_and_utxos( @@ -435,7 +435,7 @@ class MemPool: # - some UTXOs we find in the DB=utxo_map (chainstate), # - some we find in self.txs (our memool), # - some we might only find in tx_map (among the candidates: consider long chain of unconfirmed txs) - # - some we might not find anywhere: if DB is corrupted or bitcoind gave inconsistent mempool, or other race + # - some we might not find anywhere: if DB is corrupted or the daemon gave inconsistent mempool, or other race # In each iteration, we loop over tx_map and move accepted transactions from it to self.txs. # In the worst degenerate case, this could be O(n^2). To avoid that, we topologically sort tx_map. nonlocal tx_map, utxo_map diff --git a/src/electrumx/server/session.py b/src/electrumx/server/session.py index fc8d3bf..b6afd13 100644 --- a/src/electrumx/server/session.py +++ b/src/electrumx/server/session.py @@ -892,7 +892,7 @@ class SessionManager: # significantly reduces the size of the critical time window. # We try to give the illusion of a consistent monolithic information-source to the client. # Most protocol method handlers only use e-x's db and mempool to resolve requests, - # the few that also query bitcoind need to implement their own logic to make sure + # the few that also query the daemon need to implement their own logic to make sure # the state they present is consistent with e-x's current db state. if not (bp_height == db_height == notif_height): raise ChainSyncError(message="DB/mempool still catching up with each other") @@ -1598,15 +1598,15 @@ class ElectrumX(SessionBase): async def _calc_oc_txo_status(self, funder_txid_rev: bytes, txout_idx: int) -> 'TXOSpendStatus': """For an outpoint, returns its spend-status (ignoring mempool events). - Uses daemon (bitcoind) to find the spender_txhash, requiring "txospenderindex=1". + Uses the daemon to find the spender_txhash, requiring "txospenderindex=1". However, mempool events are ignored, as it would be difficult to distinguish block height 0 vs -1 - using only the daemon. Instead, our own mempool data (as opposed to bitcoind's) can be used + using only the daemon. Instead, our own mempool data (as opposed to the daemon's) can be used separately to enrich the return value. Can raise RPCError. """ funder_txid_hum = hash_to_hex_str(funder_txid_rev) - # 1. call bitcoind "getrawtransaction" to see if prevtx exists/is_mined + # 1. call the daemon "getrawtransaction" to see if prevtx exists/is_mined self.bump_cost(1) try: funder_item = await self.session_mgr.daemon.getrawtransaction(funder_txid_hum, verbose=True) # verbose=int(1) @@ -1633,7 +1633,7 @@ class ElectrumX(SessionBase): if len(vouts) <= txout_idx: return TXOSpendStatus(funder_height=None) # txout_idx was out-of-bounds # by now we know the funding TXO existed in the chain. Let's see if it was spent. - # 2. call bitcoind "gettxspendingprevout" + # 2. call the daemon "gettxspendingprevout" self.bump_cost(1) try: spender_item = await self.session_mgr.daemon.gettxspendingprevout(funder_txid_hum, txout_idx) @@ -1642,7 +1642,7 @@ class ElectrumX(SessionBase): self.logger.debug(f"gettxspendingprevout errored. txo={funder_txid_hum}:{txout_idx}. {error=}") raise RPCError(DAEMON_ERROR, f'daemon error: {error!r}') from None assert spender_item.get("txid") == funder_txid_hum, f"{spender_item.get('txid')=} != {funder_txid_hum=}" - # paranoia: check if funder tx got reorged while we were awaiting bitcoind RPC + # paranoia: check if funder tx got reorged while we were awaiting the daemon RPC if self.db.get_blockheight_from_blockhash(funder_bhash) is None: raise ReorgWhileProcessingRequest() spender_bhash = spender_item.get("blockhash") @@ -1675,7 +1675,7 @@ class ElectrumX(SessionBase): """Can raise RPCError""" self.bump_cost(0.1) prevout = (prev_txid_rev, txout_idx) - # first, consider only on-chain mined events, and check cache first (to avoid bitcoind RPC calls) + # first, consider only on-chain mined events, and check cache first (to avoid daemon RPC calls) self.session_mgr.oc_txo_status_cache.num_lookups += 1 try: oc_status = self.session_mgr.oc_txo_status_cache[prevout] @@ -1880,7 +1880,7 @@ class ElectrumX(SessionBase): txout_idx = non_negative_integer(txout_idx) assert_hex_str(spk_hint) # calc status, update client-last-seen status. - # if we can't calc the status, as e.g. bitcoind errors, don't add the subscription + # if we can't calc the status, as e.g. the daemon errors, don't add the subscription spend_status = await self.txoutpoint_status_for_notif(txid_rev, txout_idx) db_chaintip = self.session_mgr.get_block_ref_for_chaintip() if max(spend_status.funder_height or 0, spend_status.spender_height or 0) > db_chaintip.height: @@ -2040,7 +2040,7 @@ class ElectrumX(SessionBase): async def phandle_relayfee(self): """The minimum fee required for a transaction to be relayed on by the daemon to the - bitcoin network. Doesn't guarantee mempool acceptance.""" + network. Doesn't guarantee mempool acceptance.""" self.bump_cost(1.0) return await self.daemon_request('relayfee') @@ -2048,9 +2048,9 @@ class ElectrumX(SessionBase): """ mempool.get_info, introduced in protocol 1.6. returns: { - "mempoolminfee": BTC/kvB, - "minrelaytxfee": BTC/kvB, - "incrementalrelayfee": BTC/kvB, + "mempoolminfee": PLM/kvB, + "minrelaytxfee": PLM/kvB, + "incrementalrelayfee": PLM/kvB, } """ self.bump_cost(1.0)