Minor environment variable improvements
- COIN and NET strip surrounding whitespace - new environment variable RPC_HOST, similar to HOST, but for RPC. Permits fine-grained control of which addresses the RPC server listens on. - HOST and RPC_HOST strip surrounding whitespace from hostnames and IP addresses - tests and documentation updated to match
This commit is contained in:
parent
0c2e5c6368
commit
131344715a
@ -8,6 +8,11 @@ given, the rest will have sensible defaults if not specified. Many of
|
|||||||
the defaults around resource usage are conservative; I encourage you
|
the defaults around resource usage are conservative; I encourage you
|
||||||
to review them.
|
to review them.
|
||||||
|
|
||||||
|
Note: by default the server will only serve to connections from the
|
||||||
|
same machine. To be accessible to other users across the internet you
|
||||||
|
must set **HOST** appropriately; see below.
|
||||||
|
|
||||||
|
|
||||||
Required
|
Required
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@ -53,6 +58,7 @@ The following are required if you use the `run` script:
|
|||||||
|
|
||||||
The username the server will run as.
|
The username the server will run as.
|
||||||
|
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@ -63,12 +69,6 @@ These environment variables are optional:
|
|||||||
Must be a *NET* from one of the **Coin** classes in `lib/coins.py`_.
|
Must be a *NET* from one of the **Coin** classes in `lib/coins.py`_.
|
||||||
Defaults to `mainnet`.
|
Defaults to `mainnet`.
|
||||||
|
|
||||||
Note: if you are using Bitcoin Core post the August 1st fork, you
|
|
||||||
should have NET be `bitcoin-segwit`, and if on the Bitcoin Cash
|
|
||||||
chain NET should be `mainnet`.
|
|
||||||
Note Bitcoin Core >= 0.13.1 requires a special *NET* for testnet:
|
|
||||||
`testnet-segwit`.
|
|
||||||
|
|
||||||
* **DB_ENGINE**
|
* **DB_ENGINE**
|
||||||
|
|
||||||
Database engine for the UTXO and history database. The default is
|
Database engine for the UTXO and history database. The default is
|
||||||
@ -76,13 +76,6 @@ These environment variables are optional:
|
|||||||
install the appropriate python package for your engine. The value
|
install the appropriate python package for your engine. The value
|
||||||
is not case sensitive.
|
is not case sensitive.
|
||||||
|
|
||||||
* **REORG_LIMIT**
|
|
||||||
|
|
||||||
The maximum number of blocks to be able to handle in a chain
|
|
||||||
reorganisation. ElectrumX retains some fairly compact undo
|
|
||||||
information for this many blocks in levelDB. The default is a
|
|
||||||
function of **COIN** and **NET**; for Bitcoin mainnet it is 200.
|
|
||||||
|
|
||||||
* **HOST**
|
* **HOST**
|
||||||
|
|
||||||
The host or IP address that the TCP and SSL servers will use when
|
The host or IP address that the TCP and SSL servers will use when
|
||||||
@ -101,20 +94,25 @@ These environment variables are optional:
|
|||||||
If set then SSL_CERTFILE and SSL_KEYFILE must be defined and be
|
If set then SSL_CERTFILE and SSL_KEYFILE must be defined and be
|
||||||
filesystem paths to those SSL files.
|
filesystem paths to those SSL files.
|
||||||
|
|
||||||
|
* **RPC_HOST**
|
||||||
|
|
||||||
|
The host or IP address that the RPC server will listen on and
|
||||||
|
defaults to `localhost`. To listen on multiple specific addresses
|
||||||
|
specify a comma-separated list. Servers with unusual networking
|
||||||
|
setups might want to specify e.g. `::1` or `127.0.0.1` explicitly
|
||||||
|
rather than defaulting to `localhost`.
|
||||||
|
|
||||||
|
An empty string (normally indicating all interfaces) is interpreted
|
||||||
|
as `localhost`, because allowing access to the server's RPC
|
||||||
|
interface to arbitrary connections aacross the internet is not a
|
||||||
|
good idea.
|
||||||
|
|
||||||
* **RPC_PORT**
|
* **RPC_PORT**
|
||||||
|
|
||||||
ElectrumX will listen on this port for local RPC connections.
|
ElectrumX will listen on this port for local RPC connections.
|
||||||
ElectrumX listens for RPC connections unless this is explicitly set
|
ElectrumX listens for RPC connections unless this is explicitly set
|
||||||
to blank. The default is appropriate for **COIN** and **NET**
|
to blank. The default depends on **COIN** and **NET** (e.g., 8000
|
||||||
(e.g., 8000 for Bitcoin mainnet) if not set.
|
for Bitcoin mainnet) if not set, as indicated in `lib/coins.py`_.
|
||||||
|
|
||||||
* **EVENT_LOOP_POLICY**
|
|
||||||
|
|
||||||
The name of an event loop policy to replace the default asyncio
|
|
||||||
policy, if any. At present only `uvloop` is accepted, in which case
|
|
||||||
you must have installed the `uvloop`_ Python package.
|
|
||||||
|
|
||||||
If you are not sure what this means leave it unset.
|
|
||||||
|
|
||||||
* **DONATION_ADDRESS**
|
* **DONATION_ADDRESS**
|
||||||
|
|
||||||
@ -159,6 +157,22 @@ These environment variables are optional:
|
|||||||
that **ANON_LOGS** is honoured. Defaults to 3600. Set to zero to
|
that **ANON_LOGS** is honoured. Defaults to 3600. Set to zero to
|
||||||
suppress this logging.
|
suppress this logging.
|
||||||
|
|
||||||
|
* **REORG_LIMIT**
|
||||||
|
|
||||||
|
The maximum number of blocks to be able to handle in a chain
|
||||||
|
reorganisation. ElectrumX retains some fairly compact undo
|
||||||
|
information for this many blocks in levelDB. The default is a
|
||||||
|
function of **COIN** and **NET**; for Bitcoin mainnet it is 200.
|
||||||
|
|
||||||
|
* **EVENT_LOOP_POLICY**
|
||||||
|
|
||||||
|
The name of an event loop policy to replace the default asyncio
|
||||||
|
policy, if any. At present only `uvloop` is accepted, in which case
|
||||||
|
you must have installed the `uvloop`_ Python package.
|
||||||
|
|
||||||
|
If you are not sure what this means leave it unset.
|
||||||
|
|
||||||
|
|
||||||
Resource Usage Limits
|
Resource Usage Limits
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|||||||
@ -204,7 +204,7 @@ class Controller(util.LoggedClass):
|
|||||||
async def main_loop(self):
|
async def main_loop(self):
|
||||||
'''Controller main loop.'''
|
'''Controller main loop.'''
|
||||||
if self.env.rpc_port is not None:
|
if self.env.rpc_port is not None:
|
||||||
await self.start_server('RPC', ('127.0.0.1', '::1'),
|
await self.start_server('RPC', self.env.cs_host(for_rpc=True),
|
||||||
self.env.rpc_port)
|
self.env.rpc_port)
|
||||||
self.ensure_future(self.bp.main_loop())
|
self.ensure_future(self.bp.main_loop())
|
||||||
self.ensure_future(self.wait_for_bp_catchup())
|
self.ensure_future(self.wait_for_bp_catchup())
|
||||||
@ -292,7 +292,7 @@ class Controller(util.LoggedClass):
|
|||||||
self.state = self.LISTENING
|
self.state = self.LISTENING
|
||||||
|
|
||||||
env = self.env
|
env = self.env
|
||||||
host = env.cs_host()
|
host = env.cs_host(for_rpc=False)
|
||||||
if env.tcp_port is not None:
|
if env.tcp_port is not None:
|
||||||
await self.start_server('TCP', host, env.tcp_port)
|
await self.start_server('TCP', host, env.tcp_port)
|
||||||
if env.ssl_port is not None:
|
if env.ssl_port is not None:
|
||||||
@ -317,7 +317,8 @@ class Controller(util.LoggedClass):
|
|||||||
self.header_cache.clear()
|
self.header_cache.clear()
|
||||||
|
|
||||||
# Make a copy; self.sessions can change whilst await-ing
|
# Make a copy; self.sessions can change whilst await-ing
|
||||||
sessions = [s for s in self.sessions if isinstance(s, self.coin.SESSIONCLS)]
|
sessions = [s for s in self.sessions
|
||||||
|
if isinstance(s, self.coin.SESSIONCLS)]
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
await session.notify(self.bp.db_height, touched)
|
await session.notify(self.bp.db_height, touched)
|
||||||
|
|
||||||
|
|||||||
@ -31,8 +31,8 @@ class Env(lib_util.LoggedClass):
|
|||||||
self.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK'])
|
self.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK'])
|
||||||
self.db_dir = self.required('DB_DIRECTORY')
|
self.db_dir = self.required('DB_DIRECTORY')
|
||||||
self.daemon_url = self.required('DAEMON_URL')
|
self.daemon_url = self.required('DAEMON_URL')
|
||||||
coin_name = self.required('COIN')
|
coin_name = self.required('COIN').strip()
|
||||||
network = self.default('NET', 'mainnet')
|
network = self.default('NET', 'mainnet').strip()
|
||||||
self.coin = Coin.lookup_coin_class(coin_name, network)
|
self.coin = Coin.lookup_coin_class(coin_name, network)
|
||||||
self.cache_MB = self.integer('CACHE_MB', 1200)
|
self.cache_MB = self.integer('CACHE_MB', 1200)
|
||||||
self.host = self.default('HOST', 'localhost')
|
self.host = self.default('HOST', 'localhost')
|
||||||
@ -43,6 +43,7 @@ class Env(lib_util.LoggedClass):
|
|||||||
if self.ssl_port:
|
if self.ssl_port:
|
||||||
self.ssl_certfile = self.required('SSL_CERTFILE')
|
self.ssl_certfile = self.required('SSL_CERTFILE')
|
||||||
self.ssl_keyfile = self.required('SSL_KEYFILE')
|
self.ssl_keyfile = self.required('SSL_KEYFILE')
|
||||||
|
self.rpc_host = self.default('RPC_HOST', 'localhost')
|
||||||
self.rpc_port = self.integer('RPC_PORT', 8000)
|
self.rpc_port = self.integer('RPC_PORT', 8000)
|
||||||
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
|
self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000)
|
||||||
self.banner_file = self.default('BANNER_FILE', None)
|
self.banner_file = self.default('BANNER_FILE', None)
|
||||||
@ -184,11 +185,20 @@ class Env(lib_util.LoggedClass):
|
|||||||
return uvloop.EventLoopPolicy()
|
return uvloop.EventLoopPolicy()
|
||||||
raise self.Error('unknown event loop policy "{}"'.format(policy))
|
raise self.Error('unknown event loop policy "{}"'.format(policy))
|
||||||
|
|
||||||
def cs_host(self):
|
def cs_host(self, *, for_rpc):
|
||||||
'''Returns the 'host' argument to pass to asyncio's create_server
|
'''Returns the 'host' argument to pass to asyncio's create_server
|
||||||
call. The result can be a single host name string, a list of
|
call. The result can be a single host name string, a list of
|
||||||
host name strings, or an empty string to bind to all interfaces.'''
|
host name strings, or an empty string to bind to all interfaces.
|
||||||
result = self.host.split(',')
|
|
||||||
|
If rpc is True the host to use for the RPC server is returned.
|
||||||
|
Otherwise the host to use for SSL/TCP servers is returned.
|
||||||
|
'''
|
||||||
|
host = self.rpc_host if for_rpc else self.host
|
||||||
|
result = [part.strip() for part in host.split(',')]
|
||||||
if len(result) == 1:
|
if len(result) == 1:
|
||||||
result = result[0]
|
result = result[0]
|
||||||
|
# An empty result indicates all interfaces, which is not
|
||||||
|
# permitted for the RPC server.
|
||||||
|
if for_rpc and not result:
|
||||||
|
result = 'localhost'
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -524,7 +524,7 @@ class PeerManager(util.LoggedClass):
|
|||||||
|
|
||||||
# Use our listening Host/IP for outgoing connections so our
|
# Use our listening Host/IP for outgoing connections so our
|
||||||
# peers see the correct source.
|
# peers see the correct source.
|
||||||
host = self.env.cs_host()
|
host = self.env.cs_host(for_rpc=False)
|
||||||
if isinstance(host, list):
|
if isinstance(host, list):
|
||||||
host = host[0]
|
host = host[0]
|
||||||
local_addr = (host, None) if host else None
|
local_addr = (host, None) if host else None
|
||||||
|
|||||||
@ -82,8 +82,11 @@ def test_COIN_NET():
|
|||||||
os.environ['NET'] = 'testnet'
|
os.environ['NET'] = 'testnet'
|
||||||
e = Env()
|
e = Env()
|
||||||
assert e.coin == lib_coins.BitcoinCashTestnet
|
assert e.coin == lib_coins.BitcoinCashTestnet
|
||||||
|
os.environ['NET'] = ' testnet '
|
||||||
|
e = Env()
|
||||||
|
assert e.coin == lib_coins.BitcoinCashTestnet
|
||||||
os.environ.pop('NET')
|
os.environ.pop('NET')
|
||||||
os.environ['COIN'] = 'Litecoin'
|
os.environ['COIN'] = ' Litecoin '
|
||||||
e = Env()
|
e = Env()
|
||||||
assert e.coin == lib_coins.Litecoin
|
assert e.coin == lib_coins.Litecoin
|
||||||
os.environ['NET'] = 'testnet'
|
os.environ['NET'] = 'testnet'
|
||||||
@ -97,10 +100,23 @@ def test_HOST():
|
|||||||
assert_default('HOST', 'host', 'localhost')
|
assert_default('HOST', 'host', 'localhost')
|
||||||
os.environ['HOST'] = ''
|
os.environ['HOST'] = ''
|
||||||
e = Env()
|
e = Env()
|
||||||
assert e.cs_host() == ''
|
assert e.cs_host(for_rpc=False) == ''
|
||||||
os.environ['HOST'] = '192.168.0.1,23.45.67.89'
|
os.environ['HOST'] = '192.168.0.1,23.45.67.89'
|
||||||
e = Env()
|
e = Env()
|
||||||
assert e.cs_host() == ['192.168.0.1', '23.45.67.89']
|
assert e.cs_host(for_rpc=False) == ['192.168.0.1', '23.45.67.89']
|
||||||
|
os.environ['HOST'] = '192.168.0.1 , 23.45.67.89 '
|
||||||
|
e = Env()
|
||||||
|
assert e.cs_host(for_rpc=False) == ['192.168.0.1', '23.45.67.89']
|
||||||
|
|
||||||
|
def test_RPC_HOST():
|
||||||
|
assert_default('RPC_HOST', 'rpc_host', 'localhost')
|
||||||
|
os.environ['RPC_HOST'] = ''
|
||||||
|
e = Env()
|
||||||
|
# Blank reverts to localhost
|
||||||
|
assert e.cs_host(for_rpc=True) == 'localhost'
|
||||||
|
os.environ['RPC_HOST'] = '127.0.0.1, ::1'
|
||||||
|
e = Env()
|
||||||
|
assert e.cs_host(for_rpc=True) == ['127.0.0.1', '::1']
|
||||||
|
|
||||||
def test_REORG_LIMIT():
|
def test_REORG_LIMIT():
|
||||||
assert_integer('REORG_LIMIT', 'reorg_limit',
|
assert_integer('REORG_LIMIT', 'reorg_limit',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user