electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 4512f9d6d87fc8778d574b4c5cf901fe946d4ea0
parent 1dc3100ba3b179d395ce12a3a2cd07654f00716a
Author: ThomasV <thomasv@electrum.org>
Date:   Mon,  6 Apr 2020 16:56:34 +0200

Merge pull request #6070 from spesmilo/channel_save_seed2

Save channel seed in localconfig
Diffstat:
Melectrum/lnpeer.py | 31+++++++++----------------------
Melectrum/lnutil.py | 32++++++++++++++++++++++++--------
Melectrum/tests/test_lnchannel.py | 1+
Melectrum/wallet_db.py | 11++++++++++-
4 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py @@ -20,7 +20,6 @@ import aiorpcx from .crypto import sha256, sha256d from . import bitcoin -from .bip32 import BIP32Node from . import ecc from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string, der_sig_from_sig_string from . import constants @@ -484,38 +483,26 @@ class Peer(Logger): return bool(self.features & LnFeatures.OPTION_STATIC_REMOTEKEY_OPT) def make_local_config(self, funding_sat: int, push_msat: int, initiator: HTLCOwner) -> LocalConfig: - # key derivation - seed = os.urandom(32) - node = BIP32Node.from_rootseed(seed, xtype='standard') - keypair_generator = lambda family: generate_keypair(node, family) - - if initiator == LOCAL: - initial_msat = funding_sat * 1000 - push_msat - else: - initial_msat = push_msat - + channel_seed = os.urandom(32) + initial_msat = funding_sat * 1000 - push_msat if initiator == LOCAL else push_msat if self.is_static_remotekey(): + # Note: in the future, if a CSV delay is added, + # we will want to derive that key wallet = self.lnworker.wallet assert wallet.txin_type == 'p2wpkh' addr = wallet.get_unused_address() - static_key = wallet.get_public_key(addr) # just a pubkey - payment_basepoint = OnlyPubkeyKeypair(bfh(static_key)) + static_remotekey = bfh(wallet.get_public_key(addr)) else: - payment_basepoint = keypair_generator(LnKeyFamily.PAYMENT_BASE) - - local_config=LocalConfig( - payment_basepoint=payment_basepoint, - multisig_key=keypair_generator(LnKeyFamily.MULTISIG), - htlc_basepoint=keypair_generator(LnKeyFamily.HTLC_BASE), - delayed_basepoint=keypair_generator(LnKeyFamily.DELAY_BASE), - revocation_basepoint=keypair_generator(LnKeyFamily.REVOCATION_BASE), + static_remotekey = None + local_config = LocalConfig.from_seed( + channel_seed=channel_seed, + static_remotekey=static_remotekey, to_self_delay=DEFAULT_TO_SELF_DELAY, dust_limit_sat=546, max_htlc_value_in_flight_msat=funding_sat * 1000, max_accepted_htlcs=5, initial_msat=initial_msat, reserve_sat=546, - per_commitment_secret_seed=keypair_generator(LnKeyFamily.REVOCATION_ROOT).privkey, funding_locked_received=False, was_announced=False, current_commitment_signature=None, diff --git a/electrum/lnutil.py b/electrum/lnutil.py @@ -23,7 +23,7 @@ from .bitcoin import push_script, redeem_script_to_address, address_to_script from . import segwit_addr from .i18n import _ from .lnaddr import lndecode -from .bip32 import BIP32Node +from .bip32 import BIP32Node, BIP32_PRIME if TYPE_CHECKING: from .lnchannel import Channel @@ -77,11 +77,27 @@ class Config(StoredObject): @attr.s class LocalConfig(Config): - per_commitment_secret_seed = attr.ib(type=bytes, converter=hex_to_bytes) + channel_seed = attr.ib(type=bytes, converter=hex_to_bytes) # type: Optional[bytes] funding_locked_received = attr.ib(type=bool) was_announced = attr.ib(type=bool) current_commitment_signature = attr.ib(type=bytes, converter=hex_to_bytes) current_htlc_signatures = attr.ib(type=bytes, converter=hex_to_bytes) + per_commitment_secret_seed = attr.ib(type=bytes, converter=hex_to_bytes) + + @classmethod + def from_seed(self, **kwargs): + channel_seed = kwargs['channel_seed'] + static_remotekey = kwargs.pop('static_remotekey') + node = BIP32Node.from_rootseed(channel_seed, xtype='standard') + keypair_generator = lambda family: generate_keypair(node, family) + kwargs['per_commitment_secret_seed'] = keypair_generator(LnKeyFamily.REVOCATION_ROOT).privkey + kwargs['multisig_key'] = keypair_generator(LnKeyFamily.MULTISIG) + kwargs['htlc_basepoint'] = keypair_generator(LnKeyFamily.HTLC_BASE) + kwargs['delayed_basepoint'] = keypair_generator(LnKeyFamily.DELAY_BASE) + kwargs['revocation_basepoint'] = keypair_generator(LnKeyFamily.REVOCATION_BASE) + kwargs['payment_basepoint'] = OnlyPubkeyKeypair(static_remotekey) if static_remotekey else keypair_generator(LnKeyFamily.PAYMENT_BASE) + return LocalConfig(**kwargs) + @attr.s class RemoteConfig(Config): @@ -1024,12 +1040,12 @@ def extract_nodeid(connect_contents: str) -> Tuple[bytes, str]: # key derivation # see lnd/keychain/derivation.go class LnKeyFamily(IntEnum): - MULTISIG = 0 - REVOCATION_BASE = 1 - HTLC_BASE = 2 - PAYMENT_BASE = 3 - DELAY_BASE = 4 - REVOCATION_ROOT = 5 + MULTISIG = 0 | BIP32_PRIME + REVOCATION_BASE = 1 | BIP32_PRIME + HTLC_BASE = 2 | BIP32_PRIME + PAYMENT_BASE = 3 | BIP32_PRIME + DELAY_BASE = 4 | BIP32_PRIME + REVOCATION_ROOT = 5 | BIP32_PRIME NODE_KEY = 6 diff --git a/electrum/tests/test_lnchannel.py b/electrum/tests/test_lnchannel.py @@ -70,6 +70,7 @@ def create_channel_state(funding_txid, funding_index, funding_sat, is_initiator, current_per_commitment_point=cur, ), "local_config":lnpeer.LocalConfig( + channel_seed = None, payment_basepoint=privkeys[0], multisig_key=privkeys[1], htlc_basepoint=privkeys[2], diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py @@ -50,7 +50,7 @@ if TYPE_CHECKING: OLD_SEED_VERSION = 4 # electrum versions < 2.0 NEW_SEED_VERSION = 11 # electrum versions >= 2.0 -FINAL_SEED_VERSION = 27 # electrum >= 2.7 will set this to prevent +FINAL_SEED_VERSION = 28 # electrum >= 2.7 will set this to prevent # old versions from overwriting new format @@ -173,6 +173,7 @@ class WalletDB(JsonDB): self._convert_version_25() self._convert_version_26() self._convert_version_27() + self._convert_version_28() self.put('seed_version', FINAL_SEED_VERSION) # just to be sure self._after_upgrade_tasks() @@ -596,6 +597,14 @@ class WalletDB(JsonDB): c['local_config']['htlc_minimum_msat'] = 1 self.data['seed_version'] = 27 + def _convert_version_28(self): + if not self._is_upgrade_method_needed(27, 27): + return + channels = self.data.get('channels', {}) + for channel_id, c in channels.items(): + c['local_config']['channel_seed'] = None + self.data['seed_version'] = 28 + def _convert_imported(self): if not self._is_upgrade_method_needed(0, 13): return