Skip to content

Commit

Permalink
Add tx signing metrics (#1659)
Browse files Browse the repository at this point in the history
Monitor the rate at which we sign channel txs and the duration of the
signing operations.
  • Loading branch information
t-bast authored Feb 12, 2021
1 parent 5d3958d commit 15c1837
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 25 deletions.
11 changes: 11 additions & 0 deletions eclair-core/src/main/scala/fr/acinq/eclair/crypto/Monitoring.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,21 @@ object Monitoring {

object Metrics {
val OnionPayloadFormat = Kamon.counter("crypto.sphinx.onion-payload-format")
val SignTxCount = Kamon.counter("crypto.keymanager.sign.count")
val SignTxDuration = Kamon.timer("crypto.keymanager.sign.duration")
}

object Tags {
val LegacyOnion = "legacy"
val TxOwner = "txOwner"
val TxType = "txType"

object TxTypes {
val CommitTx = "commit"
val HtlcTx = "htlc"
val RevokedTx = "revoked"
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@

package fr.acinq.eclair.crypto.keymanager

import java.io.ByteArrayInputStream
import java.nio.ByteOrder

import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.DeterministicWallet.ExtendedPublicKey
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 scodec.bits.ByteVector

import java.io.ByteArrayInputStream
import java.nio.ByteOrder

trait ChannelKeyManager {
def fundingPublicKey(keyPath: DeterministicWallet.KeyPath): ExtendedPublicKey

Expand Down Expand Up @@ -62,8 +62,7 @@ trait ChannelKeyManager {
* @param publicKey extended public key
* @param txOwner owner of the transaction (local/remote)
* @param commitmentFormat format of the commitment tx
* @return a signature generated with the private key that matches the input
* extended public key
* @return a signature generated with the private key that matches the input extended public key
*/
def sign(tx: TransactionWithInputInfo, publicKey: ExtendedPublicKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64

Expand All @@ -75,8 +74,7 @@ trait ChannelKeyManager {
* @param remotePoint remote point
* @param txOwner owner of the transaction (local/remote)
* @param commitmentFormat format of the commitment tx
* @return a signature generated with a private key generated from the input keys's matching
* private key and the remote point.
* @return a signature generated with a private key generated from the input key's matching private key and the remote point.
*/
def sign(tx: TransactionWithInputInfo, publicKey: ExtendedPublicKey, remotePoint: PublicKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64

Expand All @@ -88,8 +86,7 @@ trait ChannelKeyManager {
* @param remoteSecret remote secret
* @param txOwner owner of the transaction (local/remote)
* @param commitmentFormat format of the commitment tx
* @return a signature generated with a private key generated from the input keys's matching
* private key and the remote secret.
* @return a signature generated with a private key generated from the input key's matching private key and the remote secret.
*/
def sign(tx: TransactionWithInputInfo, publicKey: ExtendedPublicKey, remoteSecret: PrivateKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64

Expand All @@ -105,7 +102,7 @@ trait ChannelKeyManager {
object ChannelKeyManager {
/**
* Create a BIP32 path from a public key. This path will be used to derive channel keys.
* Having channel keys derived from the funding public keys makes it very easy to retrieve your funds when've you've lost your data:
* Having channel keys derived from the funding public keys makes it very easy to retrieve your funds when you've lost your data:
* - connect to your peer and use DLP to get them to publish their remote commit tx
* - retrieve the commit tx from the bitcoin network, extract your funding pubkey from its witness data
* - recompute your channel keys and spend your output
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ 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.crypto.Generators
import fr.acinq.eclair.crypto.Monitoring.{Metrics, Tags}
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.{KamonExt, secureRandom}
import grizzled.slf4j.Logging
import kamon.tag.TagSet
import scodec.bits.ByteVector

object LocalChannelKeyManager {
Expand Down Expand Up @@ -97,12 +99,16 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: ByteVector32) extends
* @param publicKey extended public key
* @param txOwner owner of the transaction (local/remote)
* @param commitmentFormat format of the commitment tx
* @return a signature generated with the private key that matches the input
* extended public key
* @return a signature generated with the private key that matches the input extended public key
*/
override def sign(tx: TransactionWithInputInfo, publicKey: ExtendedPublicKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64 = {
val privateKey = privateKeys.get(publicKey.path)
Transactions.sign(tx, privateKey.privateKey, txOwner, commitmentFormat)
// NB: not all those transactions are actually commit txs (especially during closing), but this is good enough for monitoring purposes
val tags = TagSet.Empty.withTag(Tags.TxOwner, txOwner.toString).withTag(Tags.TxType, Tags.TxTypes.CommitTx)
Metrics.SignTxCount.withTags(tags).increment()
KamonExt.time(Metrics.SignTxDuration.withTags(tags)) {
val privateKey = privateKeys.get(publicKey.path)
Transactions.sign(tx, privateKey.privateKey, txOwner, commitmentFormat)
}
}

/**
Expand All @@ -113,13 +119,17 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: ByteVector32) extends
* @param remotePoint remote point
* @param txOwner owner of the transaction (local/remote)
* @param commitmentFormat format of the commitment tx
* @return a signature generated with a private key generated from the input keys's matching
* private key and the remote point.
* @return a signature generated with a private key generated from the input key's matching private key and the remote point.
*/
override def sign(tx: TransactionWithInputInfo, publicKey: ExtendedPublicKey, remotePoint: PublicKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64 = {
val privateKey = privateKeys.get(publicKey.path)
val currentKey = Generators.derivePrivKey(privateKey.privateKey, remotePoint)
Transactions.sign(tx, currentKey, txOwner, commitmentFormat)
// NB: not all those transactions are actually htlc txs (especially during closing), but this is good enough for monitoring purposes
val tags = TagSet.Empty.withTag(Tags.TxOwner, txOwner.toString).withTag(Tags.TxType, Tags.TxTypes.HtlcTx)
Metrics.SignTxCount.withTags(tags).increment()
KamonExt.time(Metrics.SignTxDuration.withTags(tags)) {
val privateKey = privateKeys.get(publicKey.path)
val currentKey = Generators.derivePrivKey(privateKey.privateKey, remotePoint)
Transactions.sign(tx, currentKey, txOwner, commitmentFormat)
}
}

/**
Expand All @@ -130,13 +140,16 @@ class LocalChannelKeyManager(seed: ByteVector, chainHash: ByteVector32) extends
* @param remoteSecret remote secret
* @param txOwner owner of the transaction (local/remote)
* @param commitmentFormat format of the commitment tx
* @return a signature generated with a private key generated from the input keys's matching
* private key and the remote secret.
* @return a signature generated with a private key generated from the input key's matching private key and the remote secret.
*/
override def sign(tx: TransactionWithInputInfo, publicKey: ExtendedPublicKey, remoteSecret: PrivateKey, txOwner: TxOwner, commitmentFormat: CommitmentFormat): ByteVector64 = {
val privateKey = privateKeys.get(publicKey.path)
val currentKey = Generators.revocationPrivKey(privateKey.privateKey, remoteSecret)
Transactions.sign(tx, currentKey, txOwner, commitmentFormat)
val tags = TagSet.Empty.withTag(Tags.TxOwner, txOwner.toString).withTag(Tags.TxType, Tags.TxTypes.RevokedTx)
Metrics.SignTxCount.withTags(tags).increment()
KamonExt.time(Metrics.SignTxDuration.withTags(tags)) {
val privateKey = privateKeys.get(publicKey.path)
val currentKey = Generators.revocationPrivKey(privateKey.privateKey, remoteSecret)
Transactions.sign(tx, currentKey, txOwner, commitmentFormat)
}
}

override def signChannelAnnouncement(witness: ByteVector, fundingKeyPath: KeyPath): ByteVector64 =
Expand Down

0 comments on commit 15c1837

Please sign in to comment.