diff --git a/electrumX.egg-info/PKG-INFO b/electrumX.egg-info/PKG-INFO index afdd1d9..035eb03 100644 --- a/electrumX.egg-info/PKG-INFO +++ b/electrumX.egg-info/PKG-INFO @@ -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 diff --git a/electrumX.egg-info/SOURCES.txt b/electrumX.egg-info/SOURCES.txt index f473149..c0db713 100644 --- a/electrumX.egg-info/SOURCES.txt +++ b/electrumX.egg-info/SOURCES.txt @@ -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 diff --git a/electrumX.egg-info/requires.txt b/electrumX.egg-info/requires.txt index a3db38f..0e68774 100644 --- a/electrumX.egg-info/requires.txt +++ b/electrumX.egg-info/requires.txt @@ -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 diff --git a/electrumx/__init__.py b/electrumx/__init__.py index 8f40b44..0102877 100644 --- a/electrumx/__init__.py +++ b/electrumx/__init__.py @@ -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 diff --git a/electrumx/lib/coins.py b/electrumx/lib/coins.py index eb3489c..da92813 100644 --- a/electrumx/lib/coins.py +++ b/electrumx/lib/coins.py @@ -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) diff --git a/electrumx/lib/script.py b/electrumx/lib/script.py index c6467d6..2e7687c 100644 --- a/electrumx/lib/script.py +++ b/electrumx/lib/script.py @@ -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 diff --git a/electrumx/lib/tx.py b/electrumx/lib/tx.py index 847a2b9..5c31d05 100644 --- a/electrumx/lib/tx.py +++ b/electrumx/lib/tx.py @@ -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.''' diff --git a/electrumx/server/block_processor.py b/electrumx/server/block_processor.py index 7dc4376..c31672a 100644 --- a/electrumx/server/block_processor.py +++ b/electrumx/server/block_processor.py @@ -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) diff --git a/electrumx/wallet/bip32.py b/electrumx/wallet/bip32.py deleted file mode 100644 index 0d1ef95..0000000 --- a/electrumx/wallet/bip32.py +++ /dev/null @@ -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)) diff --git a/electrumx/wallet/env.py b/electrumx/wallet/env.py deleted file mode 100644 index 1d1b3a0..0000000 --- a/electrumx/wallet/env.py +++ /dev/null @@ -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) diff --git a/electrumx_server b/electrumx_server index 44e636e..cb068a3 100755 --- a/electrumx_server +++ b/electrumx_server @@ -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