1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-03-12 19:01:39 +01:00

Derive per-user swap-in keys

We derive keys based on the server xpub and our node ID.
This commit is contained in:
t-bast 2023-06-15 12:12:45 +02:00
parent 98d588ab03
commit 72f0993a31
No known key found for this signature in database
GPG key ID: 34F377B0100ED6BB
2 changed files with 27 additions and 10 deletions

View file

@ -25,6 +25,7 @@ import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.transactions.Transactions.TransactionWithInputInfo
import fr.acinq.eclair.{Features, ShortChannelId, secureRandom}
import scodec.bits.ByteVector
import scodec.codecs.uint16
object LocalKeyManager {
def channelKeyBasePath(chainHash: ByteVector32) = (chainHash: @unchecked) match {
@ -158,7 +159,8 @@ class LocalKeyManager(seed: ByteVector, chainHash: ByteVector32) extends KeyMana
Announcements.signChannelAnnouncement(chainHash, shortChannelId, localNodeSecret, remoteNodeId, localFundingPrivKey, remoteFundingKey, features)
}
def multisigSwapInAddress(serverPublicKey: Crypto.PublicKey, refundDelay: Int): String = {
def multisigSwapInAddress(localNodeId: PublicKey, serverExtendedPublicKey: String, refundDelay: Int): String = {
val serverPublicKey = deriveSwapInServerPublicKey(localNodeId, serverExtendedPublicKey)
val userKeyPath = chainHash match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash =>
DeterministicWallet.hardened(51) :: DeterministicWallet.hardened(0) :: DeterministicWallet.hardened(0) :: Nil
@ -179,4 +181,11 @@ class LocalKeyManager(seed: ByteVector, chainHash: ByteVector32) extends KeyMana
val address = Bech32.encodeWitnessAddress(hrp, 0, witnessScript)
address
}
private def deriveSwapInServerPublicKey(localNodeId: PublicKey, serverExtendedPublicKey: String): PublicKey = {
val (_, xpub) = DeterministicWallet.ExtendedPublicKey.decode(serverExtendedPublicKey)
val h = Crypto.sha256(localNodeId.value)
val path = h.bits.grouped(16).toSeq.map(uint16.decode(_).require.value.toLong)
DeterministicWallet.derivePublicKey(xpub, path).publicKey
}
}

View file

@ -77,7 +77,7 @@ class LocalKeyManagerSpec extends AnyFunSuite {
}
def makefundingKeyPath(entropy: ByteVector, isFunder: Boolean) = {
val items = for(i <- 0 to 7) yield entropy.drop(i * 4).take(4).toInt(signed = false) & 0xFFFFFFFFL
val items = for (i <- 0 to 7) yield entropy.drop(i * 4).take(4).toInt(signed = false) & 0xFFFFFFFFL
val last = DeterministicWallet.hardened(if (isFunder) 1L else 0L)
KeyPath(items :+ last)
}
@ -151,14 +151,22 @@ class LocalKeyManagerSpec extends AnyFunSuite {
}
test("generate multisig swap-in address") {
val entropy = ByteVector32.fromValidHex("0101010101010101010101010101010101010101010101010101010101010101")
val seed = MnemonicCode.toSeed(MnemonicCode.toMnemonics(entropy), "").take(32)
val keyManager = new LocalKeyManager(seed, Block.RegtestGenesisBlock.hash)
val serverPublicKey = PublicKey(ByteVector.fromValidHex("02cd0e2ed9c42af42e0b30e2a0b339c8335bbdc1f895fe552d8e224aedc82d6c88"))
val aliceKeyManager = {
val entropy = ByteVector32.fromValidHex("0101010101010101010101010101010101010101010101010101010101010101")
val seed = MnemonicCode.toSeed(MnemonicCode.toMnemonics(entropy), "").take(32)
new LocalKeyManager(seed, Block.RegtestGenesisBlock.hash)
}
val bobKeyManager = {
val entropy = ByteVector32.fromValidHex("0202020202020202020202020202020202020202020202020202020202020202")
val seed = MnemonicCode.toSeed(MnemonicCode.toMnemonics(entropy), "").take(32)
new LocalKeyManager(seed, Block.RegtestGenesisBlock.hash)
}
val swapInRefundDelay = 144 * 30 * 6
val swapInAddress = keyManager.multisigSwapInAddress(serverPublicKey, swapInRefundDelay)
assert(swapInAddress == "bcrt1qvwc4zcelvlj3pcy97pj09dz2hgq0ptav25nrjm54dt3ch09plxnq6pmjje")
val bobSwapInServerXpub = "tpubDDt5vQap1awkyDXx1z1cP7QFKSZHDCCpbU8nSq9jy7X2grTjUVZDePexf6gc6AHtRRzkgfPW87K6EKUVV6t3Hu2hg7YkHkmMeLSfrP85x41"
val swapInAddressAlice = aliceKeyManager.multisigSwapInAddress(aliceKeyManager.kmpNodeKey.publicKey, bobSwapInServerXpub, swapInRefundDelay)
assert(swapInAddressAlice == "bcrt1qw78cdcsn55vwsvmwe9qgwnx0fwffzqej7keuqfjnwj5xm0f5u6js2hp66f")
val aliceSwapInServerXpub = "tpubDCvYeHUZisCMVTSfWDa1yevTf89NeF6TWxXUQwqkcmFrNvNdNvZQh1j4m4uTA4QcmPEwcrKVF8bJih1v16zDZacRr4j9MCAFQoSydKKy66q"
val swapInAddressBob = bobKeyManager.multisigSwapInAddress(bobKeyManager.kmpNodeKey.publicKey, aliceSwapInServerXpub, swapInRefundDelay)
assert(swapInAddressBob == "bcrt1qjs2l2ey9rk742hvv25kjghqvqdyhvf7vshwesgflzch9kcq2c8lqh60h44")
}
}