From e3dd65f194913a75f5d22387fb7778ca360ad300 Mon Sep 17 00:00:00 2001 From: sstone Date: Wed, 25 Sep 2019 09:49:12 +0200 Subject: [PATCH 1/3] Fix pubkey to channel key path computation Channel key path is generated from 8 bytes computed from our funding pubkey, but we extracted 4 uint32 values instead of 2 (last 2 were always 0). --- .../src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala index 6d80d46b1f..26eb544a52 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala @@ -114,7 +114,7 @@ object KeyManager { def channelKeyPath(fundingPubKey: PublicKey) : DeterministicWallet.KeyPath = { val buffer = fundingPubKey.hash160.take(8) val bis = new ByteArrayInputStream(buffer.toArray) - DeterministicWallet.KeyPath(Seq(Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN))) + DeterministicWallet.KeyPath(Seq(Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN))) } def channelKeyPath(fundingPubKey: DeterministicWallet.ExtendedPublicKey) : DeterministicWallet.KeyPath = channelKeyPath(fundingPubKey.publicKey) From 78dc4228c37d5b45bab9b672774009c5ff8a1c59 Mon Sep 17 00:00:00 2001 From: sstone Date: Wed, 25 Sep 2019 09:59:28 +0200 Subject: [PATCH 2/3] Add a channel key path compatibility test This test will fail if we change the way we compute channel key paths, which would break existing channels. --- .../fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala index 37a8dc367b..7ceb85de37 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala @@ -16,9 +16,9 @@ package fr.acinq.eclair.crypto -import fr.acinq.bitcoin.{Block, ByteVector32, DeterministicWallet} -import fr.acinq.bitcoin.Crypto.PublicKey +import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} import fr.acinq.bitcoin.DeterministicWallet.KeyPath +import fr.acinq.bitcoin.{Block, ByteVector32, DeterministicWallet} import org.scalatest.FunSuite import scodec.bits._ @@ -57,4 +57,12 @@ class LocalKeyManagerSpec extends FunSuite { assert(keyManager1.fundingPublicKey(keyPath) != keyManager2.fundingPublicKey(keyPath)) assert(keyManager1.commitmentPoint(keyPath, 1) != keyManager2.commitmentPoint(keyPath, 1)) } + + test("compute channel key path from funding keys") { + // if this test fails it means that we don't generate the same chanel key path from the same funding pubkey, which + // will break existing channels ! + val pub = PrivateKey(ByteVector32.fromValidHex("01" * 32)).publicKey + val keyPath = KeyManager.channelKeyPath(pub) + assert(keyPath.toString() == "m/2041577608/1982247572") + } } From fcbc6eb08820aebf6e358e2d2f91b6b23be4ec7c Mon Sep 17 00:00:00 2001 From: sstone Date: Wed, 25 Sep 2019 10:17:18 +0200 Subject: [PATCH 3/3] Use 128 bits to derive channel key paths --- .../src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala | 4 ++-- .../scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala index 26eb544a52..017dc44f45 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/crypto/KeyManager.scala @@ -112,9 +112,9 @@ object KeyManager { * @return a BIP32 path */ def channelKeyPath(fundingPubKey: PublicKey) : DeterministicWallet.KeyPath = { - val buffer = fundingPubKey.hash160.take(8) + val buffer = fundingPubKey.hash160.take(16) val bis = new ByteArrayInputStream(buffer.toArray) - DeterministicWallet.KeyPath(Seq(Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN))) + DeterministicWallet.KeyPath(Seq(Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN))) } def channelKeyPath(fundingPubKey: DeterministicWallet.ExtendedPublicKey) : DeterministicWallet.KeyPath = channelKeyPath(fundingPubKey.publicKey) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala index 7ceb85de37..fa2f872405 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/crypto/LocalKeyManagerSpec.scala @@ -59,10 +59,10 @@ class LocalKeyManagerSpec extends FunSuite { } test("compute channel key path from funding keys") { - // if this test fails it means that we don't generate the same chanel key path from the same funding pubkey, which + // if this test fails it means that we don't generate the same channel key path from the same funding pubkey, which // will break existing channels ! val pub = PrivateKey(ByteVector32.fromValidHex("01" * 32)).publicKey val keyPath = KeyManager.channelKeyPath(pub) - assert(keyPath.toString() == "m/2041577608/1982247572") + assert(keyPath.toString() == "m/2041577608/1982247572/689197082'/1288840885") } }