Merge branch 'release-0.7.2'
This commit is contained in:
commit
35f56462d3
@ -100,7 +100,7 @@ might do::
|
|||||||
|
|
||||||
Then copy the all sample scripts from the ElectrumX source tree there::
|
Then copy the all sample scripts from the ElectrumX source tree there::
|
||||||
|
|
||||||
cp -R /path/to/repo/electrumx/samples/scripts ~/scripts/electrumx
|
cp -R /path/to/repo/electrumx/samples/daemontools ~/scripts/electrumx
|
||||||
|
|
||||||
This copies 3 things: the top level server run script, a log/ directory
|
This copies 3 things: the top level server run script, a log/ directory
|
||||||
with the logger run script, an env/ directory.
|
with the logger run script, an env/ directory.
|
||||||
@ -136,7 +136,7 @@ Using systemd
|
|||||||
This repository contains a sample systemd unit file that you can use to
|
This repository contains a sample systemd unit file that you can use to
|
||||||
setup ElectrumX with systemd. Simply copy it to :code:`/etc/systemd/system`::
|
setup ElectrumX with systemd. Simply copy it to :code:`/etc/systemd/system`::
|
||||||
|
|
||||||
cp samples/systemd-unit /etc/systemd/system/electrumx.service
|
cp samples/systemd/electrumx.service /etc/systemd/system/
|
||||||
|
|
||||||
The sample unit file assumes that the repository is located at
|
The sample unit file assumes that the repository is located at
|
||||||
:code:`/home/electrumx/electrumx`. If that differs on your system, you need to
|
:code:`/home/electrumx/electrumx`. If that differs on your system, you need to
|
||||||
|
|||||||
@ -358,6 +358,7 @@ class Dash(Coin):
|
|||||||
TX_COUNT_HEIGHT = 569399
|
TX_COUNT_HEIGHT = 569399
|
||||||
TX_COUNT = 2157510
|
TX_COUNT = 2157510
|
||||||
TX_PER_BLOCK = 4
|
TX_PER_BLOCK = 4
|
||||||
|
DEFAULT_RPC_PORT = 9998
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def header_hashes(cls, header):
|
def header_hashes(cls, header):
|
||||||
@ -379,3 +380,4 @@ class DashTestnet(Dash):
|
|||||||
TX_COUNT_HEIGHT = 101619
|
TX_COUNT_HEIGHT = 101619
|
||||||
TX_COUNT = 132681
|
TX_COUNT = 132681
|
||||||
TX_PER_BLOCK = 1
|
TX_PER_BLOCK = 1
|
||||||
|
DEFAULT_RPC_PORT = 19998
|
||||||
|
|||||||
@ -117,7 +117,6 @@ class JSONRPC(asyncio.Protocol, LoggedClass):
|
|||||||
message = message.decode()
|
message = message.decode()
|
||||||
except UnicodeDecodeError as e:
|
except UnicodeDecodeError as e:
|
||||||
msg = 'cannot decode binary bytes: {}'.format(e)
|
msg = 'cannot decode binary bytes: {}'.format(e)
|
||||||
self.logger.warning(msg)
|
|
||||||
self.send_json_error(msg, self.PARSE_ERROR)
|
self.send_json_error(msg, self.PARSE_ERROR)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -125,7 +124,6 @@ class JSONRPC(asyncio.Protocol, LoggedClass):
|
|||||||
message = json.loads(message)
|
message = json.loads(message)
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
msg = 'cannot decode JSON: {}'.format(e)
|
msg = 'cannot decode JSON: {}'.format(e)
|
||||||
self.logger.warning(msg)
|
|
||||||
self.send_json_error(msg, self.PARSE_ERROR)
|
self.send_json_error(msg, self.PARSE_ERROR)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -133,52 +131,49 @@ class JSONRPC(asyncio.Protocol, LoggedClass):
|
|||||||
|
|
||||||
def send_json_notification(self, method, params):
|
def send_json_notification(self, method, params):
|
||||||
'''Create a json notification.'''
|
'''Create a json notification.'''
|
||||||
return self.send_json(json_notification_payload(method, params))
|
self.send_json(json_notification_payload(method, params))
|
||||||
|
|
||||||
def send_json_result(self, result, id_):
|
def send_json_result(self, result, id_):
|
||||||
'''Send a JSON result.'''
|
'''Send a JSON result.'''
|
||||||
return self.send_json(json_result_payload(result, id_))
|
self.send_json(json_result_payload(result, id_))
|
||||||
|
|
||||||
def send_json_error(self, message, code, id_=None):
|
def send_json_error(self, message, code, id_=None):
|
||||||
'''Send a JSON error.'''
|
'''Send a JSON error.'''
|
||||||
|
self.send_json(json_error_payload(message, code, id_))
|
||||||
self.error_count += 1
|
self.error_count += 1
|
||||||
return self.send_json(json_error_payload(message, code, id_))
|
# Close abusive clients
|
||||||
|
if self.error_count >= 10:
|
||||||
|
self.transport.close()
|
||||||
|
|
||||||
def send_json(self, payload):
|
def send_json(self, payload):
|
||||||
'''Send a JSON payload.'''
|
'''Send a JSON payload.'''
|
||||||
|
# Confirmed this happens, sometimes a lot
|
||||||
if self.transport.is_closing():
|
if self.transport.is_closing():
|
||||||
# Confirmed this happens, sometimes a lot
|
return
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = (json.dumps(payload) + '\n').encode()
|
data = (json.dumps(payload) + '\n').encode()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
msg = 'JSON encoding failure: {}'.format(payload)
|
msg = 'JSON encoding failure: {}'.format(payload)
|
||||||
self.logger.error(msg)
|
self.logger.error(msg)
|
||||||
return self.send_json_error(msg, self.INTERNAL_ERROR,
|
self.send_json_error(msg, self.INTERNAL_ERROR, payload.get('id'))
|
||||||
payload.get('id'))
|
else:
|
||||||
|
self.send_count += 1
|
||||||
self.send_count += 1
|
self.send_size += len(data)
|
||||||
self.send_size += len(data)
|
self.transport.write(data)
|
||||||
self.transport.write(data)
|
|
||||||
return True
|
|
||||||
|
|
||||||
async def handle_json_request(self, request):
|
async def handle_json_request(self, request):
|
||||||
'''Asynchronously handle a JSON request.
|
'''Asynchronously handle a JSON request.
|
||||||
|
|
||||||
Handles batch requests. Returns True if the request response
|
Handles batch requests.
|
||||||
was sent (or if nothing was sent because the request was a
|
|
||||||
notification). Returns False if the send was aborted because
|
|
||||||
the connection is closing.
|
|
||||||
'''
|
'''
|
||||||
if isinstance(request, list):
|
if isinstance(request, list):
|
||||||
payload = await self.batch_request_payload(request)
|
payload = await self.batch_request_payload(request)
|
||||||
else:
|
else:
|
||||||
payload = await self.single_request_payload(request)
|
payload = await self.single_request_payload(request)
|
||||||
|
|
||||||
if not payload:
|
if payload:
|
||||||
return True
|
self.send_json(payload)
|
||||||
return self.send_json(payload)
|
|
||||||
|
|
||||||
async def batch_request_payload(self, batch):
|
async def batch_request_payload(self, batch):
|
||||||
'''Return the JSON payload corresponding to a batch JSON request.'''
|
'''Return the JSON payload corresponding to a batch JSON request.'''
|
||||||
|
|||||||
95
samples/systemd/electrumx.conf
Normal file
95
samples/systemd/electrumx.conf
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#suggested /etc/electrumx.conf for systemd
|
||||||
|
|
||||||
|
#
|
||||||
|
#REQUIRED
|
||||||
|
#
|
||||||
|
|
||||||
|
DB_DIRECTORY =
|
||||||
|
USERNAME = electrumx
|
||||||
|
ELECTRUMX = /usr/local/bin/electrumx_server.py
|
||||||
|
|
||||||
|
#Bitcoin Node RPC Credentials
|
||||||
|
DAEMON_URL = http://username:password@hostname:port/
|
||||||
|
#port is optional, defaults to COIN RPC default
|
||||||
|
|
||||||
|
#
|
||||||
|
#REQUIRED FOR PUBLIC VISIBILITY
|
||||||
|
#
|
||||||
|
|
||||||
|
#HOST = 0.0.0.0
|
||||||
|
#listen on interface, (0.0.0.0 is any)
|
||||||
|
#TCP_PORT = 50001
|
||||||
|
#SSL_PORT = 50002
|
||||||
|
#Requires
|
||||||
|
#SSL_CERTFILE = /path/to/server.crt
|
||||||
|
#SSL_KEYFILE = /path/to/server.key
|
||||||
|
|
||||||
|
#
|
||||||
|
#OPTIONAL VISIBILITY
|
||||||
|
#
|
||||||
|
|
||||||
|
#BANNER_FILE = /path/to/banner
|
||||||
|
#DONATION_ADDRESS =
|
||||||
|
#IRC = yes
|
||||||
|
#IRC_NICK =
|
||||||
|
#REPORT_HOST = $HOST
|
||||||
|
#REPORT_TCP_PORT = #defaults to TCP_PORT
|
||||||
|
#REPORT_SSL_PORT = #defaults to SSL_PORT
|
||||||
|
#RPC_PORT = 8000
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#MISC
|
||||||
|
#
|
||||||
|
#COIN = Bitcoin # lib/coins.py
|
||||||
|
#NETWORK = mainnet # lib/coins.py
|
||||||
|
#DB_ENGINE = leveldb
|
||||||
|
#leveldb, rocksdb, lmdb (You'll need to install appropriate python packages)
|
||||||
|
|
||||||
|
#REORG_LIMIT = 200
|
||||||
|
#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.
|
||||||
|
|
||||||
|
#ANON_LOGS =
|
||||||
|
#Set to anything non-empty to remove IP addresses from logs.
|
||||||
|
|
||||||
|
|
||||||
|
#These following environment variables are to help limit server
|
||||||
|
#resource consumption and to prevent simple DoS. Address subscriptions
|
||||||
|
#in ElectrumX are very cheap - they consume about 100 bytes of memory
|
||||||
|
#each and are processed efficiently. I feel the defaults are low and
|
||||||
|
#encourage you to raise them.
|
||||||
|
|
||||||
|
#MAX_SUBS = 250000
|
||||||
|
#Maximum number of address subscriptions across all sessions
|
||||||
|
#MAX_SESSION_SUBS = 50000
|
||||||
|
#Maximum number of address subscriptions permitted to a single session.
|
||||||
|
|
||||||
|
|
||||||
|
#If synchronizing from the Genesis block your performance might change
|
||||||
|
#by tweaking the following cache variables. Cache size is only checked
|
||||||
|
#roughly every minute, so the caches can grow beyond the specified
|
||||||
|
#size. Also the Python process is often quite a bit fatter than the
|
||||||
|
#combined cache size, because of Python overhead and also because
|
||||||
|
#leveldb consumes a lot of memory during UTXO flushing. So I recommend
|
||||||
|
#you set the sum of these to nothing over half your available physical
|
||||||
|
#RAM:
|
||||||
|
|
||||||
|
#HIST_MB = 300
|
||||||
|
#amount of history cache, in MB, to retain before flushing to
|
||||||
|
#disk. Default is 300; probably no benefit being much larger
|
||||||
|
#as history is append-only and not searched.
|
||||||
|
|
||||||
|
#UTXO_MB = 1000
|
||||||
|
#amount of UTXO and history cache, in MB, to retain before
|
||||||
|
#flushing to disk. Default is 1000. This may be too large
|
||||||
|
#for small boxes or too small for machines with lots of RAM.
|
||||||
|
#Larger caches generally perform better as there is
|
||||||
|
#significant searching of the UTXO cache during indexing.
|
||||||
|
#However, I don't see much benefit in my tests pushing this
|
||||||
|
#too high, and in fact performance begins to fall. My
|
||||||
|
#machine has 24GB RAM; the slow down is probably because of
|
||||||
|
#leveldb caching and Python GC effects. However this may be
|
||||||
|
#very dependent on hardware and you may have different
|
||||||
|
#results.
|
||||||
@ -99,7 +99,7 @@ class Daemon(util.LoggedClass):
|
|||||||
secs = 1
|
secs = 1
|
||||||
else:
|
else:
|
||||||
await asyncio.sleep(secs)
|
await asyncio.sleep(secs)
|
||||||
secs = min(16, secs * 2)
|
secs = min(max_secs, secs * 2)
|
||||||
|
|
||||||
def logged_url(self, url):
|
def logged_url(self, url):
|
||||||
'''The host and port part, for logging.'''
|
'''The host and port part, for logging.'''
|
||||||
|
|||||||
@ -93,7 +93,7 @@ class DB(LoggedClass):
|
|||||||
if self.db_version not in self.DB_VERSIONS:
|
if self.db_version not in self.DB_VERSIONS:
|
||||||
raise self.DBError('your DB version is {} but this software '
|
raise self.DBError('your DB version is {} but this software '
|
||||||
'only handles versions {}'
|
'only handles versions {}'
|
||||||
.format(db_version, self.DB_VERSIONS))
|
.format(self.db_version, self.DB_VERSIONS))
|
||||||
if state['genesis'] != self.coin.GENESIS_HASH:
|
if state['genesis'] != self.coin.GENESIS_HASH:
|
||||||
raise self.DBError('DB genesis hash {} does not match coin {}'
|
raise self.DBError('DB genesis hash {} does not match coin {}'
|
||||||
.format(state['genesis_hash'],
|
.format(state['genesis_hash'],
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
VERSION = "ElectrumX 0.7.1"
|
VERSION = "ElectrumX 0.7.2"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user