Skip to content

Commit

Permalink
add signChannelAnnouncement method to each KeyManager
Browse files Browse the repository at this point in the history
Signed-off-by: Donovan Jean <donovan@acinq.fr>
  • Loading branch information
Donovan Jean committed Nov 3, 2020
1 parent 4f88537 commit d7418a9
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 38 deletions.
13 changes: 12 additions & 1 deletion eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, FeeratePerKw, F
import fr.acinq.eclair.channel.Channel.REFRESH_CHANNEL_UPDATE_INTERVAL
import fr.acinq.eclair.crypto.{ChannelKeyManager, Generators}
import fr.acinq.eclair.db.ChannelsDb
import fr.acinq.eclair.router.Announcements
import fr.acinq.eclair.transactions.DirectedHtlc._
import fr.acinq.eclair.transactions.Scripts._
import fr.acinq.eclair.transactions.Transactions._
Expand Down Expand Up @@ -215,7 +216,17 @@ object Helpers {
def makeAnnouncementSignatures(nodeParams: NodeParams, commitments: Commitments, shortChannelId: ShortChannelId): AnnouncementSignatures = {
val features = Features.empty // empty features for now
val fundingPubKey = nodeParams.channelKeyManager.fundingPublicKey(commitments.localParams.fundingKeyPath)
val (localNodeSig, localBitcoinSig) = nodeParams.channelKeyManager.signChannelAnnouncement(nodeParams.privateKey, fundingPubKey.path, nodeParams.chainHash, shortChannelId, commitments.remoteParams.nodeId, commitments.remoteParams.fundingPubKey, features)
val witness = Announcements.generateChannelAnnouncementWitness(
nodeParams.chainHash,
shortChannelId,
nodeParams.nodeKeyManager.nodeId,
commitments.remoteParams.nodeId,
fundingPubKey.publicKey,
commitments.remoteParams.fundingPubKey,
features
)
val localBitcoinSig = nodeParams.channelKeyManager.signChannelAnnouncement(witness, fundingPubKey.path)
val localNodeSig = nodeParams.nodeKeyManager.signChannelAnnouncement(witness)
AnnouncementSignatures(commitments.channelId, shortChannelId, localNodeSig, localBitcoinSig)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import java.nio.ByteOrder

import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.DeterministicWallet.ExtendedPublicKey
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, DeterministicWallet, Protocol}
import fr.acinq.bitcoin.{ByteVector64, Crypto, DeterministicWallet, Protocol}
import fr.acinq.eclair.channel.{ChannelVersion, LocalParams}
import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, TransactionWithInputInfo, TxOwner}
import fr.acinq.eclair.{Features, ShortChannelId}
import scodec.bits.ByteVector

trait ChannelKeyManager {
def fundingPublicKey(keyPath: DeterministicWallet.KeyPath): ExtendedPublicKey
Expand Down Expand Up @@ -96,17 +96,10 @@ trait ChannelKeyManager {
/**
* Sign a channel announcement message
*
* @param localNodeSecret node private key
* @param fundingKeyPath BIP32 path of the funding public key
* @param chainHash chain hash
* @param shortChannelId short channel id
* @param remoteNodeId remote node id
* @param remoteFundingKey remote funding pubkey
* @param features channel features
* @return a (nodeSig, bitcoinSig) pair. nodeSig is the signature of the channel announcement with our node's
* private key, bitcoinSig is the signature of the channel announcement with our funding private key
* @param witness channel announcement message
* @return the signature of the channel announcement with the channel's funding private key
*/
def signChannelAnnouncement(localNodeSecret: PrivateKey, fundingKeyPath: DeterministicWallet.KeyPath, chainHash: ByteVector32, shortChannelId: ShortChannelId, remoteNodeId: PublicKey, remoteFundingKey: PublicKey, features: Features): (ByteVector64, ByteVector64)
def signChannelAnnouncement(witness: ByteVector, fundingKeyPath: DeterministicWallet.KeyPath): ByteVector64
}

object ChannelKeyManager {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.DeterministicWallet.{derivePrivateKey, _}
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto, DeterministicWallet}
import fr.acinq.eclair.router.Announcements
import fr.acinq.eclair.secureRandom
import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.transactions.Transactions.{CommitmentFormat, TransactionWithInputInfo, TxOwner}
import fr.acinq.eclair.{Features, ShortChannelId, secureRandom}
import grizzled.slf4j.Logging
import scodec.bits.ByteVector

Expand Down Expand Up @@ -138,8 +138,6 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: ByteVector32) extends
Transactions.sign(tx, currentKey, txOwner, commitmentFormat)
}

override def signChannelAnnouncement(localNodeSecret: PrivateKey, fundingKeyPath: KeyPath, chainHash: ByteVector32, shortChannelId: ShortChannelId, remoteNodeId: PublicKey, remoteFundingKey: PublicKey, features: Features): (ByteVector64, ByteVector64) = {
val localFundingPrivKey = privateKeys.get(fundingKeyPath).privateKey
Announcements.signChannelAnnouncement(chainHash, shortChannelId, localNodeSecret, remoteNodeId, localFundingPrivKey, remoteFundingKey, features)
}
override def signChannelAnnouncement(witness: ByteVector, fundingKeyPath: KeyPath): ByteVector64 =
Announcements.signChannelAnnouncement(witness, privateKeys.get(fundingKeyPath).privateKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package fr.acinq.eclair.crypto
import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.DeterministicWallet.ExtendedPrivateKey
import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Crypto, DeterministicWallet}
import fr.acinq.eclair.router.Announcements
import grizzled.slf4j.Logging
import scodec.bits.ByteVector

Expand Down Expand Up @@ -50,4 +51,7 @@ class LocalNodeKeyManager(seed: ByteVector, chainHash: ByteVector32) extends Nod
val recoveryId = if (nodeId == pub1) 0 else 1
(signature, recoveryId)
}

override def signChannelAnnouncement(witness: ByteVector): ByteVector64 =
Announcements.signChannelAnnouncement(witness, nodeKey.privateKey)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,21 @@ package fr.acinq.eclair.crypto

import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, DeterministicWallet}
import scodec.bits.ByteVector

trait NodeKeyManager {
def nodeKey: DeterministicWallet.ExtendedPrivateKey

def nodeId: PublicKey

/**
* Sign a channel announcement message
*
* @param witness channel announcement message
* @return the signature of the channel announcement with our node's private key
*/
def signChannelAnnouncement(witness: ByteVector): ByteVector64

/**
* Sign a digest, primarily used to prove ownership of the current node
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import fr.acinq.eclair.{CltvExpiryDelta, Features, MilliSatoshi, ShortChannelId,
import scodec.bits.{BitVector, ByteVector}
import shapeless.HNil

import scala.compat.Platform
import scala.concurrent.duration._

/**
Expand All @@ -40,16 +39,14 @@ object Announcements {
def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: Long, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: Option[MilliSatoshi], unknownFields: ByteVector): ByteVector =
sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: messageFlags :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: unknownFields :: HNil))))

def signChannelAnnouncement(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeSecret: PrivateKey, remoteNodeId: PublicKey, localFundingPrivKey: PrivateKey, remoteFundingKey: PublicKey, features: Features): (ByteVector64, ByteVector64) = {
val witness = if (isNode1(localNodeSecret.publicKey, remoteNodeId)) {
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeSecret.publicKey, remoteNodeId, localFundingPrivKey.publicKey, remoteFundingKey, features, unknownFields = ByteVector.empty)
def generateChannelAnnouncementWitness(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features): ByteVector =
if (isNode1(localNodeId, remoteNodeId)) {
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, features, unknownFields = ByteVector.empty)
} else {
channelAnnouncementWitnessEncode(chainHash, shortChannelId, remoteNodeId, localNodeSecret.publicKey, remoteFundingKey, localFundingPrivKey.publicKey, features, unknownFields = ByteVector.empty)
channelAnnouncementWitnessEncode(chainHash, shortChannelId, remoteNodeId, localNodeId, remoteFundingKey, localFundingKey, features, unknownFields = ByteVector.empty)
}
val nodeSig = Crypto.sign(witness, localNodeSecret)
val bitcoinSig = Crypto.sign(witness, localFundingPrivKey)
(nodeSig, bitcoinSig)
}

def signChannelAnnouncement(witness: ByteVector, key: PrivateKey): ByteVector64 = Crypto.sign(witness, key)

def makeChannelAnnouncement(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, localNodeSignature: ByteVector64, remoteNodeSignature: ByteVector64, localBitcoinSignature: ByteVector64, remoteBitcoinSignature: ByteVector64): ChannelAnnouncement = {
val (nodeId1, nodeId2, bitcoinKey1, bitcoinKey2, nodeSignature1, nodeSignature2, bitcoinSignature1, bitcoinSignature2) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import fr.acinq.eclair.payment.send.PaymentInitiator.{SendPaymentConfig, SendPay
import fr.acinq.eclair.payment.send.PaymentLifecycle
import fr.acinq.eclair.payment.send.PaymentLifecycle._
import fr.acinq.eclair.router.Announcements.makeChannelUpdate
import fr.acinq.eclair.router.BaseRouterSpec.channelAnnouncement
import fr.acinq.eclair.router.Router._
import fr.acinq.eclair.router._
import fr.acinq.eclair.transactions.Scripts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ object AnnouncementsBatchValidationSpec {
def makeChannelAnnouncement(c: SimulatedChannel)(implicit extendedBitcoinClient: ExtendedBitcoinClient, ec: ExecutionContext): ChannelAnnouncement = {
val (blockHeight, txIndex) = Await.result(extendedBitcoinClient.getTransactionShortId(c.fundingTx.txid), 10 seconds)
val shortChannelId = ShortChannelId(blockHeight, txIndex, c.fundingOutputIndex)
val (channelAnnNodeSig1, channelAnnBitcoinSig1) = Announcements.signChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, c.node1Key, c.node2Key.publicKey, c.node1FundingKey, c.node2FundingKey.publicKey, Features.empty)
val (channelAnnNodeSig2, channelAnnBitcoinSig2) = Announcements.signChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, c.node2Key, c.node1Key.publicKey, c.node2FundingKey, c.node1FundingKey.publicKey, Features.empty)
val witness = Announcements.generateChannelAnnouncementWitness(Block.RegtestGenesisBlock.hash, shortChannelId, c.node1Key.publicKey, c.node2Key.publicKey, c.node1FundingKey.publicKey, c.node2FundingKey.publicKey, Features.empty)
val channelAnnNodeSig1 = Announcements.signChannelAnnouncement(witness, c.node1Key)
val channelAnnBitcoinSig1 = Announcements.signChannelAnnouncement(witness, c.node1FundingKey)
val channelAnnNodeSig2 = Announcements.signChannelAnnouncement(witness, c.node2Key)
val channelAnnBitcoinSig2 = Announcements.signChannelAnnouncement(witness, c.node2FundingKey)
val channelAnnouncement = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, c.node1Key.publicKey, c.node2Key.publicKey, c.node1FundingKey.publicKey, c.node2FundingKey.publicKey, channelAnnNodeSig1, channelAnnNodeSig2, channelAnnBitcoinSig1, channelAnnBitcoinSig2)
channelAnnouncement
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ class AnnouncementsSpec extends AnyFunSuite {

test("create valid signed channel announcement") {
val (node_a, node_b, bitcoin_a, bitcoin_b) = (randomKey, randomKey, randomKey, randomKey)
val (node_a_sig, bitcoin_a_sig) = signChannelAnnouncement(Block.RegtestGenesisBlock.hash, ShortChannelId(42L), node_a, node_b.publicKey, bitcoin_a, bitcoin_b.publicKey, Features.empty)
val (node_b_sig, bitcoin_b_sig) = signChannelAnnouncement(Block.RegtestGenesisBlock.hash, ShortChannelId(42L), node_b, node_a.publicKey, bitcoin_b, bitcoin_a.publicKey, Features.empty)
val witness = Announcements.generateChannelAnnouncementWitness(Block.RegtestGenesisBlock.hash, ShortChannelId(42L), node_a.publicKey, node_b.publicKey, bitcoin_a.publicKey, bitcoin_b.publicKey, Features.empty)
val node_a_sig = Announcements.signChannelAnnouncement(witness, node_a)
val bitcoin_a_sig = Announcements.signChannelAnnouncement(witness, bitcoin_a)
val node_b_sig = Announcements.signChannelAnnouncement(witness, node_b)
val bitcoin_b_sig = Announcements.signChannelAnnouncement(witness, bitcoin_b)
val ann = makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, ShortChannelId(42L), node_a.publicKey, node_b.publicKey, bitcoin_a.publicKey, bitcoin_b.publicKey, node_a_sig, node_b_sig, bitcoin_a_sig, bitcoin_b_sig)
assert(checkSigs(ann))
assert(checkSigs(ann.copy(nodeId1 = randomKey.publicKey)) === false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import fr.acinq.eclair.channel.{CommitmentsSpec, LocalChannelUpdate}
import fr.acinq.eclair.crypto.{LocalChannelKeyManager, LocalNodeKeyManager, TransportHandler}
import fr.acinq.eclair.io.Peer.PeerRoutingMessage
import fr.acinq.eclair.router.Announcements._
import fr.acinq.eclair.router.BaseRouterSpec.channelAnnouncement
import fr.acinq.eclair.router.Router.{ChannelDesc, ChannelMeta, GossipDecision, PrivateChannel}
import fr.acinq.eclair.transactions.Scripts
import fr.acinq.eclair.wire._
Expand Down Expand Up @@ -77,11 +78,14 @@ abstract class BaseRouterSpec extends TestKitBaseClass with FixtureAnyFunSuiteLi
val channelId_ag = ShortChannelId(420000, 5, 0)
val channelId_gh = ShortChannelId(420000, 6, 0)

def channelAnnouncement(shortChannelId: ShortChannelId, node1_priv: PrivateKey, node2_priv: PrivateKey, funding1_priv: PrivateKey, funding2_priv: PrivateKey) = {
val (node1_sig, funding1_sig) = signChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, node1_priv, node2_priv.publicKey, funding1_priv, funding2_priv.publicKey, Features.empty)
val (node2_sig, funding2_sig) = signChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, node2_priv, node1_priv.publicKey, funding2_priv, funding1_priv.publicKey, Features.empty)
makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, node1_priv.publicKey, node2_priv.publicKey, funding1_priv.publicKey, funding2_priv.publicKey, node1_sig, node2_sig, funding1_sig, funding2_sig)
}
// def channelAnnouncement(shortChannelId: ShortChannelId, node1_priv: PrivateKey, node2_priv: PrivateKey, funding1_priv: PrivateKey, funding2_priv: PrivateKey) = {
// val witness = Announcements.generateChannelAnnouncementWitness(Block.RegtestGenesisBlock.hash, shortChannelId, node1_priv.publicKey, node2_priv.publicKey, node1_priv.publicKey, node2_priv.publicKey, Features.empty)
// val node1_sig = Announcements.signChannelAnnouncement(witness, node1_priv)
// val funding1_sig = Announcements.signChannelAnnouncement(witness, funding1_priv)
// val node2_sig = Announcements.signChannelAnnouncement(witness, node2_priv)
// val funding2_sig = Announcements.signChannelAnnouncement(witness, funding2_priv)
// makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, shortChannelId, node1_priv.publicKey, node2_priv.publicKey, funding1_priv.publicKey, funding2_priv.publicKey, node1_sig, node2_sig, funding1_sig, funding2_sig)
// }

val chan_ab = channelAnnouncement(channelId_ab, priv_a, priv_b, priv_funding_a, priv_funding_b)
val chan_bc = channelAnnouncement(channelId_bc, priv_b, priv_c, priv_funding_b, priv_funding_c)
Expand Down Expand Up @@ -214,8 +218,11 @@ abstract class BaseRouterSpec extends TestKitBaseClass with FixtureAnyFunSuiteLi

object BaseRouterSpec {
def channelAnnouncement(channelId: ShortChannelId, node1_priv: PrivateKey, node2_priv: PrivateKey, funding1_priv: PrivateKey, funding2_priv: PrivateKey) = {
val (node1_sig, funding1_sig) = signChannelAnnouncement(Block.RegtestGenesisBlock.hash, channelId, node1_priv, node2_priv.publicKey, funding1_priv, funding2_priv.publicKey, Features.empty)
val (node2_sig, funding2_sig) = signChannelAnnouncement(Block.RegtestGenesisBlock.hash, channelId, node2_priv, node1_priv.publicKey, funding2_priv, funding1_priv.publicKey, Features.empty)
val witness = Announcements.generateChannelAnnouncementWitness(Block.RegtestGenesisBlock.hash, channelId, node1_priv.publicKey, node2_priv.publicKey, funding1_priv.publicKey, funding2_priv.publicKey, Features.empty)
val node1_sig = Announcements.signChannelAnnouncement(witness, node1_priv)
val funding1_sig = Announcements.signChannelAnnouncement(witness, funding1_priv)
val node2_sig = Announcements.signChannelAnnouncement(witness, node2_priv)
val funding2_sig = Announcements.signChannelAnnouncement(witness, funding2_priv)
makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, channelId, node1_priv.publicKey, node2_priv.publicKey, funding1_priv.publicKey, funding2_priv.publicKey, node1_sig, node2_sig, funding1_sig, funding2_sig)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import fr.acinq.eclair.crypto.TransportHandler
import fr.acinq.eclair.io.Peer.PeerRoutingMessage
import fr.acinq.eclair.payment.PaymentRequest.ExtraHop
import fr.acinq.eclair.router.Announcements.{makeChannelUpdate, makeNodeAnnouncement}
import fr.acinq.eclair.router.BaseRouterSpec.channelAnnouncement
import fr.acinq.eclair.router.RouteCalculationSpec.{DEFAULT_AMOUNT_MSAT, DEFAULT_MAX_FEE}
import fr.acinq.eclair.router.Router._
import fr.acinq.eclair.transactions.Scripts
Expand Down

0 comments on commit d7418a9

Please sign in to comment.