Latest upstream changes

This commit is contained in:
Vivek Teega 2020-01-29 23:12:16 +00:00
parent 5fbb1d2dd2
commit 884208d93c
11 changed files with 232 additions and 398 deletions

View File

@ -1,19 +1,30 @@
Metadata-Version: 1.2
Metadata-Version: 2.1
Name: electrumX
Version: 1.8.7
Version: 1.13.0
Summary: ElectrumX Server
Home-page: https://github.com/kyuupichan/electrumx
Author: Neil Booth
Author-email: kyuupichan@gmail.com
License: MIT Licence
Download-URL: https://github.com/kyuupichan/electrumX/archive/1.8.7.tar.gz
Download-URL: https://github.com/kyuupichan/electrumX/archive/1.13.0.tar.gz
Description: Server implementation for the Electrum protocol
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Unix
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Database
Classifier: Topic :: Internet
Requires-Python: >=3.6
Requires-Python: >=3.7
Provides-Extra: rocksdb
Provides-Extra: uvloop
Provides-Extra: blake256
Provides-Extra: crypto
Provides-Extra: groestl
Provides-Extra: tribus-hash
Provides-Extra: xevan-hash
Provides-Extra: x11-hash
Provides-Extra: zny-yespower-0-5
Provides-Extra: bell-yespower
Provides-Extra: cpupower

View File

@ -1,4 +1,5 @@
README.rst
electrumx_compact_history
electrumx_rpc
electrumx_server
setup.py
@ -19,6 +20,8 @@ electrumx/lib/script.py
electrumx/lib/server_base.py
electrumx/lib/text.py
electrumx/lib/tx.py
electrumx/lib/tx_axe.py
electrumx/lib/tx_dash.py
electrumx/lib/util.py
electrumx/server/__init__.py
electrumx/server/block_processor.py

View File

@ -1,5 +1,38 @@
aiorpcX<0.9,>=0.8.1
aiorpcX[ws]<0.19,>=0.18.3
attrs
plyvel
pylru
aiohttp>=2
aiohttp>=3.3
[bell-yespower]
bell-yespower
[blake256]
blake256>=0.1.1
[cpupower]
cpupower
[crypto]
pycryptodomex>=3.8.1
[groestl]
groestlcoin-hash>=1.0.1
[rocksdb]
python-rocksdb>=0.6.9
[tribus-hash]
tribus-hash>=1.0.2
[uvloop]
uvloop>=0.12.2
[x11-hash]
x11-hash>=1.4
[xevan-hash]
xevan-hash
[zny-yespower-0-5]
zny-yespower-0-5

View File

@ -1,4 +1,4 @@
version = 'ElectrumX 1.13.0'
version = 'ElectrumX 1.14.0'
version_short = version.split()[-1]
from electrumx.server.controller import Controller

View File

@ -86,6 +86,7 @@ class Coin(object):
DECODE_CHECK = Base58.decode_check
GENESIS_HASH = ('000000000019d6689c085ae165831e93'
'4ff763ae46a2a6c172b3f1b60a8ce26f')
GENESIS_ACTIVATION = 100_000_000
# Peer discovery
PEER_DEFAULT_PORTS = {'t': '50001', 's': '50002'}
PEERS = []
@ -146,13 +147,7 @@ class Coin(object):
@classmethod
def hashX_from_script(cls, script):
'''Returns a hashX from a script, or None if the script is provably
unspendable so the output can be dropped.
'''
prefix = script[:2]
# Match a prefix of OP_RETURN or (OP_FALSE, OP_RETURN)
if prefix == b'\x00\x6a' or (prefix and prefix[0] == 0x6a):
return None
'''Returns a hashX from a script.'''
return sha256(script).digest()[:HASHX_LEN]
@staticmethod
@ -549,6 +544,7 @@ class BitcoinSV(BitcoinMixin, Coin):
'sv.jochen-hoenicke.de s t',
'sv.satoshi.io s t',
]
GENESIS_ACTIVATION = 620_538
class BitcoinCash(BitcoinMixin, Coin):
@ -777,6 +773,7 @@ class BitcoinSVTestnet(BitcoinTestnetMixin, Coin):
PEERS = [
'electrontest.cascharia.com t51001 s51002',
]
GENESIS_ACTIVATION = 1_344_302
class BitcoinSVScalingTestnet(BitcoinSVTestnet):
@ -787,6 +784,7 @@ class BitcoinSVScalingTestnet(BitcoinSVTestnet):
TX_COUNT = 2015
TX_COUNT_HEIGHT = 5711
TX_PER_BLOCK = 5000
GENESIS_ACTIVATION = 14_896
@classmethod
def max_fetch_blocks(cls, height):
@ -824,6 +822,7 @@ class BitcoinSVRegtest(BitcoinSVTestnet):
PEERS = []
TX_COUNT = 1
TX_COUNT_HEIGHT = 1
GENESIS_ACTIVATION = 10_000
class BitcoinSegwitTestnet(BitcoinTestnetMixin, Coin):
@ -925,16 +924,6 @@ class LitecoinTestnet(Litecoin):
'electrum.ltc.xurious.com s t',
]
class LitecoinRegtest(LitecoinTestnet):
NET = "regtest"
GENESIS_HASH = ('530827f38f93b43ed12af0b3ad25a288'
'dc02ed74d6d7857862df51fc56c416f9')
PEERS = []
TX_COUNT = 1
TX_COUNT_HEIGHT = 1
class Flo(Coin):
NAME = "FLO"
SHORTNAME = "FLO"
@ -973,6 +962,15 @@ class FloTestnet(Flo):
PEERS = []
class LitecoinRegtest(LitecoinTestnet):
NET = "regtest"
GENESIS_HASH = ('530827f38f93b43ed12af0b3ad25a288'
'dc02ed74d6d7857862df51fc56c416f9')
PEERS = []
TX_COUNT = 1
TX_COUNT_HEIGHT = 1
class BitcoinCashRegtest(BitcoinTestnetMixin, Coin):
NAME = "BitcoinCashABC" # Some releases later remove the ABC suffix
NET = "regtest"
@ -3399,3 +3397,30 @@ class Myce(Coin):
return scrypt.hash(header, header, 1024, 1, 1, 32)
else:
return double_sha256(header)
class Navcoin(Coin):
NAME = "Navcoin"
SHORTNAME = "NAV"
NET = "mainnet"
XPUB_VERBYTES = bytes.fromhex("0488b21e")
XPRV_VERBYTES = bytes.fromhex("0488ade4")
P2PKH_VERBYTE = bytes.fromhex("35")
P2SH_VERBYTES = [bytes.fromhex("55")]
WIF_BYTE = bytes.fromhex("96")
GENESIS_HASH = ('00006a4e3e18c71c6d48ad6c261e2254'
'fa764cf29607a4357c99b712dfbb8e6a')
DESERIALIZER = lib_tx.DeserializerTxTimeSegWitNavCoin
TX_COUNT = 137641
TX_COUNT_HEIGHT = 3649662
TX_PER_BLOCK = 2
RPC_PORT = 44444
REORG_LIMIT = 1000
@classmethod
def header_hash(cls, header):
if int.from_bytes(header[:4], "little") > 6:
return double_sha256(header)
else:
import x13_hash
return x13_hash.getPoWHash(header)

View File

@ -76,6 +76,16 @@ assert OpCodes.OP_CHECKSIG == 0xac
assert OpCodes.OP_CHECKMULTISIG == 0xae
def is_unspendable_legacy(script):
# OP_FALSE OP_RETURN or OP_RETURN
return script[:2] == b'\x00\x6a' or (script and script[0] == 0x6a)
def is_unspendable_genesis(script):
# OP_FALSE OP_RETURN
return script[:2] == b'\x00\x6a'
def _match_ops(ops, pattern):
if len(ops) != len(pattern):
return False

View File

@ -271,7 +271,6 @@ class DeserializerSegWit(Deserializer):
tx, _tx_hash, vsize = self._read_tx_parts()
return tx, vsize
class TxFlo(namedtuple("Tx", "version inputs outputs locktime txcomment")):
'''Class representing a transaction.'''
@ -346,8 +345,6 @@ class DeserializerFlo(DeserializerSegWit):
locktime, tx_comment), double_sha256(orig_ser), vsize
class DeserializerAuxPow(Deserializer):
VERSION_AUXPOW = (1 << 8)
@ -537,6 +534,82 @@ class DeserializerTxTimeSegWit(DeserializerTxTime):
return tx, vsize
class DeserializerTxTimeSegWitNavCoin(DeserializerTxTime):
def _read_witness(self, fields):
read_witness_field = self._read_witness_field
return [read_witness_field() for _ in range(fields)]
def _read_witness_field(self):
read_varbytes = self._read_varbytes
return [read_varbytes() for _ in range(self._read_varint())]
def read_tx_no_segwit(self):
version = self._read_le_int32()
time = self._read_le_uint32()
inputs = self._read_inputs()
outputs = self._read_outputs()
locktime = self._read_le_uint32()
strDZeel = ""
if version >= 2:
strDZeel = self._read_varbytes()
return TxTime(
version,
time,
inputs,
outputs,
locktime
)
def _read_tx_parts(self):
'''Return a (deserialized TX, tx_hash, vsize) tuple.'''
start = self.cursor
marker = self.binary[self.cursor + 8]
if marker:
tx = self.read_tx_no_segwit()
tx_hash = self.TX_HASH_FN(self.binary[start:self.cursor])
return tx, tx_hash, self.binary_length
version = self._read_le_int32()
time = self._read_le_uint32()
orig_ser = self.binary[start:self.cursor]
marker = self._read_byte()
flag = self._read_byte()
start = self.cursor
inputs = self._read_inputs()
outputs = self._read_outputs()
orig_ser += self.binary[start:self.cursor]
base_size = self.cursor - start
witness = self._read_witness(len(inputs))
start = self.cursor
locktime = self._read_le_uint32()
strDZeel = ""
if version >= 2:
strDZeel = self._read_varbytes()
vsize = (3 * base_size + self.binary_length) // 4
orig_ser += self.binary[start:self.cursor]
return TxTimeSegWit(
version, time, marker, flag, inputs, outputs, witness, locktime),\
self.TX_HASH_FN(orig_ser), vsize
def read_tx(self):
return self._read_tx_parts()[0]
def read_tx_and_hash(self):
tx, tx_hash, vsize = self._read_tx_parts()
return tx, tx_hash
def read_tx_and_vsize(self):
tx, tx_hash, vsize = self._read_tx_parts()
return tx, vsize
class TxTrezarcoin(
namedtuple("Tx", "version time inputs outputs locktime txcomment")):
'''Class representing transaction that has a time and txcomment field.'''

View File

@ -17,6 +17,7 @@ from aiorpcx import TaskGroup, run_in_thread
import electrumx
from electrumx.server.daemon import DaemonError
from electrumx.lib.hash import hash_to_hex_str, HASHX_LEN
from electrumx.lib.script import is_unspendable_legacy, is_unspendable_genesis
from electrumx.lib.util import (
chunks, class_logger, pack_le_uint32, pack_le_uint64, unpack_le_uint32
)
@ -385,10 +386,13 @@ class BlockProcessor(object):
'''
min_height = self.db.min_undo_height(self.daemon.cached_height())
height = self.height
genesis_activation = self.coin.GENESIS_ACTIVATION
for block in blocks:
height += 1
undo_info = self.advance_txs(block.transactions)
is_unspendable = (is_unspendable_genesis if height >= genesis_activation
else is_unspendable_legacy)
undo_info = self.advance_txs(block.transactions, is_unspendable)
if height >= min_height:
self.undo_infos.append((undo_info, height))
self.db.write_raw_block(block.raw, height)
@ -398,7 +402,7 @@ class BlockProcessor(object):
self.headers.extend(headers)
self.tip = self.coin.header_hash(headers[-1])
def advance_txs(self, txs):
def advance_txs(self, txs, is_unspendable):
self.tx_hashes.append(b''.join(tx_hash for tx, tx_hash in txs))
# Use local vars for speed in the loops
@ -429,12 +433,15 @@ class BlockProcessor(object):
# Add the new UTXOs
for idx, txout in enumerate(tx.outputs):
# Get the hashX. Ignore unspendable outputs
# Ignore unspendable outputs
if is_unspendable(txout.pk_script):
continue
# Get the hashX
hashX = script_hashX(txout.pk_script)
if hashX:
append_hashX(hashX)
put_utxo(tx_hash + to_le_uint32(idx),
hashX + tx_numb + to_le_uint64(txout.value))
append_hashX(hashX)
put_utxo(tx_hash + to_le_uint32(idx),
hashX + tx_numb + to_le_uint64(txout.value))
append_hashXs(hashXs)
update_touched(hashXs)
@ -455,6 +462,7 @@ class BlockProcessor(object):
'''
self.db.assert_flushed(self.flush_data())
assert self.height >= len(raw_blocks)
genesis_activation = self.coin.GENESIS_ACTIVATION
coin = self.coin
for raw_block in raw_blocks:
@ -467,13 +475,15 @@ class BlockProcessor(object):
hash_to_hex_str(self.tip),
self.height))
self.tip = coin.header_prevhash(block.header)
self.backup_txs(block.transactions)
is_unspendable = (is_unspendable_genesis if self.height >= genesis_activation
else is_unspendable_legacy)
self.backup_txs(block.transactions, is_unspendable)
self.height -= 1
self.db.tx_counts.pop()
self.logger.info('backed up to height {:,d}'.format(self.height))
def backup_txs(self, txs):
def backup_txs(self, txs, is_unspendable):
# Prevout values, in order down the block (coinbase first if present)
# undo_info is in reverse block order
undo_info = self.db.read_undo_info(self.height)
@ -493,10 +503,13 @@ class BlockProcessor(object):
for idx, txout in enumerate(tx.outputs):
# Spend the TX outputs. Be careful with unspendable
# outputs - we didn't save those in the first place.
if is_unspendable(txout.pk_script):
continue
# Get the hashX
hashX = script_hashX(txout.pk_script)
if hashX:
cache_value = spend_utxo(tx_hash, idx)
touched.add(cache_value[:-12])
cache_value = spend_utxo(tx_hash, idx)
touched.add(cache_value[:-12])
# Restore the inputs
for txin in reversed(tx.inputs):
@ -590,7 +603,7 @@ class BlockProcessor(object):
if len(candidates) > 1:
tx_num, = unpack_le_uint32(tx_num_packed)
hash, height = self.db.fs_tx_hash(tx_num)
hash, _height = self.db.fs_tx_hash(tx_num)
if hash != tx_hash:
assert hash is not None # Should always be found
continue
@ -691,8 +704,8 @@ class DecredBlockProcessor(BlockProcessor):
class NameIndexBlockProcessor(BlockProcessor):
def advance_txs(self, txs):
result = super().advance_txs(txs)
def advance_txs(self, txs, is_unspendable):
result = super().advance_txs(txs, is_unspendable)
tx_num = self.tx_count - len(txs)
script_name_hashX = self.coin.name_hashX_from_script
@ -722,7 +735,7 @@ class NameIndexBlockProcessor(BlockProcessor):
class LTORBlockProcessor(BlockProcessor):
def advance_txs(self, txs):
def advance_txs(self, txs, is_unspendable):
self.tx_hashes.append(b''.join(tx_hash for tx, tx_hash in txs))
# Use local vars for speed in the loops
@ -744,12 +757,15 @@ class LTORBlockProcessor(BlockProcessor):
tx_numb = to_le_uint32(tx_num)
for idx, txout in enumerate(tx.outputs):
# Get the hashX. Ignore unspendable outputs.
# Ignore unspendable outputs
if is_unspendable(txout.pk_script):
continue
# Get the hashX
hashX = script_hashX(txout.pk_script)
if hashX:
add_hashXs(hashX)
put_utxo(tx_hash + to_le_uint32(idx),
hashX + tx_numb + to_le_uint64(txout.value))
add_hashXs(hashX)
put_utxo(tx_hash + to_le_uint32(idx),
hashX + tx_numb + to_le_uint64(txout.value))
tx_num += 1
# Spend the inputs
@ -774,7 +790,7 @@ class LTORBlockProcessor(BlockProcessor):
return undo_info
def backup_txs(self, txs):
def backup_txs(self, txs, is_unspendable):
undo_info = self.db.read_undo_info(self.height)
if undo_info is None:
raise ChainError('no undo information found for height {:,d}'
@ -804,11 +820,14 @@ class LTORBlockProcessor(BlockProcessor):
# Remove tx outputs made in this block, by spending them.
for tx, tx_hash in txs:
for idx, txout in enumerate(tx.outputs):
hashX = script_hashX(txout.pk_script)
if hashX:
# Be careful with unspendable outputs- we didn't save those
# in the first place.
cache_value = spend_utxo(tx_hash, idx)
add_touched(cache_value[:-12])
# Spend the TX outputs. Be careful with unspendable
# outputs - we didn't save those in the first place.
if is_unspendable(txout.pk_script):
continue
# Get the hashX
hashX = script_hashX(txout.script)
cache_value = spend_utxo(tx_hash, idx)
add_touched(cache_value[:-12])
self.tx_count -= len(txs)

View File

@ -1,309 +0,0 @@
# Copyright (c) 2017, Neil Booth
#
# All rights reserved.
#
# See the file "LICENCE" for information about the copyright
# and warranty status of this software.
'''Logic for BIP32 Hierarchical Key Derviation.'''
import struct
import ecdsa
import ecdsa.ellipticcurve as EC
import ecdsa.numbertheory as NT
from electrumx.lib.coins import Coin
from electrumx.lib.hash import Base58, hmac_sha512, hash160
from electrumx.lib.util import cachedproperty, bytes_to_int, int_to_bytes, \
pack_be_uint32, unpack_be_uint32_from
class DerivationError(Exception):
'''Raised when an invalid derivation occurs.'''
class _KeyBase(object):
'''A BIP32 Key, public or private.'''
CURVE = ecdsa.SECP256k1
def __init__(self, chain_code, n, depth, parent):
if not isinstance(chain_code, (bytes, bytearray)):
raise TypeError('chain code must be raw bytes')
if len(chain_code) != 32:
raise ValueError('invalid chain code')
if not 0 <= n < 1 << 32:
raise ValueError('invalid child number')
if not 0 <= depth < 256:
raise ValueError('invalid depth')
if parent is not None:
if not isinstance(parent, type(self)):
raise TypeError('parent key has bad type')
self.chain_code = chain_code
self.n = n
self.depth = depth
self.parent = parent
def _hmac_sha512(self, msg):
'''Use SHA-512 to provide an HMAC, returned as a pair of 32-byte
objects.
'''
hmac = hmac_sha512(self.chain_code, msg)
return hmac[:32], hmac[32:]
def _extended_key(self, ver_bytes, raw_serkey):
'''Return the 78-byte extended key given prefix version bytes and
serialized key bytes.
'''
if not isinstance(ver_bytes, (bytes, bytearray)):
raise TypeError('ver_bytes must be raw bytes')
if len(ver_bytes) != 4:
raise ValueError('ver_bytes must have length 4')
if not isinstance(raw_serkey, (bytes, bytearray)):
raise TypeError('raw_serkey must be raw bytes')
if len(raw_serkey) != 33:
raise ValueError('raw_serkey must have length 33')
return (ver_bytes + bytes([self.depth])
+ self.parent_fingerprint() + pack_be_uint32(self.n)
+ self.chain_code + raw_serkey)
def fingerprint(self):
'''Return the key's fingerprint as 4 bytes.'''
return self.identifier()[:4]
def parent_fingerprint(self):
'''Return the parent key's fingerprint as 4 bytes.'''
return self.parent.fingerprint() if self.parent else bytes(4)
def extended_key_string(self, coin):
'''Return an extended key as a base58 string.'''
return Base58.encode_check(self.extended_key(coin))
class PubKey(_KeyBase):
'''A BIP32 public key.'''
def __init__(self, pubkey, chain_code, n, depth, parent=None):
super().__init__(chain_code, n, depth, parent)
if isinstance(pubkey, ecdsa.VerifyingKey):
self.verifying_key = pubkey
else:
self.verifying_key = self._verifying_key_from_pubkey(pubkey)
self.addresses = {}
@classmethod
def _verifying_key_from_pubkey(cls, pubkey):
'''Converts a 33-byte compressed pubkey into an ecdsa.VerifyingKey
object'''
if not isinstance(pubkey, (bytes, bytearray)):
raise TypeError('pubkey must be raw bytes')
if len(pubkey) != 33:
raise ValueError('pubkey must be 33 bytes')
if pubkey[0] not in (2, 3):
raise ValueError('invalid pubkey prefix byte')
curve = cls.CURVE.curve
is_odd = pubkey[0] == 3
x = bytes_to_int(pubkey[1:])
# p is the finite field order
a, b, p = curve.a(), curve.b(), curve.p()
y2 = pow(x, 3, p) + b
assert a == 0 # Otherwise y2 += a * pow(x, 2, p)
y = NT.square_root_mod_prime(y2 % p, p)
if bool(y & 1) != is_odd:
y = p - y
point = EC.Point(curve, x, y)
return ecdsa.VerifyingKey.from_public_point(point, curve=cls.CURVE)
@cachedproperty
def pubkey_bytes(self):
'''Return the compressed public key as 33 bytes.'''
point = self.verifying_key.pubkey.point
prefix = bytes([2 + (point.y() & 1)])
padded_bytes = _exponent_to_bytes(point.x())
return prefix + padded_bytes
def address(self, coin):
"The public key as a P2PKH address"
address = self.addresses.get(coin)
if not address:
address = coin.P2PKH_address_from_pubkey(self.pubkey_bytes)
self.addresses[coin] = address
return address
def ec_point(self):
return self.verifying_key.pubkey.point
def child(self, n):
'''Return the derived child extended pubkey at index N.'''
if not 0 <= n < (1 << 31):
raise ValueError('invalid BIP32 public key child number')
msg = self.pubkey_bytes + pack_be_uint32(n)
L, R = self._hmac_sha512(msg)
curve = self.CURVE
L = bytes_to_int(L)
if L >= curve.order:
raise DerivationError
point = curve.generator * L + self.ec_point()
if point == EC.INFINITY:
raise DerivationError
verkey = ecdsa.VerifyingKey.from_public_point(point, curve=curve)
return PubKey(verkey, R, n, self.depth + 1, self)
def identifier(self):
'''Return the key's identifier as 20 bytes.'''
return hash160(self.pubkey_bytes)
def extended_key(self, coin):
'''Return a raw extended public key.'''
return self._extended_key(coin.XPUB_VERBYTES, self.pubkey_bytes)
class PrivKey(_KeyBase):
'''A BIP32 private key.'''
HARDENED = 1 << 31
def __init__(self, privkey, chain_code, n, depth, parent=None):
super().__init__(chain_code, n, depth, parent)
if isinstance(privkey, ecdsa.SigningKey):
self.signing_key = privkey
else:
self.signing_key = self._signing_key_from_privkey(privkey)
@classmethod
def _signing_key_from_privkey(cls, privkey):
'''Converts a 32-byte privkey into an ecdsa.SigningKey object.'''
exponent = cls._privkey_secret_exponent(privkey)
return ecdsa.SigningKey.from_secret_exponent(exponent, curve=cls.CURVE)
@classmethod
def _privkey_secret_exponent(cls, privkey):
'''Return the private key as a secret exponent if it is a valid private
key.'''
if not isinstance(privkey, (bytes, bytearray)):
raise TypeError('privkey must be raw bytes')
if len(privkey) != 32:
raise ValueError('privkey must be 32 bytes')
exponent = bytes_to_int(privkey)
if not 1 <= exponent < cls.CURVE.order:
raise ValueError('privkey represents an invalid exponent')
return exponent
@classmethod
def from_seed(cls, seed):
# This hard-coded message string seems to be coin-independent...
hmac = hmac_sha512(b'Bitcoin seed', seed)
privkey, chain_code = hmac[:32], hmac[32:]
return cls(privkey, chain_code, 0, 0)
@cachedproperty
def privkey_bytes(self):
'''Return the serialized private key (no leading zero byte).'''
return _exponent_to_bytes(self.secret_exponent())
@cachedproperty
def public_key(self):
'''Return the corresponding extended public key.'''
verifying_key = self.signing_key.get_verifying_key()
parent_pubkey = self.parent.public_key if self.parent else None
return PubKey(verifying_key, self.chain_code, self.n, self.depth,
parent_pubkey)
def ec_point(self):
return self.public_key.ec_point()
def secret_exponent(self):
'''Return the private key as a secret exponent.'''
return self.signing_key.privkey.secret_multiplier
def WIF(self, coin):
'''Return the private key encoded in Wallet Import Format.'''
return coin.privkey_WIF(self.privkey_bytes, compressed=True)
def address(self, coin):
"The public key as a P2PKH address"
return self.public_key.address(coin)
def child(self, n):
'''Return the derived child extended privkey at index N.'''
if not 0 <= n < (1 << 32):
raise ValueError('invalid BIP32 private key child number')
if n >= self.HARDENED:
serkey = b'\0' + self.privkey_bytes
else:
serkey = self.public_key.pubkey_bytes
msg = serkey + pack_be_uint32(n)
L, R = self._hmac_sha512(msg)
curve = self.CURVE
L = bytes_to_int(L)
exponent = (L + bytes_to_int(self.privkey_bytes)) % curve.order
if exponent == 0 or L >= curve.order:
raise DerivationError
privkey = _exponent_to_bytes(exponent)
return PrivKey(privkey, R, n, self.depth + 1, self)
def identifier(self):
'''Return the key's identifier as 20 bytes.'''
return self.public_key.identifier()
def extended_key(self, coin):
'''Return a raw extended private key.'''
return self._extended_key(coin.XPRV_VERBYTES,
b'\0' + self.privkey_bytes)
def _exponent_to_bytes(exponent):
'''Convert an exponent to 32 big-endian bytes'''
return (bytes(32) + int_to_bytes(exponent))[-32:]
def _from_extended_key(ekey):
'''Return a PubKey or PrivKey from an extended key raw bytes.'''
if not isinstance(ekey, (bytes, bytearray)):
raise TypeError('extended key must be raw bytes')
if len(ekey) != 78:
raise ValueError('extended key must have length 78')
is_public, coin = Coin.lookup_xverbytes(ekey[:4])
depth = ekey[4]
fingerprint = ekey[5:9] # Not used
n, = unpack_be_uint32_from(ekey[9:13])
chain_code = ekey[13:45]
if is_public:
pubkey = ekey[45:]
key = PubKey(pubkey, chain_code, n, depth)
else:
if ekey[45] is not 0:
raise ValueError('invalid extended private key prefix byte')
privkey = ekey[46:]
key = PrivKey(privkey, chain_code, n, depth)
return key, coin
def from_extended_key_string(ekey_str):
'''Given an extended key string, such as
xpub6BsnM1W2Y7qLMiuhi7f7dbAwQZ5Cz5gYJCRzTNainXzQXYjFwtuQXHd
3qfi3t3KJtHxshXezfjft93w4UE7BGMtKwhqEHae3ZA7d823DVrL
return a (key, coin) pair. key is either a PubKey or PrivKey.
'''
return _from_extended_key(Base58.decode_check(ekey_str))

View File

@ -1,31 +0,0 @@
# Copyright (c) 2017, Neil Booth
#
# All rights reserved.
#
# See the file "LICENCE" for information about the copyright
# and warranty status of this software.
'''Class for handling environment configuration and defaults.'''
from electrumx.lib.env_base import EnvBase
class Env(EnvBase):
'''Wraps environment configuration.'''
def __init__(self):
super().__init__()
self.db_dir = self.required('DB_DIRECTORY')
self.db_engine = self.default('DB_ENGINE', 'leveldb')
self.ssl_port = self.integer('SSL_PORT', 20214)
self.ssl_certfile = self.required('SSL_CERTFILE')
self.ssl_keyfile = self.required('SSL_KEYFILE')
self.rpc_port = self.integer('RPC_PORT', 8001)
self.tor_proxy_host = self.default('TOR_PROXY_HOST', 'localhost')
self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None)
self.session_timeout = self.integer('SESSION_TIMEOUT', 600)

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3.7
#!/usr/bin/python3.7
#
# Copyright (c) 2016-2018, Neil Booth
#
@ -10,7 +10,7 @@
'''Script to kick off the server.'''
import asyncio
import loggingd
import logging
import sys
from electrumx import Controller, Env