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:
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