1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-22 22:25:26 +01:00

Use typed TxId (#2742)

And explicitly encode/decode as a `tx_hash` for lightning messages.
This commit is contained in:
Bastien Teinturier 2023-11-23 16:04:31 +01:00 committed by GitHub
parent e20b736bf3
commit e73c1cf45c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
113 changed files with 756 additions and 704 deletions

View file

@ -24,7 +24,7 @@ import akka.pattern._
import akka.util.Timeout
import com.softwaremill.quicklens.ModifyPimp
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, Satoshi, Script, addressToPublicKeyScript}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, ByteVector64, Crypto, OutPoint, Satoshi, Script, TxId, addressToPublicKeyScript}
import fr.acinq.eclair.ApiTypes.ChannelNotFound
import fr.acinq.eclair.balance.CheckBalance.GlobalBalance
import fr.acinq.eclair.balance.{BalanceActor, ChannelsListener}
@ -58,7 +58,7 @@ import java.util.UUID
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future, Promise}
case class GetInfoResponse(version: String, nodeId: PublicKey, alias: String, color: String, features: Features[Feature], chainHash: ByteVector32, network: String, blockHeight: Int, publicAddresses: Seq[NodeAddress], onionAddress: Option[NodeAddress], instanceId: String)
case class GetInfoResponse(version: String, nodeId: PublicKey, alias: String, color: String, features: Features[Feature], chainHash: BlockHash, network: String, blockHeight: Int, publicAddresses: Seq[NodeAddress], onionAddress: Option[NodeAddress], instanceId: String)
case class AuditResponse(sent: Seq[PaymentSent], received: Seq[PaymentReceived], relayed: Seq[PaymentRelayed])
@ -131,9 +131,9 @@ trait Eclair {
def sentInfo(id: PaymentIdentifier)(implicit timeout: Timeout): Future[Seq[OutgoingPayment]]
def sendOnChain(address: String, amount: Satoshi, confirmationTargetOrFeerate: Either[Long, FeeratePerByte]): Future[ByteVector32]
def sendOnChain(address: String, amount: Satoshi, confirmationTargetOrFeerate: Either[Long, FeeratePerByte]): Future[TxId]
def cpfpBumpFees(targetFeeratePerByte: FeeratePerByte, outpoints: Set[OutPoint]): Future[ByteVector32]
def cpfpBumpFees(targetFeeratePerByte: FeeratePerByte, outpoints: Set[OutPoint]): Future[TxId]
def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, pathFindingExperimentName_opt: Option[String], extraEdges: Seq[Invoice.ExtraEdge] = Seq.empty, includeLocalChannelCost: Boolean = false, ignoreNodeIds: Seq[PublicKey] = Seq.empty, ignoreShortChannelIds: Seq[ShortChannelId] = Seq.empty, maxFee_opt: Option[MilliSatoshi] = None)(implicit timeout: Timeout): Future[RouteResponse]
@ -357,7 +357,7 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging {
}
}
override def sendOnChain(address: String, amount: Satoshi, confirmationTargetOrFeerate: Either[Long, FeeratePerByte]): Future[ByteVector32] = {
override def sendOnChain(address: String, amount: Satoshi, confirmationTargetOrFeerate: Either[Long, FeeratePerByte]): Future[TxId] = {
val feeRate = confirmationTargetOrFeerate match {
case Left(blocks) =>
if (blocks < 3) appKit.nodeParams.currentFeerates.fast
@ -375,7 +375,7 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging {
}
}
override def cpfpBumpFees(targetFeeratePerByte: FeeratePerByte, outpoints: Set[OutPoint]): Future[ByteVector32] = {
override def cpfpBumpFees(targetFeeratePerByte: FeeratePerByte, outpoints: Set[OutPoint]): Future[TxId] = {
appKit.wallet match {
case w: BitcoinCoreClient => w.cpfp(outpoints, FeeratePerKw(targetFeeratePerByte)).map(_.txid)
case _ => Future.failed(new IllegalArgumentException("this call is only available with a bitcoin core backend"))

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair
import com.typesafe.config.{Config, ConfigFactory, ConfigValueType}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Crypto, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, Crypto, Satoshi}
import fr.acinq.eclair.Setup.Seeds
import fr.acinq.eclair.blockchain.fee._
import fr.acinq.eclair.channel.ChannelFlags
@ -73,7 +73,7 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
autoReconnect: Boolean,
initialRandomReconnectDelay: FiniteDuration,
maxReconnectInterval: FiniteDuration,
chainHash: ByteVector32,
chainHash: BlockHash,
invoiceExpiry: FiniteDuration,
multiPartPaymentExpiry: FiniteDuration,
peerConnectionConf: PeerConnection.Conf,
@ -184,16 +184,16 @@ object NodeParams extends Logging {
Seeds(nodeSeed, channelSeed)
}
private val chain2Hash: Map[String, ByteVector32] = Map(
private val chain2Hash: Map[String, BlockHash] = Map(
"regtest" -> Block.RegtestGenesisBlock.hash,
"testnet" -> Block.TestnetGenesisBlock.hash,
"signet" -> Block.SignetGenesisBlock.hash,
"mainnet" -> Block.LivenetGenesisBlock.hash
)
def hashFromChain(chain: String): ByteVector32 = chain2Hash.getOrElse(chain, throw new RuntimeException(s"invalid chain '$chain'"))
def hashFromChain(chain: String): BlockHash = chain2Hash.getOrElse(chain, throw new RuntimeException(s"invalid chain '$chain'"))
def chainFromHash(chainHash: ByteVector32): String = chain2Hash.map(_.swap).getOrElse(chainHash, throw new RuntimeException(s"invalid chainHash '$chainHash'"))
def chainFromHash(chainHash: BlockHash): String = chain2Hash.map(_.swap).getOrElse(chainHash, throw new RuntimeException(s"invalid chainHash '$chainHash'"))
def parseSocks5ProxyParams(config: Config): Option[Socks5ProxyParams] = {
if (config.getBoolean("socks5.enabled")) {

View file

@ -23,7 +23,7 @@ import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy, typed}
import akka.pattern.after
import akka.util.Timeout
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, BlockId, ByteVector32, Satoshi}
import fr.acinq.eclair.Setup.Seeds
import fr.acinq.eclair.balance.{BalanceActor, ChannelsListener}
import fr.acinq.eclair.blockchain._
@ -134,7 +134,7 @@ class Setup(val datadir: File,
case "password" => BitcoinJsonRPCAuthMethod.UserPassword(config.getString("bitcoind.rpcuser"), config.getString("bitcoind.rpcpassword"))
}
case class BitcoinStatus(version: Int, chainHash: ByteVector32, initialBlockDownload: Boolean, verificationProgress: Double, blockCount: Long, headerCount: Long, unspentAddresses: List[String])
case class BitcoinStatus(version: Int, chainHash: BlockHash, initialBlockDownload: Boolean, verificationProgress: Double, blockCount: Long, headerCount: Long, unspentAddresses: List[String])
def getBitcoinStatus(bitcoinClient: BasicBitcoinJsonRPCClient): Future[BitcoinStatus] = for {
json <- bitcoinClient.invoke("getblockchaininfo").recover { case e => throw BitcoinRPCConnectionException(e) }
@ -147,7 +147,8 @@ class Setup(val datadir: File,
ibd = (json \ "initialblockdownload").extract[Boolean]
blocks = (json \ "blocks").extract[Long]
headers = (json \ "headers").extract[Long]
chainHash <- bitcoinClient.invoke("getblockhash", 0).map(_.extract[String]).map(s => ByteVector32.fromValidHex(s)).map(_.reverse)
// NB: bitcoind confusingly returns the blockId instead of the blockHash.
chainHash <- bitcoinClient.invoke("getblockhash", 0).map(_.extract[String]).map(s => BlockId(ByteVector32.fromValidHex(s))).map(BlockHash(_))
bitcoinVersion <- bitcoinClient.invoke("getnetworkinfo").map(json => json \ "version").map(_.extract[Int])
unspentAddresses <- bitcoinClient.invoke("listunspent").recover { _ => if (wallet.isEmpty && wallets.length > 1) throw BitcoinDefaultWalletException(wallets) else throw BitcoinWalletNotLoadedException(wallet.getOrElse(""), wallets) }
.collect { case JArray(values) =>

View file

@ -3,7 +3,7 @@ package fr.acinq.eclair.balance
import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import akka.actor.typed.{ActorRef, Behavior}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong, TxId}
import fr.acinq.eclair.NotificationsLogger
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
import fr.acinq.eclair.balance.BalanceActor._
@ -40,11 +40,11 @@ object BalanceActor {
}
}
final case class UtxoInfo(utxos: Seq[Utxo], ancestorCount: Map[ByteVector32, Long])
final case class UtxoInfo(utxos: Seq[Utxo], ancestorCount: Map[TxId, Long])
def checkUtxos(bitcoinClient: BitcoinCoreClient)(implicit ec: ExecutionContext): Future[UtxoInfo] = {
def getUnconfirmedAncestorCount(utxo: Utxo): Future[(ByteVector32, Long)] = bitcoinClient.rpcClient.invoke("getmempoolentry", utxo.txid).map(json => {
def getUnconfirmedAncestorCount(utxo: Utxo): Future[(TxId, Long)] = bitcoinClient.rpcClient.invoke("getmempoolentry", utxo.txid).map(json => {
val JInt(ancestorCount) = json \ "ancestorcount"
(utxo.txid, ancestorCount.toLong)
}).recover {
@ -55,7 +55,7 @@ object BalanceActor {
(utxo.txid, 0)
}
def getUnconfirmedAncestorCountMap(utxos: Seq[Utxo]): Future[Map[ByteVector32, Long]] = Future.sequence(utxos.filter(_.confirmations == 0).map(getUnconfirmedAncestorCount)).map(_.toMap)
def getUnconfirmedAncestorCountMap(utxos: Seq[Utxo]): Future[Map[TxId, Long]] = Future.sequence(utxos.filter(_.confirmations == 0).map(getUnconfirmedAncestorCount)).map(_.toMap)
for {
utxos <- bitcoinClient.listUnspent()
@ -134,7 +134,7 @@ private class BalanceActor(context: ActorContext[Command],
Behaviors.same
case WrappedUtxoInfo(res) =>
res match {
case Success(UtxoInfo(utxos: Seq[Utxo], ancestorCount: Map[ByteVector32, Long])) =>
case Success(UtxoInfo(utxos, ancestorCount)) =>
val filteredByStatus: Map[String, Seq[Utxo]] = Map(
Monitoring.Tags.UtxoStatuses.Confirmed -> utxos.filter(utxo => utxo.confirmations > 0),
// We cannot create chains of unconfirmed transactions with more than 25 elements, so we ignore such utxos.

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.balance
import com.softwaremill.quicklens._
import fr.acinq.bitcoin.scalacompat.{Btc, ByteVector32, Satoshi, SatoshiLong, Script}
import fr.acinq.bitcoin.scalacompat.{Btc, ByteVector32, Satoshi, SatoshiLong, Script, TxId}
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
import fr.acinq.eclair.channel.Helpers.Closing
import fr.acinq.eclair.channel.Helpers.Closing.{CurrentRemoteClose, LocalClose, NextRemoteClose, RemoteClose}
@ -51,11 +51,11 @@ object CheckBalance {
* That's why we keep track of the id of each transaction that pays us any amount. It allows us to double check from
* bitcoin core and remove any published transaction.
*/
case class PossiblyPublishedMainBalance(toLocal: Map[ByteVector32, Btc] = Map.empty) {
case class PossiblyPublishedMainBalance(toLocal: Map[TxId, Btc] = Map.empty) {
val total: Btc = toLocal.values.map(_.toSatoshi).sum
}
case class PossiblyPublishedMainAndHtlcBalance(toLocal: Map[ByteVector32, Btc] = Map.empty, htlcs: Map[ByteVector32, Btc] = Map.empty, htlcsUnpublished: Btc = 0.sat) {
case class PossiblyPublishedMainAndHtlcBalance(toLocal: Map[TxId, Btc] = Map.empty, htlcs: Map[TxId, Btc] = Map.empty, htlcsUnpublished: Btc = 0.sat) {
val totalToLocal: Btc = toLocal.values.map(_.toSatoshi).sum
val totalHtlcs: Btc = htlcs.values.map(_.toSatoshi).sum
val total: Btc = totalToLocal + totalHtlcs + htlcsUnpublished
@ -153,7 +153,7 @@ object CheckBalance {
val finalScriptPubKey = Script.write(Script.pay2wpkh(c.params.localParams.walletStaticPaymentBasepoint.get))
Transactions.findPubKeyScriptIndex(remoteCommitPublished.commitTx, finalScriptPubKey) match {
case Right(outputIndex) => Map(remoteCommitPublished.commitTx.txid -> remoteCommitPublished.commitTx.txOut(outputIndex).amount.toBtc)
case _ => Map.empty[ByteVector32, Btc] // either we don't have an output (below dust), or we have used a non-default pubkey script
case _ => Map.empty[TxId, Btc] // either we don't have an output (below dust), or we have used a non-default pubkey script
}
} else {
remoteCommitPublished.claimMainOutputTx.toSeq.map(c => c.tx.txid -> c.tx.txOut.head.amount.toBtc).toMap
@ -258,13 +258,13 @@ object CheckBalance {
*/
def prunePublishedTransactions(br: OffChainBalance, bitcoinClient: BitcoinCoreClient)(implicit ec: ExecutionContext): Future[OffChainBalance] = {
for {
txs: Iterable[Option[(ByteVector32, Int)]] <- Future.sequence((br.closing.localCloseBalance.toLocal.keys ++
txs: Iterable[Option[(TxId, Int)]] <- Future.sequence((br.closing.localCloseBalance.toLocal.keys ++
br.closing.localCloseBalance.htlcs.keys ++
br.closing.remoteCloseBalance.toLocal.keys ++
br.closing.remoteCloseBalance.htlcs.keys ++
br.closing.mutualCloseBalance.toLocal.keys)
.map(txid => bitcoinClient.getTxConfirmations(txid).map(_ map { confirmations => txid -> confirmations })))
txMap: Map[ByteVector32, Int] = txs.flatten.toMap
txMap: Map[TxId, Int] = txs.flatten.toMap
} yield {
br
.modifyAll(

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.blockchain
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockId, Transaction}
import fr.acinq.eclair.BlockHeight
import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
@ -26,7 +26,7 @@ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
sealed trait BlockchainEvent
case class NewBlock(blockHash: ByteVector32) extends BlockchainEvent
case class NewBlock(blockId: BlockId) extends BlockchainEvent
case class NewTransaction(tx: Transaction) extends BlockchainEvent

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.blockchain
import fr.acinq.bitcoin.psbt.Psbt
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{OutPoint, Satoshi, Transaction, TxId}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import scodec.bits.ByteVector
@ -52,7 +52,7 @@ trait OnChainChannelFunder {
* Publish a transaction on the bitcoin network.
* This method must be idempotent: if the tx was already published, it must return a success.
*/
def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[ByteVector32]
def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[TxId]
/** Create a fully signed channel funding transaction with the provided pubkeyScript. */
def makeFundingTx(pubkeyScript: ByteVector, amount: Satoshi, feeRate: FeeratePerKw)(implicit ec: ExecutionContext): Future[MakeFundingTxResponse]
@ -70,10 +70,10 @@ trait OnChainChannelFunder {
def commit(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean]
/** Return the transaction if it exists, either in the blockchain or in the mempool. */
def getTransaction(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Transaction]
def getTransaction(txId: TxId)(implicit ec: ExecutionContext): Future[Transaction]
/** Get the number of confirmations of a given transaction. */
def getTxConfirmations(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]]
def getTxConfirmations(txId: TxId)(implicit ec: ExecutionContext): Future[Option[Int]]
/** Rollback a transaction that we failed to commit: this probably translates to "release locks on utxos". */
def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean]
@ -139,9 +139,9 @@ object OnChainWallet {
/** Transaction with all available witnesses. */
val partiallySignedTx: Transaction = {
var tx = psbt.getGlobal.getTx
for (i <- 0 until psbt.getInputs.size()) {
Option(psbt.getInputs.get(i).getScriptWitness).foreach { witness =>
var tx = psbt.global.tx
for (i <- 0 until psbt.inputs.size()) {
Option(psbt.inputs.get(i).getScriptWitness).foreach { witness =>
tx = tx.updateWitness(i, witness)
}
}

View file

@ -61,14 +61,14 @@ object ZmqWatcher {
private case class GetBlockCountFailed(t: Throwable) extends Command
private case class CheckBlockHeight(current: BlockHeight) extends Command
private case class PublishBlockHeight(current: BlockHeight) extends Command
private case class ProcessNewBlock(blockHash: ByteVector32) extends Command
private case class ProcessNewBlock(blockId: BlockId) extends Command
private case class ProcessNewTransaction(tx: Transaction) extends Command
final case class ValidateRequest(replyTo: ActorRef[ValidateResult], ann: ChannelAnnouncement) extends Command
final case class ValidateResult(c: ChannelAnnouncement, fundingTx: Either[Throwable, (Transaction, UtxoStatus)])
final case class GetTxWithMeta(replyTo: ActorRef[GetTxWithMetaResponse], txid: ByteVector32) extends Command
final case class GetTxWithMetaResponse(txid: ByteVector32, tx_opt: Option[Transaction], lastBlockTimestamp: TimestampSecond)
final case class GetTxWithMeta(replyTo: ActorRef[GetTxWithMetaResponse], txid: TxId) extends Command
final case class GetTxWithMetaResponse(txid: TxId, tx_opt: Option[Transaction], lastBlockTimestamp: TimestampSecond)
sealed trait UtxoStatus
object UtxoStatus {
@ -79,7 +79,7 @@ object ZmqWatcher {
/** Watch for confirmation of a given transaction. */
sealed trait WatchConfirmed[T <: WatchConfirmedTriggered] extends Watch[T] {
/** TxId of the transaction to watch. */
def txId: ByteVector32
def txId: TxId
/** Number of confirmations. */
def minDepth: Long
}
@ -93,14 +93,14 @@ object ZmqWatcher {
*/
sealed trait WatchSpent[T <: WatchSpentTriggered] extends Watch[T] {
/** TxId of the outpoint to watch. */
def txId: ByteVector32
def txId: TxId
/** Index of the outpoint to watch. */
def outputIndex: Int
/**
* TxIds of potential spending transactions; most of the time we know the txs, and it allows for optimizations.
* This argument can safely be ignored by watcher implementations.
*/
def hints: Set[ByteVector32]
def hints: Set[TxId]
}
/**
@ -112,7 +112,7 @@ object ZmqWatcher {
*/
sealed trait WatchSpentBasic[T <: WatchSpentBasicTriggered] extends Watch[T] {
/** TxId of the outpoint to watch. */
def txId: ByteVector32
def txId: TxId
/** Index of the outpoint to watch. */
def outputIndex: Int
}
@ -136,32 +136,32 @@ object ZmqWatcher {
/** This event is sent when a [[WatchSpentBasic]] condition is met. */
sealed trait WatchSpentBasicTriggered extends WatchTriggered
case class WatchExternalChannelSpent(replyTo: ActorRef[WatchExternalChannelSpentTriggered], txId: ByteVector32, outputIndex: Int, shortChannelId: RealShortChannelId) extends WatchSpentBasic[WatchExternalChannelSpentTriggered]
case class WatchExternalChannelSpent(replyTo: ActorRef[WatchExternalChannelSpentTriggered], txId: TxId, outputIndex: Int, shortChannelId: RealShortChannelId) extends WatchSpentBasic[WatchExternalChannelSpentTriggered]
case class WatchExternalChannelSpentTriggered(shortChannelId: RealShortChannelId) extends WatchSpentBasicTriggered
case class WatchFundingSpent(replyTo: ActorRef[WatchFundingSpentTriggered], txId: ByteVector32, outputIndex: Int, hints: Set[ByteVector32]) extends WatchSpent[WatchFundingSpentTriggered]
case class WatchFundingSpent(replyTo: ActorRef[WatchFundingSpentTriggered], txId: TxId, outputIndex: Int, hints: Set[TxId]) extends WatchSpent[WatchFundingSpentTriggered]
case class WatchFundingSpentTriggered(spendingTx: Transaction) extends WatchSpentTriggered
case class WatchOutputSpent(replyTo: ActorRef[WatchOutputSpentTriggered], txId: ByteVector32, outputIndex: Int, hints: Set[ByteVector32]) extends WatchSpent[WatchOutputSpentTriggered]
case class WatchOutputSpent(replyTo: ActorRef[WatchOutputSpentTriggered], txId: TxId, outputIndex: Int, hints: Set[TxId]) extends WatchSpent[WatchOutputSpentTriggered]
case class WatchOutputSpentTriggered(spendingTx: Transaction) extends WatchSpentTriggered
/** Waiting for a wallet transaction to be published guarantees that bitcoind won't double-spend it in the future, unless we explicitly call abandontransaction. */
case class WatchPublished(replyTo: ActorRef[WatchPublishedTriggered], txId: ByteVector32) extends Watch[WatchPublishedTriggered]
case class WatchPublished(replyTo: ActorRef[WatchPublishedTriggered], txId: TxId) extends Watch[WatchPublishedTriggered]
case class WatchPublishedTriggered(tx: Transaction) extends WatchTriggered
case class WatchFundingConfirmed(replyTo: ActorRef[WatchFundingConfirmedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchFundingConfirmedTriggered]
case class WatchFundingConfirmed(replyTo: ActorRef[WatchFundingConfirmedTriggered], txId: TxId, minDepth: Long) extends WatchConfirmed[WatchFundingConfirmedTriggered]
case class WatchFundingConfirmedTriggered(blockHeight: BlockHeight, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered
case class WatchFundingDeeplyBuried(replyTo: ActorRef[WatchFundingDeeplyBuriedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchFundingDeeplyBuriedTriggered]
case class WatchFundingDeeplyBuried(replyTo: ActorRef[WatchFundingDeeplyBuriedTriggered], txId: TxId, minDepth: Long) extends WatchConfirmed[WatchFundingDeeplyBuriedTriggered]
case class WatchFundingDeeplyBuriedTriggered(blockHeight: BlockHeight, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered
case class WatchTxConfirmed(replyTo: ActorRef[WatchTxConfirmedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchTxConfirmedTriggered]
case class WatchTxConfirmed(replyTo: ActorRef[WatchTxConfirmedTriggered], txId: TxId, minDepth: Long) extends WatchConfirmed[WatchTxConfirmedTriggered]
case class WatchTxConfirmedTriggered(blockHeight: BlockHeight, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered
case class WatchParentTxConfirmed(replyTo: ActorRef[WatchParentTxConfirmedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchParentTxConfirmedTriggered]
case class WatchParentTxConfirmed(replyTo: ActorRef[WatchParentTxConfirmedTriggered], txId: TxId, minDepth: Long) extends WatchConfirmed[WatchParentTxConfirmedTriggered]
case class WatchParentTxConfirmedTriggered(blockHeight: BlockHeight, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered
case class WatchAlternativeCommitTxConfirmed(replyTo: ActorRef[WatchAlternativeCommitTxConfirmedTriggered], txId: ByteVector32, minDepth: Long) extends WatchConfirmed[WatchAlternativeCommitTxConfirmedTriggered]
case class WatchAlternativeCommitTxConfirmed(replyTo: ActorRef[WatchAlternativeCommitTxConfirmedTriggered], txId: TxId, minDepth: Long) extends WatchConfirmed[WatchAlternativeCommitTxConfirmedTriggered]
case class WatchAlternativeCommitTxConfirmedTriggered(blockHeight: BlockHeight, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered
private sealed trait AddWatchResult
@ -171,7 +171,7 @@ object ZmqWatcher {
def apply(nodeParams: NodeParams, blockCount: AtomicLong, client: BitcoinCoreClient): Behavior[Command] =
Behaviors.setup { context =>
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewBlock](b => ProcessNewBlock(b.blockHash)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewBlock](b => ProcessNewBlock(b.blockId)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewTransaction](t => ProcessNewTransaction(t.tx)))
Behaviors.withTimers { timers =>
// we initialize block count
@ -184,8 +184,8 @@ object ZmqWatcher {
private def utxo(w: GenericWatch): Option[OutPoint] = {
w match {
case w: WatchSpent[_] => Some(OutPoint(w.txId.reverse, w.outputIndex))
case w: WatchSpentBasic[_] => Some(OutPoint(w.txId.reverse, w.outputIndex))
case w: WatchSpent[_] => Some(OutPoint(w.txId, w.outputIndex))
case w: WatchSpentBasic[_] => Some(OutPoint(w.txId, w.outputIndex))
case _ => None
}
}

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.blockchain.bitcoind.rpc
import fr.acinq.eclair.KamonExt
import fr.acinq.eclair.blockchain.Monitoring.{Metrics, Tags}
import fr.acinq.eclair.json.{ByteVector32KmpSerializer, ByteVector32Serializer}
import fr.acinq.eclair.json._
import org.json4s.JsonAST.JValue
import org.json4s.jackson.{JacksonSerialization, Serialization}
import org.json4s.{DefaultFormats, Formats}
@ -34,7 +34,11 @@ import scala.util.{Failure, Success, Try}
class BasicBitcoinJsonRPCClient(rpcAuthMethod: BitcoinJsonRPCAuthMethod, host: String = "127.0.0.1", port: Int = 8332, ssl: Boolean = false, override val wallet: Option[String] = None)(implicit sb: SttpBackend[Future, _]) extends BitcoinJsonRPCClient {
implicit val formats: Formats = DefaultFormats.withBigDecimal + ByteVector32Serializer + ByteVector32KmpSerializer
implicit val formats: Formats = DefaultFormats.withBigDecimal +
ByteVector32Serializer + ByteVector32KmpSerializer +
TxIdSerializer + TxIdKmpSerializer +
BlockHashSerializer + BlockHashKmpSerializer +
BlockIdSerializer + BlockIdKmpSerializer
private val scheme = if (ssl) "https" else "http"
private val serviceUri = wallet match {

View file

@ -64,15 +64,15 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
//------------------------- TRANSACTIONS -------------------------//
def getTransaction(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Transaction] =
def getTransaction(txid: TxId)(implicit ec: ExecutionContext): Future[Transaction] =
getRawTransaction(txid).map(raw => Transaction.read(raw))
private def getRawTransaction(txid: ByteVector32)(implicit ec: ExecutionContext): Future[String] =
private def getRawTransaction(txid: TxId)(implicit ec: ExecutionContext): Future[String] =
rpcClient.invoke("getrawtransaction", txid).collect {
case JString(raw) => raw
}
def getTransactionMeta(txid: ByteVector32)(implicit ec: ExecutionContext): Future[GetTxWithMetaResponse] =
def getTransactionMeta(txid: TxId)(implicit ec: ExecutionContext): Future[GetTxWithMetaResponse] =
for {
tx_opt <- getTransaction(txid).map(Some(_)).recover { case _ => None }
blockchainInfo <- rpcClient.invoke("getblockchaininfo")
@ -80,7 +80,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
} yield GetTxWithMetaResponse(txid, tx_opt, TimestampSecond(timestamp.toLong))
/** Get the number of confirmations of a given transaction. */
def getTxConfirmations(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]] =
def getTxConfirmations(txid: TxId)(implicit ec: ExecutionContext): Future[Option[Int]] =
rpcClient.invoke("getrawtransaction", txid, 1 /* verbose output is needed to get the number of confirmations */)
.map(json => Some((json \ "confirmations").extractOrElse[Int](0)))
.recover {
@ -88,9 +88,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
}
/** Get the hash of the block containing a given transaction. */
private def getTxBlockHash(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[ByteVector32]] =
private def getTxBlockId(txid: TxId)(implicit ec: ExecutionContext): Future[Option[BlockId]] =
rpcClient.invoke("getrawtransaction", txid, 1 /* verbose output is needed to get the block hash */)
.map(json => (json \ "blockhash").extractOpt[String].map(ByteVector32.fromValidHex))
.map(json => (json \ "blockhash").extractOpt[String].map(b => BlockId(ByteVector32.fromValidHex(b))))
.recover {
case t: JsonRPCError if t.error.code == -5 => None // Invalid or non-wallet transaction id (code: -5)
}
@ -99,13 +99,13 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
* @return a Future[height, index] where height is the height of the block where this transaction was published, and
* index is the index of the transaction in that block.
*/
def getTransactionShortId(txid: ByteVector32)(implicit ec: ExecutionContext): Future[(BlockHeight, Int)] =
def getTransactionShortId(txid: TxId)(implicit ec: ExecutionContext): Future[(BlockHeight, Int)] =
for {
Some(blockHash) <- getTxBlockHash(txid)
json <- rpcClient.invoke("getblock", blockHash)
Some(blockId) <- getTxBlockId(txid)
json <- rpcClient.invoke("getblock", blockId)
JInt(height) = json \ "height"
JArray(txs) = json \ "tx"
index = txs.indexOf(JString(txid.toHex))
index = txs.indexOf(JString(txid.value.toHex))
} yield (BlockHeight(height.toInt), index)
/**
@ -115,7 +115,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
* (not in the blockchain nor in the mempool) but could reappear later and be spendable at that point. If you want to
* ensure that an output is not spendable anymore, you should use [[isTransactionOutputSpent]].
*/
def isTransactionOutputSpendable(txid: ByteVector32, outputIndex: Int, includeMempool: Boolean)(implicit ec: ExecutionContext): Future[Boolean] =
def isTransactionOutputSpendable(txid: TxId, outputIndex: Int, includeMempool: Boolean)(implicit ec: ExecutionContext): Future[Boolean] =
for {
json <- rpcClient.invoke("gettxout", txid, outputIndex, includeMempool)
} yield json != JNull
@ -124,7 +124,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
* Return true if this output has already been spent by a confirmed transaction.
* Note that a reorg may invalidate the result of this function and make a spent output spendable again.
*/
private def isTransactionOutputSpent(txid: ByteVector32, outputIndex: Int)(implicit ec: ExecutionContext): Future[Boolean] = {
private def isTransactionOutputSpent(txid: TxId, outputIndex: Int)(implicit ec: ExecutionContext): Future[Boolean] = {
getTxConfirmations(txid).flatMap {
case Some(confirmations) if confirmations > 0 =>
// There is an important limitation when using isTransactionOutputSpendable: if it returns false, it can mean a
@ -170,9 +170,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
} yield doubleSpent
/** Search for mempool transaction spending a given output. */
def lookForMempoolSpendingTx(txid: ByteVector32, outputIndex: Int)(implicit ec: ExecutionContext): Future[Transaction] = {
def lookForMempoolSpendingTx(txid: TxId, outputIndex: Int)(implicit ec: ExecutionContext): Future[Transaction] = {
rpcClient.invoke("gettxspendingprevout", Seq(OutpointArg(txid, outputIndex))).collect {
case JArray(results) => results.flatMap(result => (result \ "spendingtxid").extractOpt[String].map(ByteVector32.fromValidHex))
case JArray(results) => results.flatMap(result => (result \ "spendingtxid").extractOpt[String].map(TxId.fromValidHex))
}.flatMap { spendingTxIds =>
spendingTxIds.headOption match {
case Some(spendingTxId) => getTransaction(spendingTxId)
@ -186,31 +186,28 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
* It isn't useful to look at the whole blockchain history: if the transaction was confirmed long ago, an attacker
* will have already claimed all possible outputs and there's nothing we can do about it.
*
* @param blockhash_opt hash of a block *after* the output has been spent. If not provided, we will use the blockchain tip.
* @param blockHash_opt hash of a block *after* the output has been spent. If not provided, we will use the blockchain tip.
* @param txid id of the transaction output that has been spent.
* @param outputIndex index of the transaction output that has been spent.
* @param limit maximum number of previous blocks to scan.
* @return the transaction spending the given output.
*/
def lookForSpendingTx(blockhash_opt: Option[ByteVector32], txid: ByteVector32, outputIndex: Int, limit: Int)(implicit ec: ExecutionContext): Future[Transaction] = {
lookForSpendingTx(blockhash_opt.map(KotlinUtils.scala2kmp), KotlinUtils.scala2kmp(txid), outputIndex, limit)
}
def lookForSpendingTx(blockhash_opt: Option[fr.acinq.bitcoin.ByteVector32], txid: fr.acinq.bitcoin.ByteVector32, outputIndex: Int, limit: Int)(implicit ec: ExecutionContext): Future[Transaction] =
def lookForSpendingTx(blockHash_opt: Option[BlockHash], txid: TxId, outputIndex: Int, limit: Int)(implicit ec: ExecutionContext): Future[Transaction] = {
for {
blockhash <- blockhash_opt match {
case Some(b) => Future.successful(b)
case None => rpcClient.invoke("getbestblockhash").collect { case JString(b) => ByteVector32.fromValidHex(b) }
blockId <- blockHash_opt match {
case Some(blockHash) => Future.successful(BlockId(blockHash))
// NB: bitcoind confusingly returns the blockId instead of the blockHash.
case None => rpcClient.invoke("getbestblockhash").collect { case JString(blockId) => BlockId(ByteVector32.fromValidHex(blockId)) }
}
// with a verbosity of 0, getblock returns the raw serialized block
block <- rpcClient.invoke("getblock", blockhash, 0).collect { case JString(b) => Block.read(b) }
prevblockhash = block.header.hashPreviousBlock.reversed()
res <- block.tx.asScala.find(tx => tx.txIn.asScala.exists(i => i.outPoint.txid == txid && i.outPoint.index == outputIndex)) match {
block <- rpcClient.invoke("getblock", blockId, 0).collect { case JString(b) => Block.read(b) }
res <- block.tx.asScala.find(tx => tx.txIn.asScala.exists(i => i.outPoint.txid == KotlinUtils.scala2kmp(txid) && i.outPoint.index == outputIndex)) match {
case Some(tx) => Future.successful(KotlinUtils.kmp2scala(tx))
case None if limit > 0 => lookForSpendingTx(Some(prevblockhash), txid, outputIndex, limit - 1)
case None if limit > 0 => lookForSpendingTx(Some(KotlinUtils.kmp2scala(block.header.hashPreviousBlock)), txid, outputIndex, limit - 1)
case None => Future.failed(new RuntimeException(s"couldn't find tx spending $txid:$outputIndex in the blockchain"))
}
} yield res
}
def listTransactions(count: Int, skip: Int)(implicit ec: ExecutionContext): Future[List[WalletTx]] = rpcClient.invoke("listtransactions", "*", count, skip).map {
case JArray(txs) => txs.map(tx => {
@ -222,14 +219,14 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
case _ => Satoshi(0)
}
val JInt(confirmations) = tx \ "confirmations"
// while transactions are still in the mempool, block hash will no be included
val blockHash = tx \ "blockhash" match {
case JString(blockHash) => ByteVector32.fromValidHex(blockHash)
case _ => ByteVector32.Zeroes
// while transactions are still in the mempool, blockId will not be included
val blockId_opt = tx \ "blockhash" match {
case JString(blockId) => Some(BlockId(ByteVector32.fromValidHex(blockId)))
case _ => None
}
val JString(txid) = tx \ "txid"
val JInt(timestamp) = tx \ "time"
WalletTx(address, toSatoshi(amount), fee, blockHash, confirmations.toLong, ByteVector32.fromValidHex(txid), timestamp.toLong)
WalletTx(address, toSatoshi(amount), fee, blockId_opt, confirmations.toLong, TxId.fromValidHex(txid), timestamp.toLong)
}).reverse
case _ => Nil
}
@ -409,9 +406,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
}
/** Recursively fetch unconfirmed parents and return the complete unconfirmed ancestors tree. */
def getMempoolPackage(leaves: Set[ByteVector32])(implicit ec: ExecutionContext): Future[Map[ByteVector32, MempoolTx]] = getMempoolPackage(leaves, Map.empty)
def getMempoolPackage(leaves: Set[TxId])(implicit ec: ExecutionContext): Future[Map[TxId, MempoolTx]] = getMempoolPackage(leaves, Map.empty)
private def getMempoolPackage(leaves: Set[ByteVector32], current: Map[ByteVector32, MempoolTx])(implicit ec: ExecutionContext): Future[Map[ByteVector32, MempoolTx]] = {
private def getMempoolPackage(leaves: Set[TxId], current: Map[TxId, MempoolTx])(implicit ec: ExecutionContext): Future[Map[TxId, MempoolTx]] = {
Future.sequence(leaves.map(txid => getMempoolTx(txid))).flatMap(txs => {
val current2 = current.concat(txs.map(tx => tx.txid -> tx))
val remainingParents = txs.flatMap(_.unconfirmedParents) -- current2.keySet
@ -468,9 +465,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
*
* @return the transaction id (txid)
*/
def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[ByteVector32] =
def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[TxId] =
rpcClient.invoke("sendrawtransaction", tx.toString()).collect {
case JString(txid) => ByteVector32.fromValidHex(txid)
case JString(txid) => TxId.fromValidHex(txid)
}.recoverWith {
case JsonRPCError(Error(-27, _)) =>
// "transaction already in block chain (code: -27)"
@ -485,7 +482,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
* This method can be used to replace "stuck" or evicted transactions.
* It only works on transactions which are not included in a block and are not currently in the mempool.
*/
def abandonTransaction(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Boolean] = {
def abandonTransaction(txId: TxId)(implicit ec: ExecutionContext): Future[Boolean] = {
rpcClient.invoke("abandontransaction", txId).map(_ => true).recover(_ => false)
}
@ -495,7 +492,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
case JArray(locks) => locks.map(item => {
val JString(txid) = item \ "txid"
val JInt(vout) = item \ "vout"
OutPoint(ByteVector32.fromValidHex(txid).reverse, vout.toInt)
OutPoint(TxId.fromValidHex(txid), vout.toInt)
}).toSet
}
}
@ -544,9 +541,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
// and that the address and public key match
onChainKeyManager_opt match {
case Some(keyManager) =>
// TODO: bitcoin-kmp doesn't accept 'h' for hardened indexes, we should fix this.
val keyPath1 = keyPath.replace('h', '\'')
val computed = keyManager.derivePublicKey(DeterministicWallet.KeyPath(keyPath1))
val computed = keyManager.derivePublicKey(DeterministicWallet.KeyPath(keyPath))
if (computed != (extracted, address)) return Future.failed(new RuntimeException("cannot recompute pubkey generated by bitcoin core"))
case None => ()
}
@ -583,7 +578,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
* @param feeratePerKw fee rate
* @return the txid of the sending tx.
*/
def sendToPubkeyScript(pubkeyScript: ByteVector, amount: Satoshi, feeratePerKw: FeeratePerKw)(implicit ec: ExecutionContext): Future[ByteVector32] = {
def sendToPubkeyScript(pubkeyScript: ByteVector, amount: Satoshi, feeratePerKw: FeeratePerKw)(implicit ec: ExecutionContext): Future[TxId] = {
import KotlinUtils._
val theirOutput = TxOut(amount, pubkeyScript)
@ -603,10 +598,10 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
} yield txid
}
def sendToPubkeyScript(pubkeyScript: Seq[ScriptElt], amount: Satoshi, feeratePerKw: FeeratePerKw)(implicit ec: ExecutionContext): Future[ByteVector32] = sendToPubkeyScript(Script.write(pubkeyScript), amount, feeratePerKw)
def sendToPubkeyScript(pubkeyScript: Seq[ScriptElt], amount: Satoshi, feeratePerKw: FeeratePerKw)(implicit ec: ExecutionContext): Future[TxId] = sendToPubkeyScript(Script.write(pubkeyScript), amount, feeratePerKw)
/** Calls Bitcoin Core's sendtoaddress RPC call directly. Will fail if wallet is using an external signer. */
def sendToAddress(address: String, amount: Satoshi, confirmationTarget: Long)(implicit ec: ExecutionContext): Future[ByteVector32] = {
def sendToAddress(address: String, amount: Satoshi, confirmationTarget: Long)(implicit ec: ExecutionContext): Future[TxId] = {
rpcClient.invoke(
"sendtoaddress",
address,
@ -616,7 +611,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
false, // subtractfeefromamount
true, // replaceable
confirmationTarget).collect {
case JString(txid) => ByteVector32.fromValidHex(txid)
case JString(txid) => TxId.fromValidHex(txid)
}
}
@ -624,12 +619,12 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
def getMempool()(implicit ec: ExecutionContext): Future[Seq[Transaction]] =
for {
txids <- rpcClient.invoke("getrawmempool").map(json => json.extract[List[String]].map(ByteVector32.fromValidHex))
txids <- rpcClient.invoke("getrawmempool").map(json => json.extract[List[String]].map(TxId.fromValidHex))
// NB: if a transaction is evicted before we've called getTransaction, we need to ignore it instead of failing.
txs <- Future.sequence(txids.map(getTransaction(_).map(Some(_)).recover { case _ => None }))
} yield txs.flatten
def getMempoolTx(txid: ByteVector32)(implicit ec: ExecutionContext): Future[MempoolTx] = {
def getMempoolTx(txid: TxId)(implicit ec: ExecutionContext): Future[MempoolTx] = {
rpcClient.invoke("getmempoolentry", txid).map(json => {
val JInt(vsize) = json \ "vsize"
val JInt(weight) = json \ "weight"
@ -639,7 +634,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
val JDecimal(ancestorFees) = json \ "fees" \ "ancestor"
val JDecimal(descendantFees) = json \ "fees" \ "descendant"
val JBool(replaceable) = json \ "bip125-replaceable"
val unconfirmedParents = (json \ "depends").extract[List[String]].map(ByteVector32.fromValidHex).toSet
val unconfirmedParents = (json \ "depends").extract[List[String]].map(TxId.fromValidHex).toSet
// NB: bitcoind counts the transaction itself as its own ancestor and descendant, which is confusing: we fix that by decrementing these counters.
MempoolTx(txid, vsize.toLong, weight.toLong, replaceable, toSatoshi(fees), ancestorCount.toInt - 1, toSatoshi(ancestorFees), descendantCount.toInt - 1, toSatoshi(descendantFees), unconfirmedParents)
})
@ -662,11 +657,11 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
def validate(c: ChannelAnnouncement)(implicit ec: ExecutionContext): Future[ValidateResult] = {
val TxCoordinates(blockHeight, txIndex, outputIndex) = coordinates(c.shortChannelId)
for {
blockHash <- rpcClient.invoke("getblockhash", blockHeight.toInt).map(_.extractOpt[String].map(ByteVector32.fromValidHex).getOrElse(ByteVector32.Zeroes))
txid: ByteVector32 <- rpcClient.invoke("getblock", blockHash).map(json => Try {
blockId <- rpcClient.invoke("getblockhash", blockHeight.toInt).map(_.extractOpt[String].map(b => BlockId(ByteVector32.fromValidHex(b))).getOrElse(BlockId(ByteVector32.Zeroes)))
txid <- rpcClient.invoke("getblock", blockId).map(json => Try {
val JArray(txs) = json \ "tx"
ByteVector32.fromValidHex(txs(txIndex).extract[String])
}.getOrElse(ByteVector32.Zeroes))
TxId.fromValidHex(txs(txIndex).extract[String])
}.getOrElse(TxId(ByteVector32.Zeroes)))
tx <- getRawTransaction(txid)
unspent <- isTransactionOutputSpendable(txid, outputIndex, includeMempool = true)
fundingTxStatus <- if (unspent) {
@ -690,7 +685,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
case JString(label) => Some(label)
case _ => None
}
Utxo(ByteVector32.fromValidHex(txid), (amount.doubleValue * 1000).millibtc, confirmations.toLong, safe, label)
Utxo(TxId.fromValidHex(txid), (amount.doubleValue * 1000).millibtc, confirmations.toLong, safe, label)
})
}
@ -705,7 +700,7 @@ object BitcoinCoreClient {
case class InputWeight(txid: String, vout: Long, weight: Long)
object InputWeight {
def apply(outPoint: OutPoint, weight: Long): InputWeight = InputWeight(outPoint.txid.toHex, outPoint.index, weight)
def apply(outPoint: OutPoint, weight: Long): InputWeight = InputWeight(outPoint.txid.value.toHex, outPoint.index, weight)
}
case class FundTransactionOptions(feeRate: BigDecimal, replaceable: Boolean, lockUnspents: Boolean, changePosition: Option[Int], input_weights: Option[Seq[InputWeight]])
@ -744,14 +739,14 @@ object BitcoinCoreClient {
* @param descendantFees transactions fees for the package consisting of this transaction and its unconfirmed children (without its unconfirmed parents).
* @param unconfirmedParents unconfirmed transactions used as inputs for this transaction.
*/
case class MempoolTx(txid: ByteVector32, vsize: Long, weight: Long, replaceable: Boolean, fees: Satoshi, ancestorCount: Int, ancestorFees: Satoshi, descendantCount: Int, descendantFees: Satoshi, unconfirmedParents: Set[ByteVector32])
case class MempoolTx(txid: TxId, vsize: Long, weight: Long, replaceable: Boolean, fees: Satoshi, ancestorCount: Int, ancestorFees: Satoshi, descendantCount: Int, descendantFees: Satoshi, unconfirmedParents: Set[TxId])
case class WalletTx(address: String, amount: Satoshi, fees: Satoshi, blockHash: ByteVector32, confirmations: Long, txid: ByteVector32, timestamp: Long)
case class WalletTx(address: String, amount: Satoshi, fees: Satoshi, blockId_opt: Option[BlockId], confirmations: Long, txid: TxId, timestamp: Long)
/** Outpoint used as RPC argument. */
case class OutpointArg(txid: ByteVector32, vout: Long)
case class OutpointArg(txid: TxId, vout: Long)
case class Utxo(txid: ByteVector32, amount: MilliBtc, confirmations: Long, safe: Boolean, label_opt: Option[String])
case class Utxo(txid: TxId, amount: MilliBtc, confirmations: Long, safe: Boolean, label_opt: Option[String])
def toSatoshi(btcAmount: BigDecimal): Satoshi = Satoshi(btcAmount.bigDecimal.scaleByPowerOfTen(8).longValue)

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.blockchain.bitcoind.zmq
import akka.Done
import akka.actor.{Actor, ActorLogging}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockId, ByteVector32, Transaction}
import fr.acinq.eclair.blockchain.{NewBlock, NewTransaction}
import org.zeromq.ZMQ.Event
import org.zeromq.{SocketType, ZContext, ZMQ, ZMsg}
@ -92,9 +92,9 @@ class ZMQActor(address: String, topic: String, connected: Option[Promise[Done]]
case msg: ZMsg => msg.popString() match {
case "hashblock" =>
val blockHash = ByteVector32(ByteVector(msg.pop().getData))
log.debug("received blockhash={}", blockHash)
context.system.eventStream.publish(NewBlock(blockHash))
val blockId = BlockId(ByteVector32(ByteVector(msg.pop().getData)))
log.debug("received blockId={}", blockId)
context.system.eventStream.publish(NewBlock(blockId))
case "rawtx" =>
val tx = Transaction.read(msg.pop().getData)
log.debug("received txid={}", tx.txid)

View file

@ -104,10 +104,10 @@ object BlockchainWatchdog {
case CheckLatestHeaders(blockHeight) =>
val id = UUID.randomUUID()
if (headersOverDnsEnabled) {
context.spawn(HeadersOverDns(nodeParams.chainHash, blockHeight), s"${HeadersOverDns.Source}-${blockHeight}-$id") ! HeadersOverDns.CheckLatestHeaders(context.self)
context.spawn(HeadersOverDns(nodeParams.chainHash, blockHeight), s"${HeadersOverDns.Source}-$blockHeight-$id") ! HeadersOverDns.CheckLatestHeaders(context.self)
}
explorers.foreach { explorer =>
context.spawn(ExplorerApi(nodeParams.chainHash, blockHeight, explorer), s"${explorer.name}-${blockHeight}-$id") ! ExplorerApi.CheckLatestHeaders(context.self)
context.spawn(ExplorerApi(nodeParams.chainHash, blockHeight, explorer), s"${explorer.name}-$blockHeight-$id") ! ExplorerApi.CheckLatestHeaders(context.self)
}
Behaviors.same
case headers@LatestHeaders(blockHeight, blockHeaders, source) =>

View file

@ -21,7 +21,7 @@ import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import akka.actor.typed.{ActorRef, Behavior}
import akka.pattern.after
import fr.acinq.bitcoin.BlockHeader
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, BlockId, ByteVector32}
import fr.acinq.eclair.blockchain.watchdogs.BlockchainWatchdog.{BlockHeaderAt, LatestHeaders}
import fr.acinq.eclair.blockchain.watchdogs.Monitoring.{Metrics, Tags}
import fr.acinq.eclair.tor.Socks5ProxyParams
@ -56,7 +56,7 @@ object ExplorerApi {
/** Explorer friendly-name. */
def name: String
/** Map from chainHash to explorer API URI. */
def baseUris: Map[ByteVector32, Uri]
def baseUris: Map[BlockHash, Uri]
/** Fetch latest headers from the explorer. */
def getLatestHeaders(baseUri: Uri, currentBlockHeight: BlockHeight)(implicit context: ActorContext[Command]): Future[LatestHeaders]
// @formatter:on
@ -69,7 +69,7 @@ object ExplorerApi {
private case class WrappedFailure(e: Throwable) extends Command
// @formatter:on
def apply(chainHash: ByteVector32, currentBlockHeight: BlockHeight, explorer: Explorer): Behavior[Command] = {
def apply(chainHash: BlockHash, currentBlockHeight: BlockHeight, explorer: Explorer): Behavior[Command] = {
Behaviors.setup { context =>
Behaviors.receiveMessage {
case CheckLatestHeaders(replyTo) =>
@ -164,9 +164,9 @@ object ExplorerApi {
val JString(time) = block \ "time"
val JInt(bits) = block \ "bits"
val JInt(nonce) = block \ "nonce"
val previousBlockHash = (block \ "prev_block").extractOpt[String].map(ByteVector32.fromValidHex(_).reverse).getOrElse(ByteVector32.Zeroes)
val previousBlockId = (block \ "prev_block").extractOpt[String].map(h => BlockId(ByteVector32.fromValidHex(h))).getOrElse(BlockId(ByteVector32.Zeroes))
val merkleRoot = (block \ "mrkl_root").extractOpt[String].map(ByteVector32.fromValidHex(_).reverse).getOrElse(ByteVector32.Zeroes)
val header = new BlockHeader(version.toLong, previousBlockHash, merkleRoot, OffsetDateTime.parse(time).toEpochSecond, bits.toLong, nonce.toLong)
val header = new BlockHeader(version.toLong, BlockHash(previousBlockId), merkleRoot, OffsetDateTime.parse(time).toEpochSecond, bits.toLong, nonce.toLong)
Seq(BlockHeaderAt(BlockHeight(height.toLong), header))
})
} yield header
@ -193,9 +193,9 @@ object ExplorerApi {
val JInt(time) = block \ "timestamp"
val JInt(bits) = block \ "bits"
val JInt(nonce) = block \ "nonce"
val previousBlockHash = (block \ "previousblockhash").extractOpt[String].map(ByteVector32.fromValidHex(_).reverse).getOrElse(ByteVector32.Zeroes)
val previousBlockId = (block \ "previousblockhash").extractOpt[String].map(h => BlockId(ByteVector32.fromValidHex(h))).getOrElse(BlockId(ByteVector32.Zeroes))
val merkleRoot = (block \ "merkle_root").extractOpt[String].map(ByteVector32.fromValidHex(_).reverse).getOrElse(ByteVector32.Zeroes)
val header = new BlockHeader(version.toLong, previousBlockHash, merkleRoot, time.toLong, bits.toLong, nonce.toLong)
val header = new BlockHeader(version.toLong, BlockHash(previousBlockId), merkleRoot, time.toLong, bits.toLong, nonce.toLong)
BlockHeaderAt(BlockHeight(height.toLong), header)
}))
.map(headers => LatestHeaders(currentBlockHeight, headers.filter(_.blockHeight >= currentBlockHeight).toSet, name))

View file

@ -22,8 +22,8 @@ import akka.actor.typed.scaladsl.adapter.TypedActorRefOps
import akka.actor.typed.{ActorRef, Behavior}
import akka.io.dns.{AAAARecord, DnsProtocol}
import akka.io.{Dns, IO}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32}
import fr.acinq.bitcoin.BlockHeader
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash}
import fr.acinq.eclair.BlockHeight
import fr.acinq.eclair.blockchain.watchdogs.BlockchainWatchdog.BlockHeaderAt
import fr.acinq.eclair.blockchain.watchdogs.Monitoring.{Metrics, Tags}
@ -46,7 +46,7 @@ object HeadersOverDns {
private case class WrappedDnsFailed(cause: Throwable) extends Command
// @formatter:on
def apply(chainHash: ByteVector32, currentBlockHeight: BlockHeight): Behavior[Command] = {
def apply(chainHash: BlockHash, currentBlockHeight: BlockHeight): Behavior[Command] = {
Behaviors.setup { context =>
val dnsAdapters = {
context.messageAdapter[DnsProtocol.Resolved](WrappedDnsResolved)
@ -87,7 +87,7 @@ object HeadersOverDns {
stopOrCollect(replyTo, currentBlockHeight, received1, remaining - 1)
case WrappedDnsFailed(ex) =>
context.log.warn("bitcoinheaders.net failed to resolve: {}", ex)
context.log.warn(s"bitcoinheaders.net failed to resolve: $ex")
stopOrCollect(replyTo, currentBlockHeight, received, remaining - 1)
case _ => Behaviors.unhandled

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.channel
import akka.actor.{ActorRef, PossiblyHarmful, typed}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Transaction, TxId, TxOut}
import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
import fr.acinq.eclair.channel.LocalFundingStatus.DualFundedUnconfirmedFundingTx
import fr.acinq.eclair.channel.fund.InteractiveTxBuilder._
@ -263,8 +263,8 @@ object HtlcResult {
final case class RES_ADD_SETTLED[+O <: Origin, +R <: HtlcResult](origin: O, htlc: UpdateAddHtlc, result: R) extends CommandSuccess[CMD_ADD_HTLC]
/** other specific responses */
final case class RES_BUMP_FUNDING_FEE(rbfIndex: Int, fundingTxId: ByteVector32, fee: Satoshi) extends CommandSuccess[CMD_BUMP_FUNDING_FEE]
final case class RES_SPLICE(fundingTxIndex: Long, fundingTxId: ByteVector32, capacity: Satoshi, balance: MilliSatoshi) extends CommandSuccess[CMD_SPLICE]
final case class RES_BUMP_FUNDING_FEE(rbfIndex: Int, fundingTxId: TxId, fee: Satoshi) extends CommandSuccess[CMD_BUMP_FUNDING_FEE]
final case class RES_SPLICE(fundingTxIndex: Long, fundingTxId: TxId, capacity: Satoshi, balance: MilliSatoshi) extends CommandSuccess[CMD_SPLICE]
final case class RES_GET_CHANNEL_STATE(state: ChannelState) extends CommandSuccess[CMD_GET_CHANNEL_STATE]
final case class RES_GET_CHANNEL_DATA[+D <: ChannelData](data: D) extends CommandSuccess[CMD_GET_CHANNEL_DATA]
final case class RES_GET_CHANNEL_INFO(nodeId: PublicKey, channelId: ByteVector32, channel: ActorRef, state: ChannelState, data: ChannelData) extends CommandSuccess[CMD_GET_CHANNEL_INFO]

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, Satoshi, Transaction, TxId}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.wire.protocol
import fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, InteractiveTxMessage, UpdateAddHtlc}
@ -36,7 +36,7 @@ case class RemoteError(e: protocol.Error) extends ChannelError
class ChannelException(val channelId: ByteVector32, message: String) extends RuntimeException(message)
// @formatter:off
case class InvalidChainHash (override val channelId: ByteVector32, local: ByteVector32, remote: ByteVector32) extends ChannelException(channelId, s"invalid chainHash (local=$local remote=$remote)")
case class InvalidChainHash (override val channelId: ByteVector32, local: BlockHash, remote: BlockHash) extends ChannelException(channelId, s"invalid chainHash (local=$local remote=$remote)")
case class FundingAmountTooLow (override val channelId: ByteVector32, fundingAmount: Satoshi, min: Satoshi) extends ChannelException(channelId, s"invalid funding_amount=$fundingAmount (min=$min)")
case class FundingAmountTooHigh (override val channelId: ByteVector32, fundingAmount: Satoshi, max: Satoshi) extends ChannelException(channelId, s"invalid funding_amount=$fundingAmount (max=$max)")
case class InvalidFundingBalances (override val channelId: ByteVector32, fundingAmount: Satoshi, localBalance: MilliSatoshi, remoteBalance: MilliSatoshi) extends ChannelException(channelId, s"invalid balances funding_amount=$fundingAmount local=$localBalance remote=$remoteBalance")
@ -56,10 +56,10 @@ case class InvalidFundingTx (override val channelId: Byte
case class InvalidSerialId (override val channelId: ByteVector32, serialId: UInt64) extends ChannelException(channelId, s"invalid serial_id=${serialId.toByteVector.toHex}")
case class DuplicateSerialId (override val channelId: ByteVector32, serialId: UInt64) extends ChannelException(channelId, s"duplicate serial_id=${serialId.toByteVector.toHex}")
case class UnknownSerialId (override val channelId: ByteVector32, serialId: UInt64) extends ChannelException(channelId, s"unknown serial_id=${serialId.toByteVector.toHex}")
case class DuplicateInput (override val channelId: ByteVector32, serialId: UInt64, previousTxId: ByteVector32, previousTxOutput: Long) extends ChannelException(channelId, s"duplicate input $previousTxId:$previousTxOutput (serial_id=${serialId.toByteVector.toHex})")
case class InputOutOfBounds (override val channelId: ByteVector32, serialId: UInt64, previousTxId: ByteVector32, previousTxOutput: Long) extends ChannelException(channelId, s"invalid input $previousTxId:$previousTxOutput (serial_id=${serialId.toByteVector.toHex})")
case class NonReplaceableInput (override val channelId: ByteVector32, serialId: UInt64, previousTxId: ByteVector32, previousTxOutput: Long, sequence: Long) extends ChannelException(channelId, s"$previousTxId:$previousTxOutput is not replaceable (serial_id=${serialId.toByteVector.toHex}, nSequence=$sequence)")
case class NonSegwitInput (override val channelId: ByteVector32, serialId: UInt64, previousTxId: ByteVector32, previousTxOutput: Long) extends ChannelException(channelId, s"$previousTxId:$previousTxOutput is not a native segwit input (serial_id=${serialId.toByteVector.toHex})")
case class DuplicateInput (override val channelId: ByteVector32, serialId: UInt64, previousTxId: TxId, previousTxOutput: Long) extends ChannelException(channelId, s"duplicate input $previousTxId:$previousTxOutput (serial_id=${serialId.toByteVector.toHex})")
case class InputOutOfBounds (override val channelId: ByteVector32, serialId: UInt64, previousTxId: TxId, previousTxOutput: Long) extends ChannelException(channelId, s"invalid input $previousTxId:$previousTxOutput (serial_id=${serialId.toByteVector.toHex})")
case class NonReplaceableInput (override val channelId: ByteVector32, serialId: UInt64, previousTxId: TxId, previousTxOutput: Long, sequence: Long) extends ChannelException(channelId, s"$previousTxId:$previousTxOutput is not replaceable (serial_id=${serialId.toByteVector.toHex}, nSequence=$sequence)")
case class NonSegwitInput (override val channelId: ByteVector32, serialId: UInt64, previousTxId: TxId, previousTxOutput: Long) extends ChannelException(channelId, s"$previousTxId:$previousTxOutput is not a native segwit input (serial_id=${serialId.toByteVector.toHex})")
case class PreviousTxMissing (override val channelId: ByteVector32, serialId: UInt64) extends ChannelException(channelId, s"previous tx missing from tx_add_input (serial_id=${serialId.toByteVector.toHex})")
case class InvalidSharedInput (override val channelId: ByteVector32, serialId: UInt64) extends ChannelException(channelId, s"invalid shared tx_add_input (serial_id=${serialId.toByteVector.toHex})")
case class OutputBelowDust (override val channelId: ByteVector32, serialId: UInt64, amount: Satoshi, dustLimit: Satoshi) extends ChannelException(channelId, s"invalid output amount=$amount below dust=$dustLimit (serial_id=${serialId.toByteVector.toHex})")
@ -75,7 +75,7 @@ case class DualFundingAborted (override val channelId: Byte
case class UnexpectedInteractiveTxMessage (override val channelId: ByteVector32, msg: InteractiveTxMessage) extends ChannelException(channelId, s"unexpected interactive-tx message (${msg.getClass.getSimpleName})")
case class UnexpectedFundingSignatures (override val channelId: ByteVector32) extends ChannelException(channelId, "unexpected funding signatures (tx_signatures)")
case class InvalidFundingFeerate (override val channelId: ByteVector32, targetFeerate: FeeratePerKw, actualFeerate: FeeratePerKw) extends ChannelException(channelId, s"invalid funding feerate: target=$targetFeerate actual=$actualFeerate")
case class InvalidFundingSignature (override val channelId: ByteVector32, txId_opt: Option[ByteVector32]) extends ChannelException(channelId, s"invalid funding signature: txId=${txId_opt.map(_.toHex).getOrElse("n/a")}")
case class InvalidFundingSignature (override val channelId: ByteVector32, txId_opt: Option[TxId]) extends ChannelException(channelId, s"invalid funding signature: txId=${txId_opt.map(_.toString()).getOrElse("n/a")}")
case class InvalidRbfFeerate (override val channelId: ByteVector32, proposed: FeeratePerKw, expected: FeeratePerKw) extends ChannelException(channelId, s"invalid rbf attempt: the feerate must be at least $expected, you proposed $proposed")
case class InvalidSpliceRequest (override val channelId: ByteVector32) extends ChannelException(channelId, "invalid splice request")
case class InvalidRbfAlreadyInProgress (override val channelId: ByteVector32) extends ChannelException(channelId, "invalid rbf attempt: the current rbf attempt must be completed or aborted first")
@ -98,17 +98,17 @@ case class InvalidFinalScript (override val channelId: Byte
case class MissingUpfrontShutdownScript (override val channelId: ByteVector32) extends ChannelException(channelId, "missing upfront shutdown script")
case class FundingTxTimedout (override val channelId: ByteVector32) extends ChannelException(channelId, "funding tx timed out")
case class FundingTxDoubleSpent (override val channelId: ByteVector32) extends ChannelException(channelId, "funding tx double spent")
case class FundingTxSpent (override val channelId: ByteVector32, spendingTxId: ByteVector32) extends ChannelException(channelId, s"funding tx has been spent by txId=$spendingTxId")
case class FundingTxSpent (override val channelId: ByteVector32, spendingTxId: TxId) extends ChannelException(channelId, s"funding tx has been spent by txId=$spendingTxId")
case class HtlcsTimedoutDownstream (override val channelId: ByteVector32, htlcs: Set[UpdateAddHtlc]) extends ChannelException(channelId, s"one or more htlcs timed out downstream: ids=${htlcs.take(10).map(_.id).mkString(",")}") // we only display the first 10 ids
case class HtlcsWillTimeoutUpstream (override val channelId: ByteVector32, htlcs: Set[UpdateAddHtlc]) extends ChannelException(channelId, s"one or more htlcs that should be fulfilled are close to timing out upstream: ids=${htlcs.take(10).map(_.id).mkString}") // we only display the first 10 ids
case class HtlcOverriddenByLocalCommit (override val channelId: ByteVector32, htlc: UpdateAddHtlc) extends ChannelException(channelId, s"htlc ${htlc.id} was overridden by local commit")
case class FeerateTooSmall (override val channelId: ByteVector32, remoteFeeratePerKw: FeeratePerKw) extends ChannelException(channelId, s"remote fee rate is too small: remoteFeeratePerKw=${remoteFeeratePerKw.toLong}")
case class FeerateTooDifferent (override val channelId: ByteVector32, localFeeratePerKw: FeeratePerKw, remoteFeeratePerKw: FeeratePerKw) extends ChannelException(channelId, s"local/remote feerates are too different: remoteFeeratePerKw=${remoteFeeratePerKw.toLong} localFeeratePerKw=${localFeeratePerKw.toLong}")
case class InvalidAnnouncementSignatures (override val channelId: ByteVector32, annSigs: AnnouncementSignatures) extends ChannelException(channelId, s"invalid announcement signatures: $annSigs")
case class InvalidCommitmentSignature (override val channelId: ByteVector32, fundingTxId: ByteVector32, fundingTxIndex: Long, unsignedCommitTx: Transaction) extends ChannelException(channelId, s"invalid commitment signature: fundingTxId=$fundingTxId fundingTxIndex=$fundingTxIndex commitTxId=${unsignedCommitTx.txid} commitTx=$unsignedCommitTx")
case class InvalidHtlcSignature (override val channelId: ByteVector32, txId: ByteVector32) extends ChannelException(channelId, s"invalid htlc signature: txId=$txId")
case class InvalidCloseSignature (override val channelId: ByteVector32, txId: ByteVector32) extends ChannelException(channelId, s"invalid close signature: txId=$txId")
case class InvalidCloseAmountBelowDust (override val channelId: ByteVector32, txId: ByteVector32) extends ChannelException(channelId, s"invalid closing tx: some outputs are below dust: txId=$txId")
case class InvalidCommitmentSignature (override val channelId: ByteVector32, fundingTxId: TxId, fundingTxIndex: Long, unsignedCommitTx: Transaction) extends ChannelException(channelId, s"invalid commitment signature: fundingTxId=$fundingTxId fundingTxIndex=$fundingTxIndex commitTxId=${unsignedCommitTx.txid} commitTx=$unsignedCommitTx")
case class InvalidHtlcSignature (override val channelId: ByteVector32, txId: TxId) extends ChannelException(channelId, s"invalid htlc signature: txId=$txId")
case class InvalidCloseSignature (override val channelId: ByteVector32, txId: TxId) extends ChannelException(channelId, s"invalid close signature: txId=$txId")
case class InvalidCloseAmountBelowDust (override val channelId: ByteVector32, txId: TxId) extends ChannelException(channelId, s"invalid closing tx: some outputs are below dust: txId=$txId")
case class CommitSigCountMismatch (override val channelId: ByteVector32, expected: Int, actual: Int) extends ChannelException(channelId, s"commit sig count mismatch: expected=$expected actual=$actual")
case class HtlcSigCountMismatch (override val channelId: ByteVector32, expected: Int, actual: Int) extends ChannelException(channelId, s"htlc sig count mismatch: expected=$expected actual=$actual")
case class ForcedLocalCommit (override val channelId: ByteVector32) extends ChannelException(channelId, s"forced local commit")

View file

@ -3,7 +3,7 @@ package fr.acinq.eclair.channel
import akka.event.LoggingAdapter
import com.softwaremill.quicklens.ModifyPimp
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, Satoshi, SatoshiLong, Script, Transaction}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, Satoshi, SatoshiLong, Script, Transaction, TxId}
import fr.acinq.eclair.blockchain.fee.{FeeratePerByte, FeeratePerKw, FeeratesPerKw, OnChainFeeConf}
import fr.acinq.eclair.channel.Helpers.Closing
import fr.acinq.eclair.channel.Monitoring.{Metrics, Tags}
@ -225,7 +225,7 @@ case class CommitTxAndRemoteSig(commitTx: CommitTx, remoteSig: ByteVector64)
case class LocalCommit(index: Long, spec: CommitmentSpec, commitTxAndRemoteSig: CommitTxAndRemoteSig, htlcTxsAndRemoteSigs: List[HtlcTxAndRemoteSig])
object LocalCommit {
def fromCommitSig(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxId: ByteVector32,
def fromCommitSig(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxId: TxId,
fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo,
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey): Either[ChannelException, LocalCommit] = {
val (localCommitTx, htlcTxs) = Commitment.makeLocalTxs(keyManager, params.channelConfig, params.channelFeatures, localCommitIndex, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, localPerCommitmentPoint, spec)
@ -249,7 +249,7 @@ object LocalCommit {
}
/** The remote commitment maps to a commitment transaction that only our peer can sign and broadcast. */
case class RemoteCommit(index: Long, spec: CommitmentSpec, txid: ByteVector32, remotePerCommitmentPoint: PublicKey) {
case class RemoteCommit(index: Long, spec: CommitmentSpec, txid: TxId, remotePerCommitmentPoint: PublicKey) {
def sign(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo): CommitSig = {
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(keyManager, params.channelConfig, params.channelFeatures, index, params.localParams, params.remoteParams, fundingTxIndex, remoteFundingPubKey, commitInput, remotePerCommitmentPoint, spec)
val sig = keyManager.sign(remoteCommitTx, keyManager.fundingPublicKey(params.localParams.fundingKeyPath, fundingTxIndex), TxOwner.Remote, params.commitmentFormat)
@ -276,7 +276,7 @@ case class Commitment(fundingTxIndex: Long,
localFundingStatus: LocalFundingStatus, remoteFundingStatus: RemoteFundingStatus,
localCommit: LocalCommit, remoteCommit: RemoteCommit, nextRemoteCommit_opt: Option[NextRemoteCommit]) {
val commitInput: InputInfo = localCommit.commitTxAndRemoteSig.commitTx.input
val fundingTxId: ByteVector32 = commitInput.outPoint.txid
val fundingTxId: TxId = commitInput.outPoint.txid
val capacity: Satoshi = commitInput.txOut.amount
/** Channel reserve that applies to our funds. */
@ -1148,7 +1148,7 @@ case class Commitments(params: ChannelParams,
params.channelFeatures.hasFeature(Features.DualFunding) && commitSig.batchSize == 1 && latestRemoteSig == commitSig.signature
}
def localFundingSigs(fundingTxId: ByteVector32): Option[TxSignatures] = {
def localFundingSigs(fundingTxId: TxId): Option[TxSignatures] = {
all.find(_.fundingTxId == fundingTxId).flatMap(_.localFundingStatus.localSigs_opt)
}
@ -1158,7 +1158,7 @@ case class Commitments(params: ChannelParams,
* @param updateMethod This method is tricky: it passes the fundingTxIndex of the commitment corresponding to the
* fundingTxId, because in the remote case we may update several commitments.
*/
private def updateFundingStatus(fundingTxId: ByteVector32, updateMethod: Long => PartialFunction[Commitment, Commitment])(implicit log: LoggingAdapter): Either[Commitments, (Commitments, Commitment)] = {
private def updateFundingStatus(fundingTxId: TxId, updateMethod: Long => PartialFunction[Commitment, Commitment])(implicit log: LoggingAdapter): Either[Commitments, (Commitments, Commitment)] = {
all.find(_.fundingTxId == fundingTxId) match {
case Some(commitment) =>
val commitments1 = copy(
@ -1174,7 +1174,7 @@ case class Commitments(params: ChannelParams,
}
}
def updateLocalFundingStatus(fundingTxId: ByteVector32, status: LocalFundingStatus)(implicit log: LoggingAdapter): Either[Commitments, (Commitments, Commitment)] =
def updateLocalFundingStatus(fundingTxId: TxId, status: LocalFundingStatus)(implicit log: LoggingAdapter): Either[Commitments, (Commitments, Commitment)] =
updateFundingStatus(fundingTxId, _ => {
case c if c.fundingTxId == fundingTxId =>
log.info(s"setting localFundingStatus=${status.getClass.getSimpleName} for fundingTxId=${c.fundingTxId} fundingTxIndex=${c.fundingTxIndex}")
@ -1182,7 +1182,7 @@ case class Commitments(params: ChannelParams,
case c => c
})
def updateRemoteFundingStatus(fundingTxId: ByteVector32)(implicit log: LoggingAdapter): Either[Commitments, (Commitments, Commitment)] =
def updateRemoteFundingStatus(fundingTxId: TxId)(implicit log: LoggingAdapter): Either[Commitments, (Commitments, Commitment)] =
updateFundingStatus(fundingTxId, fundingTxIndex => {
// all funding older than this one are considered locked
case c if c.fundingTxId == fundingTxId || c.fundingTxIndex < fundingTxIndex =>

View file

@ -352,7 +352,7 @@ object Helpers {
object Funding {
def makeFundingInputInfo(fundingTxId: ByteVector32, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: PublicKey, fundingPubkey2: PublicKey): InputInfo = {
def makeFundingInputInfo(fundingTxId: TxId, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: PublicKey, fundingPubkey2: PublicKey): InputInfo = {
val fundingScript = multiSig2of2(fundingPubkey1, fundingPubkey2)
val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript))
InputInfo(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript))
@ -368,7 +368,7 @@ object Helpers {
localFundingAmount: Satoshi, remoteFundingAmount: Satoshi,
localPushAmount: MilliSatoshi, remotePushAmount: MilliSatoshi,
commitTxFeerate: FeeratePerKw,
fundingTxHash: ByteVector32, fundingTxOutputIndex: Int,
fundingTxId: TxId, fundingTxOutputIndex: Int,
remoteFundingPubKey: PublicKey,
remoteFirstPerCommitmentPoint: PublicKey): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx)] = {
makeCommitTxs(keyManager, params,
@ -378,7 +378,7 @@ object Helpers {
localHtlcs = Set.empty,
commitTxFeerate,
fundingTxIndex = 0,
fundingTxHash, fundingTxOutputIndex,
fundingTxId, fundingTxOutputIndex,
remoteFundingPubKey = remoteFundingPubKey, remotePerCommitmentPoint = remoteFirstPerCommitmentPoint,
localCommitmentIndex = 0, remoteCommitmentIndex = 0).map {
case (localSpec, localCommit, remoteSpec, remoteCommit, _) => (localSpec, localCommit, remoteSpec, remoteCommit)
@ -396,7 +396,7 @@ object Helpers {
localHtlcs: Set[DirectedHtlc],
commitTxFeerate: FeeratePerKw,
fundingTxIndex: Long,
fundingTxHash: ByteVector32, fundingTxOutputIndex: Int,
fundingTxId: TxId, fundingTxOutputIndex: Int,
remoteFundingPubKey: PublicKey,
remotePerCommitmentPoint: PublicKey,
localCommitmentIndex: Long, remoteCommitmentIndex: Long): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx, Seq[HtlcTx])] = {
@ -418,7 +418,7 @@ object Helpers {
val fundingPubKey = keyManager.fundingPublicKey(localParams.fundingKeyPath, fundingTxIndex)
val channelKeyPath = keyManager.keyPath(localParams, channelConfig)
val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, fundingAmount, fundingPubKey.publicKey, remoteFundingPubKey)
val commitmentInput = makeFundingInputInfo(fundingTxId, fundingTxOutputIndex, fundingAmount, fundingPubKey.publicKey, remoteFundingPubKey)
val localPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, localCommitmentIndex)
val (localCommitTx, _) = Commitment.makeLocalTxs(keyManager, channelConfig, channelFeatures, localCommitmentIndex, localParams, remoteParams, fundingTxIndex, remoteFundingPubKey, commitmentInput, localPerCommitmentPoint, localSpec)
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(keyManager, channelConfig, channelFeatures, remoteCommitmentIndex, localParams, remoteParams, fundingTxIndex, remoteFundingPubKey, commitmentInput, remotePerCommitmentPoint, remoteSpec)

View file

@ -21,7 +21,7 @@ import akka.actor.typed.scaladsl.adapter.{ClassicActorContextOps, actorRefAdapte
import akka.actor.{Actor, ActorContext, ActorRef, FSM, OneForOneStrategy, PossiblyHarmful, Props, SupervisorStrategy, typed}
import akka.event.Logging.MDC
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi, SatoshiLong, Transaction}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi, SatoshiLong, Transaction, TxId}
import fr.acinq.eclair.Logs.LogCategory
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain.OnChainWallet.MakeFundingTxResponse
@ -161,7 +161,7 @@ object Channel {
private[channel] sealed trait BitcoinEvent extends PossiblyHarmful
private[channel] case object BITCOIN_FUNDING_PUBLISH_FAILED extends BitcoinEvent
private[channel] case object BITCOIN_FUNDING_TIMEOUT extends BitcoinEvent
private[channel] case class BITCOIN_FUNDING_DOUBLE_SPENT(fundingTxIds: Set[ByteVector32]) extends BitcoinEvent
private[channel] case class BITCOIN_FUNDING_DOUBLE_SPENT(fundingTxIds: Set[TxId]) extends BitcoinEvent
// @formatter:on
case object TickChannelOpenTimeout
@ -1135,7 +1135,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
watchFundingConfirmed(w.tx.txid, Some(nodeParams.channelConf.minDepthBlocks), delay_opt = None)
maybeEmitEventsPostSplice(d.shortIds, d.commitments, commitments1)
maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, commitments1)
stay() using d.copy(commitments = commitments1) storing() sending SpliceLocked(d.channelId, w.tx.hash)
stay() using d.copy(commitments = commitments1) storing() sending SpliceLocked(d.channelId, w.tx.txid)
case Left(_) => stay()
}
@ -1144,7 +1144,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
case Right((commitments1, commitment)) =>
val toSend = if (d.commitments.all.exists(c => c.fundingTxId == commitment.fundingTxId && c.localFundingStatus.isInstanceOf[LocalFundingStatus.NotLocked])) {
// this commitment just moved from NotLocked to Locked
Some(SpliceLocked(d.channelId, w.tx.hash))
Some(SpliceLocked(d.channelId, w.tx.txid))
} else {
// this was a zero-conf splice and we already sent our splice_locked
None
@ -1156,7 +1156,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
}
case Event(msg: SpliceLocked, d: DATA_NORMAL) =>
d.commitments.updateRemoteFundingStatus(msg.fundingTxid) match {
d.commitments.updateRemoteFundingStatus(msg.fundingTxId) match {
case Right((commitments1, _)) =>
maybeEmitEventsPostSplice(d.shortIds, d.commitments, commitments1)
maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, commitments1)
@ -1808,7 +1808,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
activeConnection = r
val channelKeyPath = keyManager.keyPath(d.channelParams.localParams, d.channelParams.channelConfig)
val myFirstPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, 0)
val nextFundingTlv: Set[ChannelReestablishTlv] = Set(ChannelReestablishTlv.NextFundingTlv(d.signingSession.fundingTx.txId.reverse))
val nextFundingTlv: Set[ChannelReestablishTlv] = Set(ChannelReestablishTlv.NextFundingTlv(d.signingSession.fundingTx.txId))
val channelReestablish = ChannelReestablish(
channelId = d.channelId,
nextLocalCommitmentNumber = 1,
@ -1828,16 +1828,16 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
val myCurrentPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, d.commitments.localCommitIndex)
val rbfTlv: Set[ChannelReestablishTlv] = d match {
case d: DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED => d.rbfStatus match {
case RbfStatus.RbfWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingTlv(status.fundingTx.txId.reverse))
case RbfStatus.RbfWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingTlv(status.fundingTx.txId))
case _ => d.latestFundingTx.sharedTx match {
case _: InteractiveTxBuilder.PartiallySignedSharedTransaction => Set(ChannelReestablishTlv.NextFundingTlv(d.latestFundingTx.sharedTx.txId.reverse))
case _: InteractiveTxBuilder.PartiallySignedSharedTransaction => Set(ChannelReestablishTlv.NextFundingTlv(d.latestFundingTx.sharedTx.txId))
case _: InteractiveTxBuilder.FullySignedSharedTransaction => Set.empty
}
}
case d: DATA_NORMAL => d.spliceStatus match {
case SpliceStatus.SpliceWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingTlv(status.fundingTx.txId.reverse))
case SpliceStatus.SpliceWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingTlv(status.fundingTx.txId))
case _ => d.commitments.latest.localFundingStatus match {
case LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx: PartiallySignedSharedTransaction, _, _) => Set(ChannelReestablishTlv.NextFundingTlv(fundingTx.txId.reverse))
case LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx: PartiallySignedSharedTransaction, _, _) => Set(ChannelReestablishTlv.NextFundingTlv(fundingTx.txId))
case _ => Set.empty
}
}
@ -1991,7 +1991,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
.filter(c => c.fundingTxIndex > 0) // only consider splice txs
.collectFirst { case c if c.localFundingStatus.isInstanceOf[LocalFundingStatus.Locked] =>
log.debug("re-sending splice_locked for fundingTxId={}", c.fundingTxId)
SpliceLocked(d.channelId, c.fundingTxId.reverse)
SpliceLocked(d.channelId, c.fundingTxId)
}
sendQueue = sendQueue ++ spliceLocked

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.fsm
import akka.actor.Status
import akka.actor.typed.scaladsl.adapter.actorRefAdapter
import akka.pattern.pipe
import fr.acinq.bitcoin.scalacompat.{SatoshiLong, Script}
import fr.acinq.bitcoin.scalacompat.{SatoshiLong, Script, TxHash}
import fr.acinq.eclair.blockchain.OnChainWallet.MakeFundingTxResponse
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
import fr.acinq.eclair.channel.Helpers.Funding
@ -212,7 +212,7 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), d@DATA_WAIT_FOR_FUNDING_INTERNAL(params, fundingAmount, pushMsat, commitTxFeerate, remoteFundingPubKey, remoteFirstPerCommitmentPoint, replyTo)) =>
val temporaryChannelId = params.channelId
// let's create the first commitment tx that spends the yet uncommitted funding tx
Funding.makeFirstCommitTxs(keyManager, params, localFundingAmount = fundingAmount, remoteFundingAmount = 0 sat, localPushAmount = pushMsat, remotePushAmount = 0 msat, commitTxFeerate, fundingTx.hash, fundingTxOutputIndex, remoteFundingPubKey = remoteFundingPubKey, remoteFirstPerCommitmentPoint = remoteFirstPerCommitmentPoint) match {
Funding.makeFirstCommitTxs(keyManager, params, localFundingAmount = fundingAmount, remoteFundingAmount = 0 sat, localPushAmount = pushMsat, remotePushAmount = 0 msat, commitTxFeerate, fundingTx.txid, fundingTxOutputIndex, remoteFundingPubKey = remoteFundingPubKey, remoteFirstPerCommitmentPoint = remoteFirstPerCommitmentPoint) match {
case Left(ex) => handleLocalError(ex, d, None)
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
require(fundingTx.txOut(fundingTxOutputIndex).publicKeyScript == localCommitTx.input.txOut.publicKeyScript, s"pubkey script mismatch!")
@ -220,11 +220,11 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
// signature of their initial commitment tx that pays remote pushMsat
val fundingCreated = FundingCreated(
temporaryChannelId = temporaryChannelId,
fundingTxHash = fundingTx.hash,
fundingTxId = fundingTx.txid,
fundingOutputIndex = fundingTxOutputIndex,
signature = localSigOfRemoteTx
)
val channelId = toLongId(fundingTx.hash, fundingTxOutputIndex)
val channelId = toLongId(fundingTx.txid, fundingTxOutputIndex)
val params1 = params.copy(channelId = channelId)
peer ! ChannelIdAssigned(self, remoteNodeId, temporaryChannelId, channelId) // we notify the peer asap so it knows how to route messages
txPublisher ! SetChannelId(remoteNodeId, channelId)
@ -256,10 +256,10 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
})
when(WAIT_FOR_FUNDING_CREATED)(handleExceptions {
case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig, _), d@DATA_WAIT_FOR_FUNDING_CREATED(params, fundingAmount, pushMsat, commitTxFeerate, remoteFundingPubKey, remoteFirstPerCommitmentPoint)) =>
case Event(FundingCreated(_, fundingTxId, fundingTxOutputIndex, remoteSig, _), d@DATA_WAIT_FOR_FUNDING_CREATED(params, fundingAmount, pushMsat, commitTxFeerate, remoteFundingPubKey, remoteFirstPerCommitmentPoint)) =>
val temporaryChannelId = params.channelId
// they fund the channel with their funding tx, so the money is theirs (but we are paid pushMsat)
Funding.makeFirstCommitTxs(keyManager, params, localFundingAmount = 0 sat, remoteFundingAmount = fundingAmount, localPushAmount = 0 msat, remotePushAmount = pushMsat, commitTxFeerate, fundingTxHash, fundingTxOutputIndex, remoteFundingPubKey = remoteFundingPubKey, remoteFirstPerCommitmentPoint = remoteFirstPerCommitmentPoint) match {
Funding.makeFirstCommitTxs(keyManager, params, localFundingAmount = 0 sat, remoteFundingAmount = fundingAmount, localPushAmount = 0 msat, remotePushAmount = pushMsat, commitTxFeerate, fundingTxId, fundingTxOutputIndex, remoteFundingPubKey = remoteFundingPubKey, remoteFirstPerCommitmentPoint = remoteFirstPerCommitmentPoint) match {
case Left(ex) => handleLocalError(ex, d, None)
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
// check remote signature validity
@ -267,10 +267,10 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
val localSigOfLocalTx = keyManager.sign(localCommitTx, fundingPubKey, TxOwner.Local, params.commitmentFormat)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, fundingPubKey.publicKey, remoteFundingPubKey, localSigOfLocalTx, remoteSig)
Transactions.checkSpendable(signedLocalCommitTx) match {
case Failure(_) => handleLocalError(InvalidCommitmentSignature(temporaryChannelId, fundingTxHash.reverse, fundingTxIndex = 0, localCommitTx.tx), d, None)
case Failure(_) => handleLocalError(InvalidCommitmentSignature(temporaryChannelId, fundingTxId, fundingTxIndex = 0, localCommitTx.tx), d, None)
case Success(_) =>
val localSigOfRemoteTx = keyManager.sign(remoteCommitTx, fundingPubKey, TxOwner.Remote, params.commitmentFormat)
val channelId = toLongId(fundingTxHash, fundingTxOutputIndex)
val channelId = toLongId(fundingTxId, fundingTxOutputIndex)
val fundingSigned = FundingSigned(
channelId = channelId,
signature = localSigOfRemoteTx

View file

@ -19,14 +19,13 @@ package fr.acinq.eclair.channel.fsm
import akka.actor.typed.scaladsl.adapter.{TypedActorRefOps, actorRefAdapter}
import com.softwaremill.quicklens.ModifyPimp
import fr.acinq.bitcoin.ScriptFlags
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Transaction}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Transaction, TxId}
import fr.acinq.eclair.ShortChannelId
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
import fr.acinq.eclair.channel.Helpers.getRelayFees
import fr.acinq.eclair.channel.LocalFundingStatus.{ConfirmedFundingTx, DualFundedUnconfirmedFundingTx, SingleFundedUnconfirmedFundingTx}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.channel.fsm.Channel.{ANNOUNCEMENTS_MINCONF, BroadcastChannelUpdate, PeriodicRefresh, REFRESH_CHANNEL_UPDATE_INTERVAL}
import fr.acinq.eclair.router.Announcements
import fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, ChannelReady, ChannelReadyTlv, TlvStream}
import scala.concurrent.duration.{DurationInt, FiniteDuration}
@ -43,7 +42,7 @@ trait CommonFundingHandlers extends CommonHandlers {
/**
* @param delay_opt optional delay to reduce herd effect at startup.
*/
def watchFundingSpent(commitment: Commitment, additionalKnownSpendingTxs: Set[ByteVector32], delay_opt: Option[FiniteDuration]): Unit = {
def watchFundingSpent(commitment: Commitment, additionalKnownSpendingTxs: Set[TxId], delay_opt: Option[FiniteDuration]): Unit = {
val knownSpendingTxs = Set(commitment.localCommit.commitTxAndRemoteSig.commitTx.tx.txid, commitment.remoteCommit.txid) ++ commitment.nextRemoteCommit_opt.map(_.commit.txid).toSet ++ additionalKnownSpendingTxs
val watch = WatchFundingSpent(self, commitment.commitInput.outPoint.txid, commitment.commitInput.outPoint.index.toInt, knownSpendingTxs)
delay_opt match {
@ -55,7 +54,7 @@ trait CommonFundingHandlers extends CommonHandlers {
/**
* @param delay_opt optional delay to reduce herd effect at startup.
*/
def watchFundingConfirmed(fundingTxId: ByteVector32, minDepth_opt: Option[Long], delay_opt: Option[FiniteDuration]): Unit = {
def watchFundingConfirmed(fundingTxId: TxId, minDepth_opt: Option[Long], delay_opt: Option[FiniteDuration]): Unit = {
val watch = minDepth_opt match {
case Some(fundingMinDepth) => WatchFundingConfirmed(self, fundingTxId, fundingMinDepth)
// When using 0-conf, we make sure that the transaction was successfully published, otherwise there is a risk

View file

@ -17,6 +17,7 @@
package fr.acinq.eclair.channel.fsm
import fr.acinq.bitcoin.scalacompat.{Transaction, TxIn}
import fr.acinq.eclair.NotificationsLogger
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
import fr.acinq.eclair.blockchain.CurrentBlockHeight
import fr.acinq.eclair.channel.Helpers.Closing
@ -26,7 +27,6 @@ import fr.acinq.eclair.channel.fsm.Channel.BITCOIN_FUNDING_DOUBLE_SPENT
import fr.acinq.eclair.channel.fund.InteractiveTxBuilder._
import fr.acinq.eclair.channel.fund.{InteractiveTxBuilder, InteractiveTxSigningSession}
import fr.acinq.eclair.wire.protocol.{ChannelReady, Error}
import fr.acinq.eclair.{Features, NotificationsLogger}
import scala.concurrent.Future
import scala.util.{Failure, Success}

View file

@ -22,7 +22,7 @@ import akka.event.LoggingAdapter
import fr.acinq.bitcoin.ScriptFlags
import fr.acinq.bitcoin.psbt.Psbt
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, LexicographicalOrdering, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxIn, TxOut}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, LexicographicalOrdering, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxId, TxIn, TxOut}
import fr.acinq.eclair.blockchain.OnChainChannelFunder
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
@ -308,13 +308,13 @@ object InteractiveTxBuilder {
// @formatter:off
sealed trait SignedSharedTransaction {
def txId: ByteVector32
def txId: TxId
def tx: SharedTransaction
def localSigs: TxSignatures
def signedTx_opt: Option[Transaction]
}
case class PartiallySignedSharedTransaction(tx: SharedTransaction, localSigs: TxSignatures) extends SignedSharedTransaction {
override val txId: ByteVector32 = tx.buildUnsignedTx().txid
override val txId: TxId = tx.buildUnsignedTx().txid
override val signedTx_opt: Option[Transaction] = None
}
case class FullySignedSharedTransaction(tx: SharedTransaction, localSigs: TxSignatures, remoteSigs: TxSignatures, sharedSigs_opt: Option[ScriptWitness]) extends SignedSharedTransaction {
@ -330,7 +330,7 @@ object InteractiveTxBuilder {
val outputs = (Seq(sharedTxOut) ++ localTxOut ++ remoteTxOut).sortBy(_._1).map(_._2)
Transaction(2, inputs, outputs, lockTime)
}
override val txId: ByteVector32 = signedTx.txid
override val txId: TxId = signedTx.txid
override val signedTx_opt: Option[Transaction] = Some(signedTx)
val feerate: FeeratePerKw = Transactions.fee2rate(tx.fees, signedTx.weight())
}
@ -746,7 +746,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
localHtlcs = purpose.localHtlcs,
purpose.commitTxFeerate,
fundingTxIndex = purpose.fundingTxIndex,
fundingTx.hash, fundingOutputIndex,
fundingTx.txid, fundingOutputIndex,
remotePerCommitmentPoint = purpose.remotePerCommitmentPoint, remoteFundingPubKey = fundingParams.remoteFundingPubKey,
localCommitmentIndex = purpose.localCommitIndex, remoteCommitmentIndex = purpose.remoteCommitIndex) match {
case Left(cause) =>

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.publish
import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
import akka.actor.typed.{ActorRef, Behavior}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Satoshi, Transaction, TxId}
import fr.acinq.eclair.blockchain.CurrentBlockHeight
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
import fr.acinq.eclair.channel.publish.TxPublisher.{TxPublishContext, TxRejectedReason}
@ -59,14 +59,14 @@ object MempoolTxMonitor {
sealed trait TxResult
sealed trait IntermediateTxResult extends TxResult
/** The transaction is still unconfirmed and available in the mempool. */
case class TxInMempool(txid: ByteVector32, blockHeight: BlockHeight, parentConfirmed: Boolean) extends IntermediateTxResult
case class TxInMempool(txid: TxId, blockHeight: BlockHeight, parentConfirmed: Boolean) extends IntermediateTxResult
/** The transaction is confirmed, but hasn't reached min depth yet, we should wait for more confirmations. */
case class TxRecentlyConfirmed(txid: ByteVector32, confirmations: Int) extends IntermediateTxResult
case class TxRecentlyConfirmed(txid: TxId, confirmations: Int) extends IntermediateTxResult
sealed trait FinalTxResult extends TxResult
/** The transaction is confirmed and has reached min depth. */
case class TxDeeplyBuried(tx: Transaction) extends FinalTxResult
/** The transaction has been evicted from the mempool. */
case class TxRejected(txid: ByteVector32, reason: TxPublisher.TxRejectedReason) extends FinalTxResult
case class TxRejected(txid: TxId, reason: TxPublisher.TxRejectedReason) extends FinalTxResult
// @formatter:on
def apply(nodeParams: NodeParams, bitcoinClient: BitcoinCoreClient, txPublishContext: TxPublishContext): Behavior[Command] = {

View file

@ -20,7 +20,7 @@ import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
import akka.actor.typed.{ActorRef, Behavior}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Satoshi, Transaction, TxId}
import fr.acinq.eclair.blockchain.CurrentBlockHeight
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
@ -84,9 +84,9 @@ object TxPublisher {
* @param fee the fee that we're actually paying: it must be set to the mining fee, unless our peer is paying it (in
* which case it must be set to zero here).
*/
case class PublishFinalTx(tx: Transaction, input: OutPoint, desc: String, fee: Satoshi, parentTx_opt: Option[ByteVector32]) extends PublishTx
case class PublishFinalTx(tx: Transaction, input: OutPoint, desc: String, fee: Satoshi, parentTx_opt: Option[TxId]) extends PublishTx
object PublishFinalTx {
def apply(txInfo: TransactionWithInputInfo, fee: Satoshi, parentTx_opt: Option[ByteVector32]): PublishFinalTx = PublishFinalTx(txInfo.tx, txInfo.input.outPoint, txInfo.desc, fee, parentTx_opt)
def apply(txInfo: TransactionWithInputInfo, fee: Satoshi, parentTx_opt: Option[TxId]): PublishFinalTx = PublishFinalTx(txInfo.tx, txInfo.input.outPoint, txInfo.desc, fee, parentTx_opt)
}
/** Publish an unsigned transaction that can be RBF-ed. */
case class PublishReplaceableTx(txInfo: ReplaceableTransactionWithInputInfo, commitment: FullCommitment) extends PublishTx {

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.publish
import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
import akka.actor.typed.{ActorRef, Behavior}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Transaction}
import fr.acinq.bitcoin.scalacompat.{Transaction, TxId}
import fr.acinq.eclair.blockchain.CurrentBlockHeight
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchParentTxConfirmed, WatchParentTxConfirmedTriggered}
@ -47,7 +47,7 @@ object TxTimeLocksMonitor {
case class CheckTx(replyTo: ActorRef[TimeLocksOk], tx: Transaction, desc: String) extends Command
final case class WrappedCurrentBlockHeight(currentBlockHeight: BlockHeight) extends Command
private case object CheckRelativeTimeLock extends Command
private case class ParentTxConfirmed(parentTxId: ByteVector32) extends Command
private case class ParentTxConfirmed(parentTxId: TxId) extends Command
// @formatter:on
def apply(nodeParams: NodeParams, watcher: ActorRef[ZmqWatcher.Command], txPublishContext: TxPublishContext): Behavior[Command] = {
@ -112,7 +112,7 @@ private class TxTimeLocksMonitor(nodeParams: NodeParams,
}
}
def waitForParentsToConfirm(parentTxIds: Set[ByteVector32]): Behavior[Command] = {
def waitForParentsToConfirm(parentTxIds: Set[TxId]): Behavior[Command] = {
Behaviors.receiveMessagePartial {
case ParentTxConfirmed(parentTxId) =>
log.debug("parent tx of {} has been confirmed (parent txid={})", cmd.desc, parentTxId)

View file

@ -20,7 +20,7 @@ import akka.actor.typed.Behavior
import akka.actor.typed.eventstream.EventStream
import akka.actor.typed.scaladsl.Behaviors
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto}
import fr.acinq.bitcoin.scalacompat.{BlockId, ByteVector32, ByteVector64, Crypto}
import fr.acinq.eclair.TimestampMilli
import fr.acinq.eclair.blockchain.NewBlock
import fr.acinq.eclair.channel.ChannelSignatureReceived
@ -47,7 +47,7 @@ object WeakEntropyPool {
// @formatter:off
sealed trait Command
private case object FlushEntropy extends Command
private case class WrappedNewBlock(blockHash: ByteVector32) extends Command
private case class WrappedNewBlock(blockId: BlockId) extends Command
private case class WrappedPaymentRelayed(paymentHash: ByteVector32, relayedAt: TimestampMilli) extends Command
private case class WrappedPeerConnected(nodeId: PublicKey) extends Command
private case class WrappedChannelSignature(wtxid: ByteVector32) extends Command
@ -56,7 +56,7 @@ object WeakEntropyPool {
def apply(collector: EntropyCollector): Behavior[Command] = {
Behaviors.setup { context =>
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewBlock](e => WrappedNewBlock(e.blockHash)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewBlock](e => WrappedNewBlock(e.blockId)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[ChannelPaymentRelayed](e => WrappedPaymentRelayed(e.paymentHash, e.timestamp)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[PeerConnected](e => WrappedPeerConnected(e.nodeId)))
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NodeUpdated](e => WrappedNodeUpdated(e.ann.signature)))
@ -79,7 +79,7 @@ object WeakEntropyPool {
Behaviors.same
}
case WrappedNewBlock(blockHash) => collecting(collector, collect(entropy_opt, blockHash ++ ByteVector.fromLong(System.currentTimeMillis())))
case WrappedNewBlock(blockHash) => collecting(collector, collect(entropy_opt, blockHash.value ++ ByteVector.fromLong(System.currentTimeMillis())))
case WrappedPaymentRelayed(paymentHash, relayedAt) => collecting(collector, collect(entropy_opt, paymentHash ++ ByteVector.fromLong(relayedAt.toLong)))

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.crypto.keymanager
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet._
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, DeterministicWallet}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32, ByteVector64, Crypto, DeterministicWallet}
import fr.acinq.eclair.crypto.Generators
import fr.acinq.eclair.crypto.Monitoring.{Metrics, Tags}
import fr.acinq.eclair.router.Announcements
@ -31,7 +31,7 @@ import kamon.tag.TagSet
import scodec.bits.ByteVector
object LocalChannelKeyManager {
def keyBasePath(chainHash: ByteVector32): List[Long] = (chainHash: @unchecked) match {
def keyBasePath(chainHash: BlockHash): List[Long] = (chainHash: @unchecked) match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(1) :: Nil
case Block.LivenetGenesisBlock.hash => DeterministicWallet.hardened(47) :: DeterministicWallet.hardened(1) :: Nil
}
@ -56,7 +56,7 @@ object LocalChannelKeyManager {
*
* @param seed seed from which the channel keys will be derived
*/
class LocalChannelKeyManager(seed: ByteVector, chainHash: ByteVector32) extends ChannelKeyManager with Logging {
class LocalChannelKeyManager(seed: ByteVector, chainHash: BlockHash) extends ChannelKeyManager with Logging {
private val master = DeterministicWallet.generate(seed)
private val privateKeys: LoadingCache[KeyPath, ExtendedPrivateKey] = CacheBuilder.newBuilder()

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.crypto.keymanager
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.ExtendedPrivateKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, DeterministicWallet}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32, ByteVector64, Crypto, DeterministicWallet}
import fr.acinq.eclair.router.Announcements
import grizzled.slf4j.Logging
import scodec.bits.ByteVector
@ -27,7 +27,7 @@ object LocalNodeKeyManager {
// WARNING: if you change this path, you will change your node id even if the seed remains the same!!!
// Note that the node path and the above channel path are on different branches so even if the
// node key is compromised there is no way to retrieve the wallet keys
def keyBasePath(chainHash: ByteVector32): List[Long] = (chainHash: @unchecked) match {
def keyBasePath(chainHash: BlockHash): List[Long] = (chainHash: @unchecked) match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(0) :: Nil
case Block.LivenetGenesisBlock.hash => DeterministicWallet.hardened(47) :: DeterministicWallet.hardened(0) :: Nil
}
@ -39,7 +39,7 @@ object LocalNodeKeyManager {
*
* @param seed seed from which the node key will be derived
*/
class LocalNodeKeyManager(seed: ByteVector, chainHash: ByteVector32) extends NodeKeyManager with Logging {
class LocalNodeKeyManager(seed: ByteVector, chainHash: BlockHash) extends NodeKeyManager with Logging {
private val master = DeterministicWallet.generate(seed)
override val nodeKey: ExtendedPrivateKey = DeterministicWallet.derivePrivateKey(master, LocalNodeKeyManager.keyBasePath(chainHash))

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.crypto.keymanager
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin.psbt.{Psbt, UpdateFailure}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet._
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Crypto, DeterministicWallet, MnemonicCode, Satoshi, Script, computeBIP84Address}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, Crypto, DeterministicWallet, MnemonicCode, Satoshi, Script, computeBIP84Address}
import fr.acinq.eclair.TimestampSecond
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.{Descriptor, Descriptors}
import grizzled.slf4j.Logging
@ -38,7 +38,7 @@ object LocalOnChainKeyManager extends Logging {
* @param chainHash chain we're on
* @return a LocalOnChainKeyManager instance if a configuration file exists
*/
def load(datadir: File, chainHash: ByteVector32): Option[LocalOnChainKeyManager] = {
def load(datadir: File, chainHash: BlockHash): Option[LocalOnChainKeyManager] = {
// we use a specific file instead of adding values to eclair's configuration file because it is available everywhere
// in the code through the actor system's settings and we'd like to restrict access to the on-chain wallet seed
val file = new File(datadir, "eclair-signer.conf")
@ -63,7 +63,7 @@ object LocalOnChainKeyManager extends Logging {
* Eclair is in charge of signing transactions.
* This is an advanced feature particularly suited when Eclair runs in a secure runtime.
*/
class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector, val walletTimestamp: TimestampSecond, chainHash: ByteVector32) extends OnChainKeyManager with Logging {
class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector, val walletTimestamp: TimestampSecond, chainHash: BlockHash) extends OnChainKeyManager with Logging {
// Master key derived from our seed. We use it to generate a BIP84 wallet that can be used:
// - to generate a watch-only wallet with any BIP84-compatible bitcoin wallet
@ -72,10 +72,10 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
private val master = DeterministicWallet.generate(seed)
private val fingerprint = DeterministicWallet.fingerprint(master) & 0xFFFFFFFFL
private val fingerPrintHex = String.format("%8s", fingerprint.toHexString).replace(' ', '0')
// Root BIP32 on-chain path: we use BIP84 (p2wpkh) paths: m/84'/{0'/1'}
// Root BIP32 on-chain path: we use BIP84 (p2wpkh) paths: m/84h/{0h/1h}
private val rootPath = chainHash match {
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => "84'/1'"
case Block.LivenetGenesisBlock.hash => "84'/0'"
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => "84h/1h"
case Block.LivenetGenesisBlock.hash => "84h/0h"
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
}
private val rootKey = DeterministicWallet.derivePrivateKey(master, KeyPath(rootPath))
@ -86,7 +86,7 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
case Block.LivenetGenesisBlock.hash => zpub
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
}
// master pubkey for account 0 is m/84'/{0'/1'}/0'
// master pubkey for account 0 is m/84h/{0h/1h}/0h
val accountPub = DeterministicWallet.publicKey(DeterministicWallet.derivePrivateKey(rootKey, hardened(account)))
DeterministicWallet.encode(accountPub, prefix)
}
@ -98,16 +98,15 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
}
override def descriptors(account: Long): Descriptors = {
// TODO: we should use 'h' everywhere once bitcoin-kmp supports it.
val keyPath = s"$rootPath/$account'".replace('\'', 'h')
val keyPath = s"$rootPath/${account}h"
val prefix = chainHash match {
case Block.LivenetGenesisBlock.hash => xpub
case _ => tpub
}
val accountPub = DeterministicWallet.publicKey(DeterministicWallet.derivePrivateKey(rootKey, hardened(account)))
// descriptors for account 0 are:
// 84'/{0'/1'}/0'/0/* for main addresses
// 84'/{0'/1'}/0'/1/* for change addresses
// 84h/{0h/1h}/0h/0/* for main addresses
// 84h/{0h/1h}/0h/1/* for change addresses
val receiveDesc = s"wpkh([$fingerPrintHex/$keyPath]${encode(accountPub, prefix)}/0/*)"
val changeDesc = s"wpkh([$fingerPrintHex/$keyPath]${encode(accountPub, prefix)}/1/*)"
Descriptors(wallet_name = walletName, descriptors = List(
@ -120,7 +119,7 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
for {
spent <- spentAmount(psbt, ourInputs)
change <- changeAmount(psbt, ourOutputs)
_ = logger.debug(s"signing txid=${psbt.getGlobal.getTx.txid} fees=${psbt.computeFees()} spent=$spent change=$change")
_ = logger.debug(s"signing txid=${psbt.global.tx.txid} fees=${psbt.computeFees()} spent=$spent change=$change")
_ <- Try {
ourOutputs.foreach(i => require(isOurOutput(psbt, i), s"could not verify output $i: bitcoin core may be malicious"))
}
@ -142,22 +141,22 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
private def changeAmount(psbt: Psbt, ourOutputs: Seq[Int]): Try[Satoshi] = Try {
ourOutputs.map(i => {
require(psbt.getGlobal.getTx.txOut.size() > i, s"output $i is missing from psbt: bitcoin core may be malicious")
fr.acinq.bitcoin.scalacompat.KotlinUtils.kmp2scala(psbt.getGlobal.getTx.txOut.get(i).amount)
require(psbt.global.tx.txOut.size() > i, s"output $i is missing from psbt: bitcoin core may be malicious")
fr.acinq.bitcoin.scalacompat.KotlinUtils.kmp2scala(psbt.global.tx.txOut.get(i).amount)
}).sum
}
/** Check that an output belongs to us (i.e. we can recompute its public key from its bip32 path). */
private def isOurOutput(psbt: Psbt, outputIndex: Int): Boolean = {
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
if (psbt.getOutputs.size() <= outputIndex || psbt.getGlobal.getTx.txOut.size() <= outputIndex) {
if (psbt.outputs.size() <= outputIndex || psbt.global.tx.txOut.size() <= outputIndex) {
return false
}
val output = psbt.getOutputs.get(outputIndex)
val txOut = psbt.getGlobal.getTx.txOut.get(outputIndex)
val output = psbt.outputs.get(outputIndex)
val txOut = psbt.global.tx.txOut.get(outputIndex)
output.getDerivationPaths.asScala.headOption match {
case Some((pub, keypath)) =>
val (expectedPubKey, _) = derivePublicKey(KeyPath(keypath.getKeyPath.path.asScala.toSeq.map(_.longValue())))
val (expectedPubKey, _) = derivePublicKey(KeyPath(keypath.keyPath.path.asScala.toSeq.map(_.longValue())))
val expectedScript = Script.write(Script.pay2wpkh(expectedPubKey))
if (expectedPubKey != kmp2scala(pub)) {
logger.warn(s"public key mismatch (expected=$expectedPubKey, actual=$pub): bitcoin core may be malicious")
@ -187,8 +186,8 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
// We check that these fields are consistent and match the outpoint that is spent in the PSBT.
// This prevents attacks where Bitcoin Core would lie about the amount being spent and make us pay very high fees.
require(input.getNonWitnessUtxo != null, "non-witness utxo is missing: bitcoin core may be malicious")
require(input.getNonWitnessUtxo.txid == psbt.getGlobal.getTx.txIn.get(pos).outPoint.txid, "utxo txid mismatch: bitcoin core may be malicious")
require(input.getNonWitnessUtxo.txOut.get(psbt.getGlobal.getTx.txIn.get(pos).outPoint.index.toInt) == input.getWitnessUtxo, "utxo mismatch: bitcoin core may be malicious")
require(input.getNonWitnessUtxo.txid == psbt.global.tx.txIn.get(pos).outPoint.txid, "utxo txid mismatch: bitcoin core may be malicious")
require(input.getNonWitnessUtxo.txOut.get(psbt.global.tx.txIn.get(pos).outPoint.index.toInt) == input.getWitnessUtxo, "utxo mismatch: bitcoin core may be malicious")
// We must use SIGHASH_ALL, otherwise we would be vulnerable to "signature reuse" attacks.
// When unspecified, the sighash used will be SIGHASH_ALL.
@ -197,13 +196,13 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
// Check that we're signing a p2wpkh input and that the keypath is provided and correct.
require(input.getDerivationPaths.size() == 1, "bip32 derivation path is missing: bitcoin core may be malicious")
val (pub, keypath) = input.getDerivationPaths.asScala.toSeq.head
val priv = fr.acinq.bitcoin.DeterministicWallet.derivePrivateKey(master.priv, keypath.getKeyPath).getPrivateKey
val priv = fr.acinq.bitcoin.DeterministicWallet.derivePrivateKey(master.priv, keypath.keyPath).getPrivateKey
require(priv.publicKey() == pub, s"derived public key doesn't match (expected=$pub actual=${priv.publicKey()}): bitcoin core may be malicious")
val expectedScript = ByteVector(Script.write(Script.pay2wpkh(pub)))
require(kmp2scala(input.getWitnessUtxo.publicKeyScript) == expectedScript, s"script mismatch (expected=$expectedScript, actual=${input.getWitnessUtxo.publicKeyScript}): bitcoin core may be malicious")
// Update the input with the right script for a p2wpkh input, which is a *p2pkh* script, then sign and finalize.
val updated: Either[UpdateFailure, Psbt] = psbt.updateWitnessInput(psbt.getGlobal.getTx.txIn.get(pos).outPoint, input.getWitnessUtxo, null, Script.pay2pkh(pub), SigHash.SIGHASH_ALL, input.getDerivationPaths)
val updated: Either[UpdateFailure, Psbt] = psbt.updateWitnessInput(psbt.global.tx.txIn.get(pos).outPoint, input.getWitnessUtxo, null, Script.pay2pkh(pub), SigHash.SIGHASH_ALL, input.getDerivationPaths)
val signed = updated.flatMap(_.sign(priv, pos))
val finalized = signed.flatMap(s => {
require(s.getSig.last.toInt == SigHash.SIGHASH_ALL, "signature must end with SIGHASH_ALL")

View file

@ -2,7 +2,7 @@ package fr.acinq.eclair.db
import com.google.common.util.concurrent.ThreadFactoryBuilder
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, Satoshi}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, Satoshi, TxId}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.db.Databases.{FileBackup, PostgresDatabases, SqliteDatabases}
import fr.acinq.eclair.db.DbEventHandler.ChannelEvent
@ -11,7 +11,7 @@ import fr.acinq.eclair.payment._
import fr.acinq.eclair.payment.relay.Relayer.RelayFees
import fr.acinq.eclair.router.Router
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelUpdate, NodeAddress, NodeAnnouncement}
import fr.acinq.eclair.{CltvExpiry, MilliSatoshi, Paginated, RealShortChannelId, ShortChannelId, TimestampMilli, TimestampSecond}
import fr.acinq.eclair.{CltvExpiry, MilliSatoshi, Paginated, RealShortChannelId, ShortChannelId, TimestampMilli}
import grizzled.slf4j.Logging
import java.io.File
@ -99,7 +99,7 @@ case class DualNetworkDb(primary: NetworkDb, secondary: NetworkDb) extends Netwo
primary.listNodes()
}
override def addChannel(c: ChannelAnnouncement, txid: ByteVector32, capacity: Satoshi): Unit = {
override def addChannel(c: ChannelAnnouncement, txid: TxId, capacity: Satoshi): Unit = {
runAsync(secondary.addChannel(c, txid, capacity))
primary.addChannel(c, txid, capacity)
}

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.db
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Satoshi, TxId}
import fr.acinq.eclair.router.Router.PublicChannel
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement}
import fr.acinq.eclair.{RealShortChannelId, ShortChannelId}
@ -36,7 +36,7 @@ trait NetworkDb {
def listNodes(): Seq[NodeAnnouncement]
def addChannel(c: ChannelAnnouncement, txid: ByteVector32, capacity: Satoshi): Unit
def addChannel(c: ChannelAnnouncement, txid: TxId, capacity: Satoshi): Unit
def updateChannel(u: ChannelUpdate): Unit

View file

@ -259,7 +259,7 @@ class PgAuditDb(implicit ds: DataSource) extends AuditDb with Logging {
override def add(e: TransactionPublished): Unit = withMetrics("audit/add-transaction-published", DbBackends.Postgres) {
inTransaction { pg =>
using(pg.prepareStatement("INSERT INTO audit.transactions_published VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING")) { statement =>
statement.setString(1, e.tx.txid.toHex)
statement.setString(1, e.tx.txid.value.toHex)
statement.setString(2, e.channelId.toHex)
statement.setString(3, e.remoteNodeId.value.toHex)
statement.setLong(4, e.miningFee.toLong)
@ -273,7 +273,7 @@ class PgAuditDb(implicit ds: DataSource) extends AuditDb with Logging {
override def add(e: TransactionConfirmed): Unit = withMetrics("audit/add-transaction-confirmed", DbBackends.Postgres) {
inTransaction { pg =>
using(pg.prepareStatement("INSERT INTO audit.transactions_confirmed VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING")) { statement =>
statement.setString(1, e.tx.txid.toHex)
statement.setString(1, e.tx.txid.value.toHex)
statement.setString(2, e.channelId.toHex)
statement.setString(3, e.remoteNodeId.value.toHex)
statement.setTimestamp(4, Timestamp.from(Instant.now()))

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.db.pg
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Crypto, Satoshi, TxId}
import fr.acinq.eclair.db.Monitoring.Metrics.withMetrics
import fr.acinq.eclair.db.Monitoring.Tags.DbBackends
import fr.acinq.eclair.db.NetworkDb
@ -182,12 +182,12 @@ class PgNetworkDb(implicit ds: DataSource) extends NetworkDb with Logging {
}
}
override def addChannel(c: ChannelAnnouncement, txid: ByteVector32, capacity: Satoshi): Unit = withMetrics("network/add-channel", DbBackends.Postgres) {
override def addChannel(c: ChannelAnnouncement, txid: TxId, capacity: Satoshi): Unit = withMetrics("network/add-channel", DbBackends.Postgres) {
inTransaction { pg =>
using(pg.prepareStatement("INSERT INTO network.public_channels (short_channel_id, txid, channel_announcement, capacity_sat, channel_announcement_json) VALUES (?, ?, ?, ?, ?::JSONB) ON CONFLICT DO NOTHING")) {
statement =>
statement.setLong(1, c.shortChannelId.toLong)
statement.setString(2, txid.toHex)
statement.setString(2, txid.value.toHex)
statement.setBytes(3, channelAnnouncementCodec.encode(c).require.toByteArray)
statement.setLong(4, capacity.toLong)
statement.setString(5, serialization.write(c))
@ -211,7 +211,7 @@ class PgNetworkDb(implicit ds: DataSource) extends NetworkDb with Logging {
private def parseChannel(rs: ResultSet): PublicChannel = {
val ann = channelAnnouncementCodec.decode(rs.getBitVectorOpt("channel_announcement").get).require.value
val txId = ByteVector32.fromValidHex(rs.getString("txid"))
val txId = TxId.fromValidHex(rs.getString("txid"))
val capacity = rs.getLong("capacity_sat")
val channel_update_1_opt = rs.getBitVectorOpt("channel_update_1").map(channelUpdateCodec.decode(_).require.value)
val channel_update_2_opt = rs.getBitVectorOpt("channel_update_2").map(channelUpdateCodec.decode(_).require.value)

View file

@ -246,7 +246,7 @@ class SqliteAuditDb(val sqlite: Connection) extends AuditDb with Logging {
override def add(e: TransactionPublished): Unit = withMetrics("audit/add-transaction-published", DbBackends.Sqlite) {
using(sqlite.prepareStatement("INSERT OR IGNORE INTO transactions_published VALUES (?, ?, ?, ?, ?, ?)")) { statement =>
statement.setBytes(1, e.tx.txid.toArray)
statement.setBytes(1, e.tx.txid.value.toArray)
statement.setBytes(2, e.channelId.toArray)
statement.setBytes(3, e.remoteNodeId.value.toArray)
statement.setLong(4, e.miningFee.toLong)
@ -258,7 +258,7 @@ class SqliteAuditDb(val sqlite: Connection) extends AuditDb with Logging {
override def add(e: TransactionConfirmed): Unit = withMetrics("audit/add-transaction-confirmed", DbBackends.Sqlite) {
using(sqlite.prepareStatement("INSERT OR IGNORE INTO transactions_confirmed VALUES (?, ?, ?, ?)")) { statement =>
statement.setBytes(1, e.tx.txid.toArray)
statement.setBytes(1, e.tx.txid.value.toArray)
statement.setBytes(2, e.channelId.toArray)
statement.setBytes(3, e.remoteNodeId.value.toArray)
statement.setLong(4, TimestampMilli.now().toLong)

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.db.sqlite
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Crypto, Satoshi, TxId}
import fr.acinq.eclair.db.Monitoring.Metrics.withMetrics
import fr.acinq.eclair.db.Monitoring.Tags.DbBackends
import fr.acinq.eclair.db.NetworkDb
@ -115,10 +115,10 @@ class SqliteNetworkDb(val sqlite: Connection) extends NetworkDb with Logging {
}
}
override def addChannel(c: ChannelAnnouncement, txid: ByteVector32, capacity: Satoshi): Unit = withMetrics("network/add-channel", DbBackends.Sqlite) {
override def addChannel(c: ChannelAnnouncement, txid: TxId, capacity: Satoshi): Unit = withMetrics("network/add-channel", DbBackends.Sqlite) {
using(sqlite.prepareStatement("INSERT OR IGNORE INTO channels VALUES (?, ?, ?, ?, NULL, NULL)")) { statement =>
statement.setLong(1, c.shortChannelId.toLong)
statement.setString(2, txid.toHex)
statement.setString(2, txid.value.toHex)
statement.setBytes(3, channelAnnouncementCodec.encode(c).require.toByteArray)
statement.setLong(4, capacity.toLong)
statement.executeUpdate()
@ -136,7 +136,7 @@ class SqliteNetworkDb(val sqlite: Connection) extends NetworkDb with Logging {
private def parseChannel(rs: ResultSet): PublicChannel = {
val ann = channelAnnouncementCodec.decode(rs.getBitVectorOpt("channel_announcement").get).require.value
val txId = ByteVector32.fromValidHex(rs.getString("txid"))
val txId = TxId.fromValidHex(rs.getString("txid"))
val capacity = rs.getLong("capacity_sat")
val channel_update_1_opt = rs.getBitVectorOpt("channel_update_1").map(channelUpdateCodec.decode(_).require.value)
val channel_update_2_opt = rs.getBitVectorOpt("channel_update_2").map(channelUpdateCodec.decode(_).require.value)

View file

@ -23,7 +23,7 @@ import akka.event.Logging.MDC
import akka.event.{BusLogging, DiagnosticLoggingAdapter}
import akka.util.Timeout
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi, SatoshiLong, TxId}
import fr.acinq.eclair.Logs.LogCategory
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
import fr.acinq.eclair._
@ -526,7 +526,7 @@ object Peer {
* double-spend the funding transaction. Callers must wait for on-chain confirmations if they want guarantees that
* the channel has been opened.
*/
case class Created(channelId: ByteVector32, fundingTxId: ByteVector32, fee: Satoshi) extends OpenChannelResponse { override def toString = s"created channel $channelId with fundingTxId=$fundingTxId and fees=$fee" }
case class Created(channelId: ByteVector32, fundingTxId: TxId, fee: Satoshi) extends OpenChannelResponse { override def toString = s"created channel $channelId with fundingTxId=$fundingTxId and fees=$fee" }
case class Rejected(reason: String) extends OpenChannelResponse { override def toString = reason }
case object Cancelled extends OpenChannelResponse { override def toString = "channel creation cancelled" }
case object Disconnected extends OpenChannelResponse { override def toString = "disconnected" }

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.io
import akka.actor.{ActorRef, FSM, OneForOneStrategy, PoisonPill, Props, Stash, SupervisorStrategy, Terminated}
import akka.event.Logging.MDC
import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.eclair.Logs.LogCategory
import fr.acinq.eclair.crypto.Noise.KeyPair
@ -555,8 +555,8 @@ object PeerConnection {
case object Nothing extends Data
case class AuthenticatingData(pendingAuth: PendingAuth, transport: ActorRef, isPersistent: Boolean) extends Data with HasTransport
case class BeforeInitData(remoteNodeId: PublicKey, pendingAuth: PendingAuth, transport: ActorRef, isPersistent: Boolean) extends Data with HasTransport
case class InitializingData(chainHash: ByteVector32, pendingAuth: PendingAuth, remoteNodeId: PublicKey, transport: ActorRef, peer: ActorRef, localInit: protocol.Init, doSync: Boolean, isPersistent: Boolean) extends Data with HasTransport
case class ConnectedData(chainHash: ByteVector32, remoteNodeId: PublicKey, transport: ActorRef, peer: ActorRef, localInit: protocol.Init, remoteInit: protocol.Init, rebroadcastDelay: FiniteDuration, gossipTimestampFilter: Option[GossipTimestampFilter] = None, behavior: Behavior = Behavior(), expectedPong_opt: Option[ExpectedPong] = None, isPersistent: Boolean) extends Data with HasTransport
case class InitializingData(chainHash: BlockHash, pendingAuth: PendingAuth, remoteNodeId: PublicKey, transport: ActorRef, peer: ActorRef, localInit: protocol.Init, doSync: Boolean, isPersistent: Boolean) extends Data with HasTransport
case class ConnectedData(chainHash: BlockHash, remoteNodeId: PublicKey, transport: ActorRef, peer: ActorRef, localInit: protocol.Init, remoteInit: protocol.Init, rebroadcastDelay: FiniteDuration, gossipTimestampFilter: Option[GossipTimestampFilter] = None, behavior: Behavior = Behavior(), expectedPong_opt: Option[ExpectedPong] = None, isPersistent: Boolean) extends Data with HasTransport
case class ExpectedPong(ping: Ping, timestamp: TimestampMilli = TimestampMilli.now())
case class PingTimeout(ping: Ping)
@ -572,7 +572,7 @@ object PeerConnection {
def outgoing: Boolean = remoteNodeId_opt.isDefined // if this is an outgoing connection, we know the node id in advance
}
case class Authenticated(peerConnection: ActorRef, remoteNodeId: PublicKey, outgoing: Boolean) extends RemoteTypes
case class InitializeConnection(peer: ActorRef, chainHash: ByteVector32, features: Features[InitFeature], doSync: Boolean) extends RemoteTypes
case class InitializeConnection(peer: ActorRef, chainHash: BlockHash, features: Features[InitFeature], doSync: Boolean) extends RemoteTypes
case class ConnectionReady(peerConnection: ActorRef, remoteNodeId: PublicKey, address: NodeAddress, outgoing: Boolean, localInit: protocol.Init, remoteInit: protocol.Init) extends RemoteTypes
sealed trait ConnectionResult extends RemoteTypes

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.json
import com.google.common.net.HostAndPort
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
import fr.acinq.bitcoin.scalacompat.{Btc, ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockHash, BlockId, Btc, ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction, TxId}
import fr.acinq.eclair.balance.CheckBalance.{CorrectedOnChainBalance, GlobalBalance, OffChainBalance}
import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
import fr.acinq.eclair.channel._
@ -118,6 +118,34 @@ object ByteVector32KmpSerializer extends MinimalSerializer({
case x: fr.acinq.bitcoin.ByteVector32 => JString(x.toHex)
})
object TxIdSerializer extends MinimalSerializer({
case x: TxId => JString(x.value.toHex)
})
object TxIdKeySerializer extends MinimalKeySerializer({
case x: TxId => x.value.toHex
})
object TxIdKmpSerializer extends MinimalSerializer({
case x: fr.acinq.bitcoin.TxId => JString(x.value.toHex)
})
object BlockIdSerializer extends MinimalSerializer({
case x: BlockId => JString(x.value.toHex)
})
object BlockIdKmpSerializer extends MinimalSerializer({
case x: fr.acinq.bitcoin.BlockId => JString(x.value.toHex)
})
object BlockHashSerializer extends MinimalSerializer({
case x: BlockHash => JString(x.value.toHex)
})
object BlockHashKmpSerializer extends MinimalSerializer({
case x: fr.acinq.bitcoin.BlockHash => JString(x.value.toHex)
})
object ByteVector64Serializer extends MinimalSerializer({
case x: ByteVector64 => JString(x.toHex)
})
@ -217,7 +245,7 @@ object CommandResponseSerializer extends MinimalSerializer({
object TransactionSerializer extends MinimalSerializer({
case x: Transaction => JObject(List(
JField("txid", JString(x.txid.toHex)),
JField("txid", JString(x.txid.value.toHex)),
JField("tx", JString(x.toString()))
))
})
@ -228,34 +256,34 @@ object KeyPathSerializer extends MinimalSerializer({
object TransactionWithInputInfoSerializer extends MinimalSerializer({
case x: HtlcSuccessTx => JObject(List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString())),
JField("paymentHash", JString(x.paymentHash.toString())),
JField("htlcId", JLong(x.htlcId)),
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
))
case x: HtlcTimeoutTx => JObject(List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString())),
JField("htlcId", JLong(x.htlcId)),
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
))
case x: ClaimHtlcSuccessTx => JObject(List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString())),
JField("paymentHash", JString(x.paymentHash.toString())),
JField("htlcId", JLong(x.htlcId)),
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
))
case x: ClaimHtlcTx => JObject(List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString())),
JField("htlcId", JLong(x.htlcId)),
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
))
case x: ClosingTx =>
val txFields = List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString()))
)
x.toLocalOutput match {
@ -269,7 +297,7 @@ object TransactionWithInputInfoSerializer extends MinimalSerializer({
case None => JObject(txFields)
}
case x: ReplaceableTransactionWithInputInfo => JObject(List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString())),
x.confirmationTarget match {
case ConfirmationTarget.Absolute(confirmBefore) => JField("confirmBeforeBlock", JLong(confirmBefore.toLong))
@ -278,7 +306,7 @@ object TransactionWithInputInfoSerializer extends MinimalSerializer({
))
case x: TransactionWithInputInfo => JObject(List(
JField("txid", JString(x.tx.txid.toHex)),
JField("txid", JString(x.tx.txid.value.toHex)),
JField("tx", JString(x.tx.toString()))
))
})
@ -528,7 +556,7 @@ object ShortIdsSerializer extends ConvertClassSerializer[ShortIds](s => ShortIds
// @formatter:on
// @formatter:off
private case class FundingTxStatusJson(status: String, txid: Option[ByteVector32])
private case class FundingTxStatusJson(status: String, txid: Option[TxId])
object FundingTxStatusSerializer extends ConvertClassSerializer[LocalFundingStatus]({
case s: LocalFundingStatus.UnconfirmedFundingTx => FundingTxStatusJson("unconfirmed", s.signedTx_opt.map(_.txid))
case s: LocalFundingStatus.ConfirmedFundingTx => FundingTxStatusJson("confirmed", s.signedTx_opt.map(_.txid))
@ -634,6 +662,9 @@ object JsonSerializers {
TypedActorRefSerializer +
ByteVectorSerializer +
ByteVector32Serializer +
TxIdSerializer +
BlockIdSerializer +
BlockHashSerializer +
ByteVector64Serializer +
ChannelEventSerializer +
UInt64Serializer +
@ -676,6 +707,7 @@ object JsonSerializers {
JavaUUIDSerializer +
OriginSerializer +
ByteVector32KeySerializer +
TxIdKeySerializer +
GlobalBalanceSerializer +
PeerInfoSerializer +
PaymentFailedSummarySerializer +

View file

@ -16,7 +16,6 @@
package fr.acinq
import fr.acinq.bitcoin.Bitcoin
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
import fr.acinq.bitcoin.scalacompat._
@ -25,8 +24,6 @@ import fr.acinq.eclair.payment.relay.Relayer.RelayFees
import scodec.Attempt
import scodec.bits.{BitVector, ByteVector}
import scala.jdk.CollectionConverters.CollectionHasAsScala
package object eclair {
val randomGen = new StrongRandom()
@ -45,8 +42,9 @@ package object eclair {
def randomLong(): Long = randomGen.nextLong()
def toLongId(fundingTxHash: ByteVector32, fundingOutputIndex: Int): ByteVector32 = {
require(fundingOutputIndex < 65536, "fundingOutputIndex must not be greater than FFFF")
def toLongId(fundingTxId: TxId, fundingOutputIndex: Int): ByteVector32 = {
require(fundingOutputIndex < 65536, "fundingOutputIndex must not be greater than 0xFFFF")
val fundingTxHash = TxHash(fundingTxId).value
val channelId = ByteVector32(fundingTxHash.take(30) :+ (fundingTxHash(30) ^ (fundingOutputIndex >> 8)).toByte :+ (fundingTxHash(31) ^ fundingOutputIndex).toByte)
channelId
}

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.payment
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32, ByteVector64, Crypto}
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32}
import fr.acinq.eclair.{Bolt11Feature, CltvExpiryDelta, Feature, FeatureSupport, Features, InvoiceFeature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TimestampSecond, randomBytes32}
import scodec.bits.{BitVector, ByteOrdering, ByteVector}
@ -144,7 +144,7 @@ object Bolt11Invoice {
val defaultFeatures: Features[Bolt11Feature] = Features((Features.VariableLengthOnion, FeatureSupport.Mandatory), (Features.PaymentSecret, FeatureSupport.Mandatory))
def apply(chainHash: ByteVector32,
def apply(chainHash: BlockHash,
amount: Option[MilliSatoshi],
paymentHash: ByteVector32,
privateKey: PrivateKey,

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.payment
import fr.acinq.bitcoin.Bech32
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, ByteVector64, Crypto}
import fr.acinq.eclair.crypto.Sphinx
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute
import fr.acinq.eclair.wire.protocol.OfferTypes._
@ -191,7 +191,7 @@ object MinimalBolt12Invoice {
val hrp = "lndi"
def apply(offer: Offer,
chain: ByteVector32,
chain: BlockHash,
amount: MilliSatoshi,
quantity: Long,
paymentHash: ByteVector32,

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.payment.send
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import akka.actor.{ActorRef, typed}
import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.bitcoin.scalacompat.BlockHash
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute
import fr.acinq.eclair.message.Postman.{OnionMessageResponse, SendMessage}
@ -40,7 +40,7 @@ object OfferPayment {
case class UnsupportedFeatures(features: Features[InvoiceFeature]) extends Failure
case class UnsupportedChains(chains: Seq[ByteVector32]) extends Failure
case class UnsupportedChains(chains: Seq[BlockHash]) extends Failure
case class ExpiredOffer(expiryDate: TimestampSecond) extends Failure

View file

@ -149,7 +149,7 @@ object EclairInternalsSerializer {
def initializeConnectionCodec(system: ExtendedActorSystem): Codec[PeerConnection.InitializeConnection] = (
("peer" | actorRefCodec(system)) ::
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("features" | variableSizeBytes(uint16, initFeaturesCodec)) ::
("doSync" | bool(8))).as[PeerConnection.InitializeConnection]
@ -164,7 +164,7 @@ object EclairInternalsSerializer {
val optionQueryChannelRangeTlv: Codec[Option[QueryChannelRangeTlv]] = variableSizeBytes(uint16, optional(bool(8), variableSizeBytesLong(varintoverflow, queryFlagsCodec.upcast[QueryChannelRangeTlv])))
def sendChannelQueryCodec(system: ExtendedActorSystem): Codec[SendChannelQuery] = (
("chainsHash" | bytes32) ::
("chainHash" | blockHash) ::
("remoteNodeId" | publicKey) ::
("to" | actorRefCodec(system)) ::
("replacePrevious" | bool(8)) ::

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.router
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, sha256, verifySignature}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector64, Crypto, LexicographicalOrdering}
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{CltvExpiryDelta, Feature, Features, MilliSatoshi, NodeFeature, RealShortChannelId, ShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult}
import scodec.bits.ByteVector
@ -28,16 +28,16 @@ import shapeless.HNil
*/
object Announcements {
def channelAnnouncementWitnessEncode(chainHash: ByteVector32, shortChannelId: RealShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, bitcoinKey1: PublicKey, bitcoinKey2: PublicKey, features: Features[Feature], tlvStream: TlvStream[ChannelAnnouncementTlv]): ByteVector =
def channelAnnouncementWitnessEncode(chainHash: BlockHash, shortChannelId: RealShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, bitcoinKey1: PublicKey, bitcoinKey2: PublicKey, features: Features[Feature], tlvStream: TlvStream[ChannelAnnouncementTlv]): ByteVector =
sha256(sha256(serializationResult(LightningMessageCodecs.channelAnnouncementWitnessCodec.encode(features :: chainHash :: shortChannelId :: nodeId1 :: nodeId2 :: bitcoinKey1 :: bitcoinKey2 :: tlvStream :: HNil))))
def nodeAnnouncementWitnessEncode(timestamp: TimestampSecond, nodeId: PublicKey, rgbColor: Color, alias: String, features: Features[Feature], addresses: List[NodeAddress], tlvStream: TlvStream[NodeAnnouncementTlv]): ByteVector =
sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: tlvStream :: HNil))))
def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: TimestampSecond, messageFlags: ChannelUpdate.MessageFlags, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector =
def channelUpdateWitnessEncode(chainHash: BlockHash, shortChannelId: ShortChannelId, timestamp: TimestampSecond, messageFlags: ChannelUpdate.MessageFlags, channelFlags: ChannelUpdate.ChannelFlags, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, tlvStream: TlvStream[ChannelUpdateTlv]): ByteVector =
sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: messageFlags :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: tlvStream :: HNil))))
def generateChannelAnnouncementWitness(chainHash: ByteVector32, shortChannelId: RealShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features[Feature]): ByteVector =
def generateChannelAnnouncementWitness(chainHash: BlockHash, shortChannelId: RealShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, features: Features[Feature]): ByteVector =
if (isNode1(localNodeId, remoteNodeId)) {
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, features, TlvStream.empty)
} else {
@ -46,7 +46,7 @@ object Announcements {
def signChannelAnnouncement(witness: ByteVector, key: PrivateKey): ByteVector64 = Crypto.sign(witness, key)
def makeChannelAnnouncement(chainHash: ByteVector32, shortChannelId: RealShortChannelId, localNodeId: PublicKey, remoteNodeId: PublicKey, localFundingKey: PublicKey, remoteFundingKey: PublicKey, localNodeSignature: ByteVector64, remoteNodeSignature: ByteVector64, localBitcoinSignature: ByteVector64, remoteBitcoinSignature: ByteVector64): ChannelAnnouncement = {
def makeChannelAnnouncement(chainHash: BlockHash, shortChannelId: RealShortChannelId, 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) =
if (isNode1(localNodeId, remoteNodeId)) {
(localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, localNodeSignature, remoteNodeSignature, localBitcoinSignature, remoteBitcoinSignature)
@ -118,7 +118,7 @@ object Announcements {
u1.htlcMinimumMsat == u2.htlcMinimumMsat &&
u1.htlcMaximumMsat == u2.htlcMaximumMsat
def makeChannelUpdate(chainHash: ByteVector32, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, isPrivate: Boolean = false, enable: Boolean = true, timestamp: TimestampSecond = TimestampSecond.now()): ChannelUpdate = {
def makeChannelUpdate(chainHash: BlockHash, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: CltvExpiryDelta, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, isPrivate: Boolean = false, enable: Boolean = true, timestamp: TimestampSecond = TimestampSecond.now()): ChannelUpdate = {
val messageFlags = ChannelUpdate.MessageFlags(isPrivate)
val channelFlags = ChannelUpdate.ChannelFlags(isNode1 = isNode1(nodeSecret.publicKey, remoteNodeId), isEnabled = enable)
val witness = channelUpdateWitnessEncode(chainHash, shortChannelId, timestamp, messageFlags, channelFlags, cltvExpiryDelta, htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, htlcMaximumMsat, TlvStream.empty)

View file

@ -22,7 +22,7 @@ import akka.actor.{Actor, ActorLogging, ActorRef, Cancellable, Props, Terminated
import akka.event.DiagnosticLoggingAdapter
import akka.event.Logging.MDC
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, Satoshi, TxId}
import fr.acinq.eclair.Logs.LogCategory
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
@ -89,7 +89,7 @@ class Router(val nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Comm
// watch the funding tx of all these channels
// note: some of them may already have been spent, in that case we will receive the watch event immediately
(channels.values ++ pruned.values).foreach { pc =>
val txid = pc.fundingTxid
val txid = pc.fundingTxId
val outputIndex = ShortChannelId.coordinates(pc.ann.shortChannelId).outputIndex
// avoid herd effect at startup because watch-spent are intensive in terms of rpc calls to bitcoind
context.system.scheduler.scheduleOnce(Random.nextLong(nodeParams.routerConf.watchSpentWindow.toSeconds).seconds) {
@ -395,7 +395,7 @@ object Router {
def updateBalances(commitments: Commitments): KnownChannel
def applyChannelUpdate(update: Either[LocalChannelUpdate, RemoteChannelUpdate]): KnownChannel
}
case class PublicChannel(ann: ChannelAnnouncement, fundingTxid: ByteVector32, capacity: Satoshi, update_1_opt: Option[ChannelUpdate], update_2_opt: Option[ChannelUpdate], meta_opt: Option[ChannelMeta]) extends KnownChannel {
case class PublicChannel(ann: ChannelAnnouncement, fundingTxId: TxId, capacity: Satoshi, update_1_opt: Option[ChannelUpdate], update_2_opt: Option[ChannelUpdate], meta_opt: Option[ChannelMeta]) extends KnownChannel {
update_1_opt.foreach(u => assert(u.channelFlags.isNode1))
update_2_opt.foreach(u => assert(!u.channelFlags.isNode1))
@ -689,7 +689,7 @@ object Router {
// @formatter:on
// @formatter:off
case class SendChannelQuery(chainHash: ByteVector32, remoteNodeId: PublicKey, to: ActorRef, replacePrevious: Boolean, flags_opt: Option[QueryChannelRangeTlv]) extends RemoteTypes
case class SendChannelQuery(chainHash: BlockHash, remoteNodeId: PublicKey, to: ActorRef, replacePrevious: Boolean, flags_opt: Option[QueryChannelRangeTlv]) extends RemoteTypes
case object GetRoutingState
case class RoutingState(channels: Iterable[PublicChannel], nodes: Iterable[NodeAnnouncement])
case object GetRoutingStateStreaming extends RemoteTypes

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.router
import akka.actor.{ActorContext, ActorRef}
import akka.event.LoggingAdapter
import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.bitcoin.scalacompat.BlockHash
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.eclair.crypto.TransportHandler
import fr.acinq.eclair.router.Monitoring.{Metrics, Tags}
@ -481,7 +481,7 @@ object Sync {
* @param channels channels map
* @return a ReplyChannelRange object
*/
def buildReplyChannelRange(chunk: ShortChannelIdsChunk, syncComplete: Boolean, chainHash: ByteVector32, defaultEncoding: EncodingType, queryFlags_opt: Option[QueryChannelRangeTlv.QueryFlags], channels: SortedMap[RealShortChannelId, PublicChannel]): ReplyChannelRange = {
def buildReplyChannelRange(chunk: ShortChannelIdsChunk, syncComplete: Boolean, chainHash: BlockHash, defaultEncoding: EncodingType, queryFlags_opt: Option[QueryChannelRangeTlv.QueryFlags], channels: SortedMap[RealShortChannelId, PublicChannel]): ReplyChannelRange = {
val encoding = if (chunk.shortChannelIds.isEmpty) EncodingType.UNCOMPRESSED else defaultEncoding
val (timestamps, checksums) = queryFlags_opt match {
case Some(extension) if extension.wantChecksums | extension.wantTimestamps =>

View file

@ -21,7 +21,7 @@ import akka.actor.{ActorContext, ActorRef, typed}
import akka.event.{DiagnosticLoggingAdapter, LoggingAdapter}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.Script.{pay2wsh, write}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Satoshi, TxId}
import fr.acinq.eclair.ShortChannelId.outputIndex
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult, WatchExternalChannelSpent}
@ -156,15 +156,15 @@ object Validation {
}
}
private def addPublicChannel(d: Data, nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], ann: ChannelAnnouncement, fundingTxid: ByteVector32, capacity: Satoshi, privChan_opt: Option[PrivateChannel])(implicit ctx: ActorContext, log: DiagnosticLoggingAdapter): Data = {
private def addPublicChannel(d: Data, nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Command], ann: ChannelAnnouncement, fundingTxId: TxId, capacity: Satoshi, privChan_opt: Option[PrivateChannel])(implicit ctx: ActorContext, log: DiagnosticLoggingAdapter): Data = {
implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors
val fundingOutputIndex = outputIndex(ann.shortChannelId)
watcher ! WatchExternalChannelSpent(ctx.self, fundingTxid, fundingOutputIndex, ann.shortChannelId)
watcher ! WatchExternalChannelSpent(ctx.self, fundingTxId, fundingOutputIndex, ann.shortChannelId)
ctx.system.eventStream.publish(ChannelsDiscovered(SingleChannelDiscovered(ann, capacity, None, None) :: Nil))
nodeParams.db.network.addChannel(ann, fundingTxid, capacity)
nodeParams.db.network.addChannel(ann, fundingTxId, capacity)
val pubChan = PublicChannel(
ann = ann,
fundingTxid = fundingTxid,
fundingTxId = fundingTxId,
capacity = capacity,
update_1_opt = privChan_opt.flatMap(_.update_1_opt),
update_2_opt = privChan_opt.flatMap(_.update_2_opt),

View file

@ -108,11 +108,11 @@ object Scripts {
/**
* @return the number of confirmations of each parent before which the given transaction can be published.
*/
def csvTimeouts(tx: Transaction): Map[ByteVector32, Long] = {
def csvTimeouts(tx: Transaction): Map[TxId, Long] = {
if (tx.version < 2) {
Map.empty
} else {
tx.txIn.foldLeft(Map.empty[ByteVector32, Long]) { case (current, txIn) =>
tx.txIn.foldLeft(Map.empty[TxId, Long]) { case (current, txIn) =>
val csvTimeout = sequenceToBlockHeight(txIn.sequence)
if (csvTimeout > 0) {
val maxCsvTimeout = math.max(csvTimeout, current.getOrElse(txIn.outPoint.txid, 0L))

View file

@ -17,8 +17,8 @@
package fr.acinq.eclair.wire.internal.channel.version0
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxId, TxOut}
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
import fr.acinq.eclair.channel.LocalFundingStatus.SingleFundedUnconfirmedFundingTx
import fr.acinq.eclair.channel._
@ -172,7 +172,7 @@ private[channel] object ChannelCodecs0 {
val remoteCommitCodec: Codec[RemoteCommit] = (
("index" | uint64overflow) ::
("spec" | commitmentSpecCodec) ::
("txid" | bytes32) ::
("txid" | txId) ::
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit].decodeOnly
val updateFulfillHtlcCodec: Codec[UpdateFulfillHtlc] = (
@ -260,10 +260,10 @@ private[channel] object ChannelCodecs0 {
(wire: BitVector) => originsListCodec.decode(wire).map(_.map(_.toMap))
)
val spentListCodec: Codec[List[(OutPoint, ByteVector32)]] = listOfN(uint16, outPointCodec ~ bytes32)
val spentListCodec: Codec[List[(OutPoint, TxId)]] = listOfN(uint16, outPointCodec ~ txId)
val spentMapCodec: Codec[Map[OutPoint, ByteVector32]] = Codec[Map[OutPoint, ByteVector32]](
(map: Map[OutPoint, ByteVector32]) => spentListCodec.encode(map.toList),
val spentMapCodec: Codec[Map[OutPoint, TxId]] = Codec[Map[OutPoint, TxId]](
(map: Map[OutPoint, TxId]) => spentListCodec.encode(map.toList),
(wire: BitVector) => spentListCodec.decode(wire).map(_.map(_.toMap))
)
@ -335,7 +335,7 @@ private[channel] object ChannelCodecs0 {
val fundingCreatedCodec: Codec[FundingCreated] = (
("temporaryChannelId" | bytes32) ::
("fundingTxid" | bytes32) ::
("fundingTxHash" | txIdAsHash) ::
("fundingOutputIndex" | uint16) ::
("signature" | bytes64) ::
("tlvStream" | provide(TlvStream.empty[FundingCreatedTlv]))).as[FundingCreated]

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.wire.internal.channel.version0
import com.softwaremill.quicklens._
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OP_CHECKMULTISIG, OP_PUSHDATA, OutPoint, Satoshi, Script, ScriptWitness, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OP_CHECKMULTISIG, OP_PUSHDATA, OutPoint, Satoshi, Script, ScriptWitness, Transaction, TxId, TxOut}
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.ShaChain
@ -51,10 +51,10 @@ private[channel] object ChannelTypes0 {
InputInfo(input, parentTx.txOut(input.index.toInt), Nil)
}
case class LocalCommitPublished(commitTx: Transaction, claimMainDelayedOutputTx: Option[Transaction], htlcSuccessTxs: List[Transaction], htlcTimeoutTxs: List[Transaction], claimHtlcDelayedTxs: List[Transaction], irrevocablySpent: Map[OutPoint, ByteVector32]) {
case class LocalCommitPublished(commitTx: Transaction, claimMainDelayedOutputTx: Option[Transaction], htlcSuccessTxs: List[Transaction], htlcTimeoutTxs: List[Transaction], claimHtlcDelayedTxs: List[Transaction], irrevocablySpent: Map[OutPoint, TxId]) {
def migrate(): channel.LocalCommitPublished = {
val htlcTxs = htlcSuccessTxs ++ htlcTimeoutTxs
val knownTxs: Map[ByteVector32, Transaction] = (commitTx :: claimMainDelayedOutputTx.toList ::: htlcTxs ::: claimHtlcDelayedTxs).map(tx => tx.txid -> tx).toMap
val knownTxs: Map[TxId, Transaction] = (commitTx :: claimMainDelayedOutputTx.toList ::: htlcTxs ::: claimHtlcDelayedTxs).map(tx => tx.txid -> tx).toMap
// NB: irrevocablySpent may contain transactions that belong to our peer: we will drop them in this migration but
// the channel will put a watch at start-up which will make us fetch the spending transaction.
val irrevocablySpentNew = irrevocablySpent.collect { case (outpoint, txid) if knownTxs.contains(txid) => (outpoint, knownTxs(txid)) }
@ -71,10 +71,10 @@ private[channel] object ChannelTypes0 {
}
}
case class RemoteCommitPublished(commitTx: Transaction, claimMainOutputTx: Option[Transaction], claimHtlcSuccessTxs: List[Transaction], claimHtlcTimeoutTxs: List[Transaction], irrevocablySpent: Map[OutPoint, ByteVector32]) {
case class RemoteCommitPublished(commitTx: Transaction, claimMainOutputTx: Option[Transaction], claimHtlcSuccessTxs: List[Transaction], claimHtlcTimeoutTxs: List[Transaction], irrevocablySpent: Map[OutPoint, TxId]) {
def migrate(): channel.RemoteCommitPublished = {
val claimHtlcTxs = claimHtlcSuccessTxs ::: claimHtlcTimeoutTxs
val knownTxs: Map[ByteVector32, Transaction] = (commitTx :: claimMainOutputTx.toList ::: claimHtlcTxs).map(tx => tx.txid -> tx).toMap
val knownTxs: Map[TxId, Transaction] = (commitTx :: claimMainOutputTx.toList ::: claimHtlcTxs).map(tx => tx.txid -> tx).toMap
// NB: irrevocablySpent may contain transactions that belong to our peer: we will drop them in this migration but
// the channel will put a watch at start-up which will make us fetch the spending transaction.
val irrevocablySpentNew = irrevocablySpent.collect { case (outpoint, txid) if knownTxs.contains(txid) => (outpoint, knownTxs(txid)) }
@ -86,9 +86,9 @@ private[channel] object ChannelTypes0 {
}
}
case class RevokedCommitPublished(commitTx: Transaction, claimMainOutputTx: Option[Transaction], mainPenaltyTx: Option[Transaction], htlcPenaltyTxs: List[Transaction], claimHtlcDelayedPenaltyTxs: List[Transaction], irrevocablySpent: Map[OutPoint, ByteVector32]) {
case class RevokedCommitPublished(commitTx: Transaction, claimMainOutputTx: Option[Transaction], mainPenaltyTx: Option[Transaction], htlcPenaltyTxs: List[Transaction], claimHtlcDelayedPenaltyTxs: List[Transaction], irrevocablySpent: Map[OutPoint, TxId]) {
def migrate(): channel.RevokedCommitPublished = {
val knownTxs: Map[ByteVector32, Transaction] = (commitTx :: claimMainOutputTx.toList ::: mainPenaltyTx.toList ::: htlcPenaltyTxs ::: claimHtlcDelayedPenaltyTxs).map(tx => tx.txid -> tx).toMap
val knownTxs: Map[TxId, Transaction] = (commitTx :: claimMainOutputTx.toList ::: mainPenaltyTx.toList ::: htlcPenaltyTxs ::: claimHtlcDelayedPenaltyTxs).map(tx => tx.txid -> tx).toMap
// NB: irrevocablySpent may contain transactions that belong to our peer: we will drop them in this migration but
// the channel will put a watch at start-up which will make us fetch the spending transaction.
val irrevocablySpentNew = irrevocablySpent.collect { case (outpoint, txid) if knownTxs.contains(txid) => (outpoint, knownTxs(txid)) }

View file

@ -17,8 +17,8 @@
package fr.acinq.eclair.wire.internal.channel.version1
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
import fr.acinq.bitcoin.scalacompat.{OutPoint, Transaction, TxId, TxOut}
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
import fr.acinq.eclair.channel.LocalFundingStatus.SingleFundedUnconfirmedFundingTx
import fr.acinq.eclair.channel._
@ -135,7 +135,7 @@ private[channel] object ChannelCodecs1 {
val remoteCommitCodec: Codec[RemoteCommit] = (
("index" | uint64overflow) ::
("spec" | commitmentSpecCodec) ::
("txid" | bytes32) ::
("txid" | txId) ::
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))
@ -181,7 +181,7 @@ private[channel] object ChannelCodecs1 {
val originsMapCodec: Codec[Map[Long, Origin]] = mapCodec(int64, originCodec)
val spentMapCodec: Codec[Map[OutPoint, ByteVector32]] = mapCodec(outPointCodec, bytes32)
val spentMapCodec: Codec[Map[OutPoint, TxId]] = mapCodec(outPointCodec, txId)
val commitmentsCodec: Codec[Commitments] = (
("channelVersion" | channelVersionCodec) >>:~ { channelVersion =>

View file

@ -162,7 +162,7 @@ private[channel] object ChannelCodecs2 {
val remoteCommitCodec: Codec[RemoteCommit] = (
("index" | uint64overflow) ::
("spec" | commitmentSpecCodec) ::
("txid" | bytes32) ::
("txid" | txId) ::
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))

View file

@ -214,7 +214,7 @@ private[channel] object ChannelCodecs3 {
val remoteCommitCodec: Codec[RemoteCommit] = (
("index" | uint64overflow) ::
("spec" | commitmentSpecCodec) ::
("txid" | bytes32) ::
("txid" | txId) ::
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))

View file

@ -395,7 +395,7 @@ private[channel] object ChannelCodecs4 {
private def remoteCommitCodec(commitmentSpecCodec: Codec[CommitmentSpec]): Codec[RemoteCommit] = (
("index" | uint64overflow) ::
("spec" | commitmentSpecCodec) ::
("txid" | bytes32) ::
("txid" | txId) ::
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
private def nextRemoteCommitCodec(commitmentSpecCodec: Codec[CommitmentSpec]): Codec[NextRemoteCommit] = (

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.wire.protocol
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Satoshi, TxId}
import fr.acinq.eclair.channel.{ChannelType, ChannelTypes}
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream, tmillisatoshi}
@ -165,10 +165,10 @@ sealed trait ChannelReestablishTlv extends Tlv
object ChannelReestablishTlv {
case class NextFundingTlv(txHash: ByteVector32) extends ChannelReestablishTlv
case class NextFundingTlv(txId: TxId) extends ChannelReestablishTlv
object NextFundingTlv {
val codec: Codec[NextFundingTlv] = tlvField("funding_tx_hash" | bytes32)
val codec: Codec[NextFundingTlv] = tlvField(txIdAsHash)
}
val channelReestablishTlvCodec: Codec[TlvStream[ChannelReestablishTlv]] = tlvStream(discriminated[ChannelReestablishTlv].by(varint)

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.wire.protocol
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, ByteVector64, Satoshi, Transaction, TxHash, TxId}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.channel.{ChannelFlags, RealScidStatus, ShortIds}
import fr.acinq.eclair.crypto.Mac32
@ -107,6 +107,13 @@ object CommonCodecs {
val sha256: Codec[ByteVector32] = bytes32
val blockHash: Codec[BlockHash] = bytes32.as[BlockHash]
val txId: Codec[TxId] = bytes32.as[TxId]
/** In lightning messages, transaction IDs are usually encoded as a tx_hash, which reverses the endianness. */
val txIdAsHash: Codec[TxId] = bytes32.xmap(b => TxId(TxHash(b)), txId => TxHash(txId).value)
val varsizebinarydata: Codec[ByteVector] = variableSizeBytes(uint16, bytes)
val listofsignatures: Codec[List[ByteVector64]] = listOfN(uint16, bytes64)

View file

@ -16,9 +16,9 @@
package fr.acinq.eclair.wire.protocol
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Satoshi}
import fr.acinq.bitcoin.scalacompat.{ByteVector64, Satoshi, TxId}
import fr.acinq.eclair.UInt64
import fr.acinq.eclair.wire.protocol.CommonCodecs.{bytes32, bytes64, satoshiSigned, varint}
import fr.acinq.eclair.wire.protocol.CommonCodecs.{bytes64, satoshiSigned, txIdAsHash, varint}
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
import scodec.Codec
import scodec.codecs.discriminated
@ -31,11 +31,11 @@ sealed trait TxAddInputTlv extends Tlv
object TxAddInputTlv {
/** When doing a splice, the initiator must provide the previous funding txId instead of the whole transaction. */
case class SharedInputTxId(txid: ByteVector32) extends TxAddInputTlv
case class SharedInputTxId(txId: TxId) extends TxAddInputTlv
val txAddInputTlvCodec: Codec[TlvStream[TxAddInputTlv]] = tlvStream(discriminated[TxAddInputTlv].by(varint)
// Note that we actually encode as a tx_hash to be consistent with other lightning messages.
.typecase(UInt64(1105), tlvField(bytes32.xmap(txId => txId.reverse, (txHash: ByteVector32) => txHash.reverse).as[SharedInputTxId]))
.typecase(UInt64(1105), tlvField(txIdAsHash.as[SharedInputTxId]))
)
}

View file

@ -69,7 +69,7 @@ object LightningMessageCodecs {
("tlvStream" | ChannelReestablishTlv.channelReestablishTlvCodec)).as[ChannelReestablish]
val openChannelCodec: Codec[OpenChannel] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("temporaryChannelId" | bytes32) ::
("fundingSatoshis" | satoshi) ::
("pushMsat" | millisatoshi) ::
@ -90,7 +90,7 @@ object LightningMessageCodecs {
("tlvStream" | OpenChannelTlv.openTlvCodec)).as[OpenChannel]
val openDualFundedChannelCodec: Codec[OpenDualFundedChannel] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("temporaryChannelId" | bytes32) ::
("fundingFeerate" | feeratePerKw) ::
("commitmentFeerate" | feeratePerKw) ::
@ -148,7 +148,7 @@ object LightningMessageCodecs {
val fundingCreatedCodec: Codec[FundingCreated] = (
("temporaryChannelId" | bytes32) ::
("fundingTxid" | bytes32) ::
("fundingTxHash" | txIdAsHash) ::
("fundingOutputIndex" | uint16) ::
("signature" | bytes64) ::
("tlvStream" | FundingCreatedTlv.fundingCreatedTlvCodec)).as[FundingCreated]
@ -197,7 +197,7 @@ object LightningMessageCodecs {
val txSignaturesCodec: Codec[TxSignatures] = (
("channelId" | bytes32) ::
("txHash" | sha256) ::
("txHash" | txIdAsHash) ::
("witnesses" | witnessesCodec) ::
("tlvStream" | TxSignaturesTlv.txSignaturesTlvCodec)).as[TxSignatures]
@ -281,7 +281,7 @@ object LightningMessageCodecs {
val channelAnnouncementWitnessCodec =
("features" | lengthPrefixedFeaturesCodec) ::
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("shortChannelId" | realshortchannelid) ::
("nodeId1" | publicKey) ::
("nodeId2" | publicKey) ::
@ -317,7 +317,7 @@ object LightningMessageCodecs {
val channelFlagsCodec = ("channelFlags" | (ignore(6) :: reverseBool :: reverseBool)).as[ChannelUpdate.ChannelFlags]
val channelUpdateChecksumCodec =
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("shortChannelId" | shortchannelid) ::
messageFlagsCodec ::
channelFlagsCodec ::
@ -328,7 +328,7 @@ object LightningMessageCodecs {
("htlcMaximumMsat" | millisatoshi)
val channelUpdateWitnessCodec =
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("shortChannelId" | shortchannelid) ::
("timestamp" | timestampSecond) ::
messageFlagsCodec ::
@ -355,23 +355,23 @@ object LightningMessageCodecs {
}((provide[EncodingType](EncodingType.COMPRESSED_ZLIB) :: zlib(list(realshortchannelid))).as[EncodedShortChannelIds])
val queryShortChannelIdsCodec: Codec[QueryShortChannelIds] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("shortChannelIds" | variableSizeBytes(uint16, encodedShortChannelIdsCodec)) ::
("tlvStream" | QueryShortChannelIdsTlv.codec)).as[QueryShortChannelIds]
val replyShortChannelIdsEndCodec: Codec[ReplyShortChannelIdsEnd] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("complete" | byte) ::
("tlvStream" | ReplyShortChannelIdsEndTlv.replyShortChannelIdsEndTlvCodec)).as[ReplyShortChannelIdsEnd]
val queryChannelRangeCodec: Codec[QueryChannelRange] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("firstBlock" | blockHeight) ::
("numberOfBlocks" | uint32) ::
("tlvStream" | QueryChannelRangeTlv.codec)).as[QueryChannelRange]
val replyChannelRangeCodec: Codec[ReplyChannelRange] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("firstBlock" | blockHeight) ::
("numberOfBlocks" | uint32) ::
("complete" | byte) ::
@ -379,7 +379,7 @@ object LightningMessageCodecs {
("tlvStream" | ReplyChannelRangeTlv.codec)).as[ReplyChannelRange]
val gossipTimestampFilterCodec: Codec[GossipTimestampFilter] = (
("chainHash" | bytes32) ::
("chainHash" | blockHash) ::
("firstTimestamp" | timestampSecond) ::
("timestampRange" | uint32) ::
("tlvStream" | GossipTimestampFilterTlv.gossipTimestampFilterTlvCodec)).as[GossipTimestampFilter]
@ -418,7 +418,7 @@ object LightningMessageCodecs {
val spliceLockedCodec: Codec[SpliceLocked] = (
("channelId" | bytes32) ::
("fundingTxid" | bytes32) ::
("fundingTxHash" | txIdAsHash) ::
("tlvStream" | SpliceLockedTlv.spliceLockedTlvCodec)).as[SpliceLocked]
val stfuCodec: Codec[Stfu] = (

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.wire.protocol
import com.google.common.base.Charsets
import com.google.common.net.InetAddresses
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, OutPoint, Satoshi, SatoshiLong, ScriptWitness, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, ByteVector64, OutPoint, Satoshi, SatoshiLong, ScriptWitness, Transaction, TxId}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.channel.{ChannelFlags, ChannelType}
import fr.acinq.eclair.payment.relay.Relayer
@ -47,7 +47,7 @@ sealed trait AnnouncementMessage extends RoutingMessage // <- not in the spec
sealed trait HasTimestamp extends LightningMessage { def timestamp: TimestampSecond }
sealed trait HasTemporaryChannelId extends LightningMessage { def temporaryChannelId: ByteVector32 } // <- not in the spec
sealed trait HasChannelId extends LightningMessage { def channelId: ByteVector32 } // <- not in the spec
sealed trait HasChainHash extends LightningMessage { def chainHash: ByteVector32 } // <- not in the spec
sealed trait HasChainHash extends LightningMessage { def chainHash: BlockHash } // <- not in the spec
sealed trait HasSerialId extends LightningMessage { def serialId: UInt64 } // <- not in the spec
sealed trait ForbiddenMessageDuringSplice extends LightningMessage // <- not in the spec
sealed trait UpdateMessage extends HtlcMessage with ForbiddenMessageDuringSplice // <- not in the spec
@ -89,7 +89,7 @@ case class TxAddInput(channelId: ByteVector32,
previousTxOutput: Long,
sequence: Long,
tlvStream: TlvStream[TxAddInputTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId with HasSerialId {
val sharedInput_opt: Option[OutPoint] = tlvStream.get[TxAddInputTlv.SharedInputTxId].map(i => OutPoint(i.txid.reverse, previousTxOutput))
val sharedInput_opt: Option[OutPoint] = tlvStream.get[TxAddInputTlv.SharedInputTxId].map(i => OutPoint(i.txId, previousTxOutput))
}
object TxAddInput {
@ -116,16 +116,15 @@ case class TxComplete(channelId: ByteVector32,
tlvStream: TlvStream[TxCompleteTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId
case class TxSignatures(channelId: ByteVector32,
txHash: ByteVector32,
txId: TxId,
witnesses: Seq[ScriptWitness],
tlvStream: TlvStream[TxSignaturesTlv] = TlvStream.empty) extends InteractiveTxMessage with HasChannelId {
val txId: ByteVector32 = txHash.reverse
val previousFundingTxSig_opt: Option[ByteVector64] = tlvStream.get[TxSignaturesTlv.PreviousFundingTxSig].map(_.sig)
}
object TxSignatures {
def apply(channelId: ByteVector32, tx: Transaction, witnesses: Seq[ScriptWitness], previousFundingSig_opt: Option[ByteVector64]): TxSignatures = {
TxSignatures(channelId, tx.hash, witnesses, TlvStream(previousFundingSig_opt.map(TxSignaturesTlv.PreviousFundingTxSig).toSet[TxSignaturesTlv]))
TxSignatures(channelId, tx.txid, witnesses, TlvStream(previousFundingSig_opt.map(TxSignaturesTlv.PreviousFundingTxSig).toSet[TxSignaturesTlv]))
}
}
@ -167,10 +166,10 @@ case class ChannelReestablish(channelId: ByteVector32,
yourLastPerCommitmentSecret: PrivateKey,
myCurrentPerCommitmentPoint: PublicKey,
tlvStream: TlvStream[ChannelReestablishTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
val nextFundingTxId_opt: Option[ByteVector32] = tlvStream.get[ChannelReestablishTlv.NextFundingTlv].map(_.txHash.reverse)
val nextFundingTxId_opt: Option[TxId] = tlvStream.get[ChannelReestablishTlv.NextFundingTlv].map(_.txId)
}
case class OpenChannel(chainHash: ByteVector32,
case class OpenChannel(chainHash: BlockHash,
temporaryChannelId: ByteVector32,
fundingSatoshis: Satoshi,
pushMsat: MilliSatoshi,
@ -213,7 +212,7 @@ case class AcceptChannel(temporaryChannelId: ByteVector32,
}
// NB: this message is named open_channel2 in the specification.
case class OpenDualFundedChannel(chainHash: ByteVector32,
case class OpenDualFundedChannel(chainHash: BlockHash,
temporaryChannelId: ByteVector32,
fundingFeerate: FeeratePerKw,
commitmentFeerate: FeeratePerKw,
@ -263,7 +262,7 @@ case class AcceptDualFundedChannel(temporaryChannelId: ByteVector32,
}
case class FundingCreated(temporaryChannelId: ByteVector32,
fundingTxHash: ByteVector32,
fundingTxId: TxId,
fundingOutputIndex: Int,
signature: ByteVector64,
tlvStream: TlvStream[FundingCreatedTlv] = TlvStream.empty) extends ChannelMessage with HasTemporaryChannelId
@ -319,9 +318,8 @@ object SpliceAck {
}
case class SpliceLocked(channelId: ByteVector32,
fundingTxHash: ByteVector32,
fundingTxId: TxId,
tlvStream: TlvStream[SpliceLockedTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
val fundingTxid: ByteVector32 = fundingTxHash.reverse
}
case class Shutdown(channelId: ByteVector32,
@ -401,7 +399,7 @@ case class ChannelAnnouncement(nodeSignature1: ByteVector64,
bitcoinSignature1: ByteVector64,
bitcoinSignature2: ByteVector64,
features: Features[Feature],
chainHash: ByteVector32,
chainHash: BlockHash,
shortChannelId: RealShortChannelId,
nodeId1: PublicKey,
nodeId2: PublicKey,
@ -489,7 +487,7 @@ case class NodeAnnouncement(signature: ByteVector64,
}
case class ChannelUpdate(signature: ByteVector64,
chainHash: ByteVector32,
chainHash: BlockHash,
shortChannelId: ShortChannelId,
timestamp: TimestampSecond,
messageFlags: ChannelUpdate.MessageFlags,
@ -531,23 +529,23 @@ case class EncodedShortChannelIds(encoding: EncodingType, array: List[RealShortC
override def toString: String = s"EncodedShortChannelIds($encoding,${array.headOption.getOrElse("")}->${array.lastOption.getOrElse("")} size=${array.size})"
}
case class QueryShortChannelIds(chainHash: ByteVector32, shortChannelIds: EncodedShortChannelIds, tlvStream: TlvStream[QueryShortChannelIdsTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash {
case class QueryShortChannelIds(chainHash: BlockHash, shortChannelIds: EncodedShortChannelIds, tlvStream: TlvStream[QueryShortChannelIdsTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash {
val queryFlags_opt: Option[QueryShortChannelIdsTlv.EncodedQueryFlags] = tlvStream.get[QueryShortChannelIdsTlv.EncodedQueryFlags]
}
case class ReplyShortChannelIdsEnd(chainHash: ByteVector32, complete: Byte, tlvStream: TlvStream[ReplyShortChannelIdsEndTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash
case class ReplyShortChannelIdsEnd(chainHash: BlockHash, complete: Byte, tlvStream: TlvStream[ReplyShortChannelIdsEndTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash
case class QueryChannelRange(chainHash: ByteVector32, firstBlock: BlockHeight, numberOfBlocks: Long, tlvStream: TlvStream[QueryChannelRangeTlv] = TlvStream.empty) extends RoutingMessage {
case class QueryChannelRange(chainHash: BlockHash, firstBlock: BlockHeight, numberOfBlocks: Long, tlvStream: TlvStream[QueryChannelRangeTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash {
val queryFlags_opt: Option[QueryChannelRangeTlv.QueryFlags] = tlvStream.get[QueryChannelRangeTlv.QueryFlags]
}
case class ReplyChannelRange(chainHash: ByteVector32, firstBlock: BlockHeight, numberOfBlocks: Long, syncComplete: Byte, shortChannelIds: EncodedShortChannelIds, tlvStream: TlvStream[ReplyChannelRangeTlv] = TlvStream.empty) extends RoutingMessage {
case class ReplyChannelRange(chainHash: BlockHash, firstBlock: BlockHeight, numberOfBlocks: Long, syncComplete: Byte, shortChannelIds: EncodedShortChannelIds, tlvStream: TlvStream[ReplyChannelRangeTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash {
val timestamps_opt: Option[ReplyChannelRangeTlv.EncodedTimestamps] = tlvStream.get[ReplyChannelRangeTlv.EncodedTimestamps]
val checksums_opt: Option[ReplyChannelRangeTlv.EncodedChecksums] = tlvStream.get[ReplyChannelRangeTlv.EncodedChecksums]
}
object ReplyChannelRange {
def apply(chainHash: ByteVector32,
def apply(chainHash: BlockHash,
firstBlock: BlockHeight,
numberOfBlocks: Long,
syncComplete: Byte,
@ -560,7 +558,7 @@ object ReplyChannelRange {
}
}
case class GossipTimestampFilter(chainHash: ByteVector32, firstTimestamp: TimestampSecond, timestampRange: Long, tlvStream: TlvStream[GossipTimestampFilterTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash
case class GossipTimestampFilter(chainHash: BlockHash, firstTimestamp: TimestampSecond, timestampRange: Long, tlvStream: TlvStream[GossipTimestampFilterTlv] = TlvStream.empty) extends RoutingMessage with HasChainHash
case class OnionMessage(blindingKey: PublicKey, onionRoutingPacket: OnionRoutingPacket, tlvStream: TlvStream[OnionMessageTlv] = TlvStream.empty) extends LightningMessage

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.wire.protocol
import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.bitcoin.scalacompat.BlockHash
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.{BlindedNode, BlindedRoute}
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.OfferTypes.{InvoiceRequestChain, InvoiceRequestPayerNote, InvoiceRequestQuantity, _}
@ -26,7 +26,7 @@ import scodec.{Attempt, Codec, Err}
import scodec.codecs._
object OfferCodecs {
private val offerChains: Codec[OfferChains] = tlvField(list(bytes32).xmap[Seq[ByteVector32]](_.toSeq, _.toList))
private val offerChains: Codec[OfferChains] = tlvField(list(blockHash).xmap[Seq[BlockHash]](_.toSeq, _.toList))
private val offerMetadata: Codec[OfferMetadata] = tlvField(bytes)
@ -91,7 +91,7 @@ object OfferCodecs {
private val invoiceRequestMetadata: Codec[InvoiceRequestMetadata] = tlvField(bytes)
private val invoiceRequestChain: Codec[InvoiceRequestChain] = tlvField(bytes32)
private val invoiceRequestChain: Codec[InvoiceRequestChain] = tlvField(blockHash)
private val invoiceRequestAmount: Codec[InvoiceRequestAmount] = tlvField(tmillisatoshi)

View file

@ -18,12 +18,12 @@ package fr.acinq.eclair.wire.protocol
import fr.acinq.bitcoin.Bech32
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, XonlyPublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, LexicographicalOrdering}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32, ByteVector64, Crypto, LexicographicalOrdering}
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.{BlindedNode, BlindedRoute}
import fr.acinq.eclair.wire.protocol.CommonCodecs.varint
import fr.acinq.eclair.wire.protocol.OnionRoutingCodecs.{ForbiddenTlv, InvalidTlvPayload, MissingRequiredTlv}
import fr.acinq.eclair.wire.protocol.TlvCodecs.genericTlv
import fr.acinq.eclair.{Bolt12Feature, CltvExpiryDelta, Feature, Features, MilliSatoshi, RealShortChannelId, ShortChannelId, TimestampSecond, UInt64, nodeFee, randomBytes32}
import fr.acinq.eclair.{Bolt12Feature, CltvExpiryDelta, Feature, Features, MilliSatoshi, RealShortChannelId, TimestampSecond, UInt64, nodeFee, randomBytes32}
import scodec.Codec
import scodec.bits.ByteVector
import scodec.codecs.vector
@ -61,7 +61,7 @@ object OfferTypes {
/**
* Chains for which the offer is valid. If empty, bitcoin mainnet is implied.
*/
case class OfferChains(chains: Seq[ByteVector32]) extends OfferTlv
case class OfferChains(chains: Seq[BlockHash]) extends OfferTlv
/**
* Data from the offer creator to themselves, for instance a signature that authenticates the offer so that they don't need to store the offer.
@ -125,7 +125,7 @@ object OfferTypes {
/**
* If `OfferChains` is present, this specifies which chain is going to be used to pay.
*/
case class InvoiceRequestChain(hash: ByteVector32) extends InvoiceRequestTlv
case class InvoiceRequestChain(hash: BlockHash) extends InvoiceRequestTlv
/**
* Amount that the sender is going to send.
@ -216,11 +216,11 @@ object OfferTypes {
case class Signature(signature: ByteVector64) extends InvoiceRequestTlv with InvoiceTlv
def filterOfferFields(tlvs: TlvStream[InvoiceRequestTlv]): TlvStream[OfferTlv] =
// Offer TLVs are in the range (0, 80).
// Offer TLVs are in the range (0, 80).
TlvStream[OfferTlv](tlvs.records.collect { case tlv: OfferTlv => tlv }, tlvs.unknown.filter(_.tag < UInt64(80)))
def filterInvoiceRequestFields(tlvs: TlvStream[InvoiceTlv]): TlvStream[InvoiceRequestTlv] =
// Invoice request TLVs are in the range [0, 160): invoice request metadata (tag 0), offer TLVs, and additional invoice request TLVs in the range [80, 160).
// Invoice request TLVs are in the range [0, 160): invoice request metadata (tag 0), offer TLVs, and additional invoice request TLVs in the range [80, 160).
TlvStream[InvoiceRequestTlv](tlvs.records.collect { case tlv: InvoiceRequestTlv => tlv }, tlvs.unknown.filter(_.tag < UInt64(160)))
case class ErroneousField(tag: Long) extends InvoiceErrorTlv
@ -230,7 +230,7 @@ object OfferTypes {
case class Error(message: String) extends InvoiceErrorTlv
case class Offer(records: TlvStream[OfferTlv]) {
val chains: Seq[ByteVector32] = records.get[OfferChains].map(_.chains).getOrElse(Seq(Block.LivenetGenesisBlock.hash))
val chains: Seq[BlockHash] = records.get[OfferChains].map(_.chains).getOrElse(Seq(Block.LivenetGenesisBlock.hash))
val metadata: Option[ByteVector] = records.get[OfferMetadata].map(_.data)
val currency: Option[String] = records.get[OfferCurrency].map(_.iso4217)
val amount: Option[MilliSatoshi] = currency match {
@ -271,7 +271,7 @@ object OfferTypes {
description: String,
nodeId: PublicKey,
features: Features[Bolt12Feature],
chain: ByteVector32,
chain: BlockHash,
additionalTlvs: Set[OfferTlv] = Set.empty,
customTlvs: Set[GenericTlv] = Set.empty): Offer = {
val tlvs: Set[OfferTlv] = Set(
@ -310,7 +310,7 @@ object OfferTypes {
val offer: Offer = Offer.validate(filterOfferFields(records)).toOption.get
val metadata: ByteVector = records.get[InvoiceRequestMetadata].get.data
val chain: ByteVector32 = records.get[InvoiceRequestChain].map(_.hash).getOrElse(Block.LivenetGenesisBlock.hash)
val chain: BlockHash = records.get[InvoiceRequestChain].map(_.hash).getOrElse(Block.LivenetGenesisBlock.hash)
val amount: Option[MilliSatoshi] = records.get[InvoiceRequestAmount].map(_.amount)
val features: Features[Bolt12Feature] = records.get[InvoiceRequestFeatures].map(_.features.bolt12Features()).getOrElse(Features.empty)
val quantity_opt: Option[Long] = records.get[InvoiceRequestQuantity].map(_.quantity)
@ -367,7 +367,7 @@ object OfferTypes {
quantity: Long,
features: Features[Bolt12Feature],
payerKey: PrivateKey,
chain: ByteVector32,
chain: BlockHash,
additionalTlvs: Set[InvoiceRequestTlv] = Set.empty,
customTlvs: Set[GenericTlv] = Set.empty): InvoiceRequest = {
require(offer.chains.contains(chain))

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.wire.protocol
import fr.acinq.bitcoin.scalacompat.ByteVector32
import fr.acinq.bitcoin.scalacompat.BlockHash
import fr.acinq.eclair.UInt64
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
@ -33,7 +33,7 @@ sealed trait InitTlv extends Tlv
object InitTlv {
/** The chains the node is interested in. */
case class Networks(chainHashes: List[ByteVector32]) extends InitTlv
case class Networks(chainHashes: List[BlockHash]) extends InitTlv
/**
* When receiving an incoming connection, we can send back the public address our peer is connecting from.
@ -47,7 +47,7 @@ object InitTlvCodecs {
import InitTlv._
private val networks: Codec[Networks] = tlvField(list(bytes32))
private val networks: Codec[Networks] = tlvField(list(blockHash))
private val remoteAddress: Codec[RemoteAddress] = tlvField(nodeaddress)
val initTlvCodec = tlvStream(discriminated[InitTlv].by(varint)

View file

@ -120,7 +120,7 @@
"waitingSince" : 400000,
"lastSent" : {
"temporaryChannelId" : "0000000000000000000000000000000000000000000000000000000000000000",
"fundingTxHash" : "e917adc681383fe00f779c6144a1bd91135ba2c9862ad1bc5aa8a14d37bae3f4",
"fundingTxId" : "f4e3ba374da1a85abcd12a86c9a25b1391bda144619c770fe03f3881c6ad17e9",
"fundingOutputIndex" : 0,
"signature" : "bd55d8660f54a1be6e123e2ed9cd6669d90b830d99dc6f9addd9ae65c447ea6b657e2fe39841f66c1d6a2fc80ad59e6f3d9bfaf177f4e95a579f0683bf3e9790",
"tlvStream" : { }

View file

@ -22,7 +22,7 @@ import akka.pattern.pipe
import akka.testkit.TestProbe
import akka.util.Timeout
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, SatoshiLong, TxId}
import fr.acinq.eclair.ApiTypes.{ChannelIdentifier, ChannelNotFound}
import fr.acinq.eclair.TestConstants._
import fr.acinq.eclair.blockchain.DummyOnChainWallet
@ -235,7 +235,7 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I
(ann_cd, makeUpdateShort(ShortChannelId(3L), c, d, feeBase = 1 msat, 0, minHtlc = 0 msat, maxHtlc = None, cltvDelta = CltvExpiryDelta(500))),
(ann_ec, makeUpdateShort(ShortChannelId(7L), e, c, feeBase = 2 msat, 0, minHtlc = 0 msat, maxHtlc = None, cltvDelta = CltvExpiryDelta(12)))
).map { case (ann, update) =>
update.shortChannelId -> PublicChannel(ann, ByteVector32.Zeroes, 100 sat, Some(update.copy(channelFlags = ChannelUpdate.ChannelFlags.DUMMY)), None, None)
update.shortChannelId -> PublicChannel(ann, TxId(ByteVector32.Zeroes), 100 sat, Some(update.copy(channelFlags = ChannelUpdate.ChannelFlags.DUMMY)), None, None)
}: _*)
val eclair = new EclairImpl(kit)
@ -361,8 +361,8 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I
val channel1 = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, RealShortChannelId(1), a, b, a, b, ByteVector64.Zeroes, ByteVector64.Zeroes, ByteVector64.Zeroes, ByteVector64.Zeroes)
val channel2 = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, RealShortChannelId(2), b, c, b, c, ByteVector64.Zeroes, ByteVector64.Zeroes, ByteVector64.Zeroes, ByteVector64.Zeroes)
val publicChannels = SortedMap(
channel1.shortChannelId -> PublicChannel(channel1, ByteVector32.Zeroes, 100_000 sat, None, None, None),
channel2.shortChannelId -> PublicChannel(channel2, ByteVector32.Zeroes, 150_000 sat, None, None, None),
channel1.shortChannelId -> PublicChannel(channel1, TxId(ByteVector32.Zeroes), 100_000 sat, None, None, None),
channel2.shortChannelId -> PublicChannel(channel2, TxId(ByteVector32.Zeroes), 150_000 sat, None, None, None),
)
val (channelId3, shortIds3) = (randomBytes32(), ShortIds(RealScidStatus.Unknown, Alias(13), None))
val (channelId4, shortIds4) = (randomBytes32(), ShortIds(RealScidStatus.Final(RealShortChannelId(4)), Alias(14), None))

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Crypto, Script, addressToPublicKeyScript}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Crypto, Script, TxHash, TxId, addressToPublicKeyScript}
import fr.acinq.bitcoin.{Base58, Base58Check, Bech32}
import org.scalatest.funsuite.AnyFunSuite
import scodec.bits._
@ -32,13 +32,15 @@ class PackageSpec extends AnyFunSuite {
implicit def byteVector322array(input: ByteVector32): Array[Byte] = input.toArray
test("compute long channel id") {
val data = ((hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0, hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") ::
(hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1, hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE") ::
(hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000", 2, hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0002") ::
(hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00F0", 0x0F00, hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FF0") :: Nil)
.map(x => (ByteVector32(x._1), x._2, ByteVector32(x._3)))
case class TestCase(fundingTxId: TxId, fundingOutputIndex: Int, expected: ByteVector32)
data.foreach(x => assert(toLongId(ByteVector32(x._1), x._2) == x._3))
val testCases = Seq(
TestCase(TxId.fromValidHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 0, ByteVector32.fromValidHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")),
TestCase(TxId.fromValidHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 1, ByteVector32.fromValidHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")),
TestCase(TxId.fromValidHex("0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 2, ByteVector32.fromValidHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0002")),
TestCase(TxId.fromValidHex("F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 0x0f00, ByteVector32.fromValidHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FF0")),
)
testCases.foreach(t => assert(toLongId(t.fundingTxId, t.fundingOutputIndex) == t.expected))
}
test("decode base58 addresses") {

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair
import akka.actor.ActorSystem
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Transaction}
import fr.acinq.bitcoin.scalacompat.{BlockId, Transaction, TxId}
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinJsonRPCAuthMethod.UserPassword
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinCoreClient}
@ -32,17 +32,17 @@ class TestBitcoinCoreClient()(implicit system: ActorSystem) extends BitcoinCoreC
import scala.concurrent.ExecutionContext.Implicits.global
system.scheduler.scheduleWithFixedDelay(100 milliseconds, 100 milliseconds)(() => system.eventStream.publish(NewBlock(randomBytes32())))
system.scheduler.scheduleWithFixedDelay(100 milliseconds, 100 milliseconds)(() => system.eventStream.publish(NewBlock(BlockId(randomBytes32()))))
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[ByteVector32] = {
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[TxId] = {
system.eventStream.publish(NewTransaction(tx))
Future.successful(tx.txid)
}
override def getTxConfirmations(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]] = Future.successful(Some(10))
override def getTxConfirmations(txId: TxId)(implicit ec: ExecutionContext): Future[Option[Int]] = Future.successful(Some(10))
override def getTransaction(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Transaction] = Future.failed(new RuntimeException("not implemented"))
override def getTransaction(txId: TxId)(implicit ec: ExecutionContext): Future[Transaction] = Future.failed(new RuntimeException("not implemented"))
override def getTransactionShortId(txId: ByteVector32)(implicit ec: ExecutionContext): Future[(BlockHeight, Int)] = Future.successful((BlockHeight(400000), 42))
override def getTransactionShortId(txId: TxId)(implicit ec: ExecutionContext): Future[(BlockHeight, Int)] = Future.successful((BlockHeight(400000), 42))
}

View file

@ -19,17 +19,17 @@ package fr.acinq.eclair
import akka.actor.{ActorRef, ActorSystem}
import akka.event.{DiagnosticLoggingAdapter, EventStream}
import akka.testkit.{TestActor, TestProbe}
import fr.acinq.bitcoin.scalacompat.TxId
import fr.acinq.eclair.channel.fsm.Channel
import fr.acinq.eclair.io.Peer
import fr.acinq.eclair.wire.protocol.LightningMessage
import org.scalatest.concurrent.Eventually.eventually
import org.scalatest.concurrent.PatienceConfiguration
import java.io.File
import java.net.ServerSocket
import java.nio.file.Files
import java.util.UUID
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.duration.FiniteDuration
object TestUtils {
@ -110,4 +110,6 @@ object TestUtils {
def waitFor(duration: FiniteDuration): Unit = Thread.sleep(duration.toMillis)
def randomTxId(): TxId = TxId(randomBytes32())
}

View file

@ -2,7 +2,8 @@ package fr.acinq.eclair.balance
import akka.pattern.pipe
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong, TxId}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.balance.CheckBalance.{ClosingBalance, MainAndHtlcBalance, OffChainBalance, PossiblyPublishedMainAndHtlcBalance, PossiblyPublishedMainBalance}
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{apply => _, _}
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
@ -250,12 +251,12 @@ class CheckBalanceSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
}
test("tx pruning") { () =>
val txids = (for (_ <- 0 until 20) yield randomBytes32()).toList
val txids = (for (_ <- 0 until 20) yield randomTxId()).toList
val knownTxids = Set(txids(1), txids(3), txids(4), txids(6), txids(9), txids(12), txids(13))
val bitcoinClient = new BitcoinCoreClient(null) {
/** Get the number of confirmations of a given transaction. */
override def getTxConfirmations(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]] =
override def getTxConfirmations(txid: TxId)(implicit ec: ExecutionContext): Future[Option[Int]] =
Future.successful(if (knownTxids.contains(txid)) Some(42) else None)
}

View file

@ -19,13 +19,14 @@ package fr.acinq.eclair.blockchain
import fr.acinq.bitcoin.TxIn.SEQUENCE_FINAL
import fr.acinq.bitcoin.psbt.Psbt
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, OutPoint, Satoshi, SatoshiLong, Script, Transaction, TxIn, TxOut}
import fr.acinq.bitcoin.scalacompat.{Crypto, OutPoint, Satoshi, SatoshiLong, Script, Transaction, TxId, TxIn, TxOut}
import fr.acinq.bitcoin.{Bech32, SigHash, SigVersion}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.OnChainWallet.{FundTransactionResponse, MakeFundingTxResponse, OnChainBalance, ProcessPsbtResponse}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.SignTransactionResponse
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.randomKey
import fr.acinq.eclair.transactions.Transactions
import fr.acinq.eclair.{randomBytes32, randomKey}
import scodec.bits._
import scala.concurrent.{ExecutionContext, Future, Promise}
@ -38,8 +39,8 @@ class DummyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
import DummyOnChainWallet._
val funded = collection.concurrent.TrieMap.empty[ByteVector32, Transaction]
val published = collection.concurrent.TrieMap.empty[ByteVector32, Transaction]
val funded = collection.concurrent.TrieMap.empty[TxId, Transaction]
val published = collection.concurrent.TrieMap.empty[TxId, Transaction]
var rolledback = Set.empty[Transaction]
override def onChainBalance()(implicit ec: ExecutionContext): Future[OnChainBalance] = Future.successful(OnChainBalance(1105 sat, 561 sat))
@ -55,7 +56,7 @@ class DummyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
override def signPsbt(psbt: Psbt, ourInputs: Seq[Int], ourOutputs: Seq[Int])(implicit ec: ExecutionContext): Future[ProcessPsbtResponse] = Future.successful(ProcessPsbtResponse(psbt, complete = true))
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[ByteVector32] = {
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[TxId] = {
published += (tx.txid -> tx)
Future.successful(tx.txid)
}
@ -68,9 +69,9 @@ class DummyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
override def commit(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = publishTransaction(tx).map(_ => true)
override def getTransaction(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Transaction] = Future.failed(new RuntimeException("transaction not found"))
override def getTransaction(txId: TxId)(implicit ec: ExecutionContext): Future[Transaction] = Future.failed(new RuntimeException("transaction not found"))
override def getTxConfirmations(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]] = Future.failed(new RuntimeException("transaction not found"))
override def getTxConfirmations(txid: TxId)(implicit ec: ExecutionContext): Future[Option[Int]] = Future.failed(new RuntimeException("transaction not found"))
override def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = {
rolledback = rolledback + tx
@ -87,7 +88,7 @@ class NoOpOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
import DummyOnChainWallet._
var rolledback = Seq.empty[Transaction]
var doubleSpent = Set.empty[ByteVector32]
var doubleSpent = Set.empty[TxId]
override def onChainBalance()(implicit ec: ExecutionContext): Future[OnChainBalance] = Future.successful(OnChainBalance(1105 sat, 561 sat))
@ -99,15 +100,15 @@ class NoOpOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
override def signPsbt(psbt: Psbt, ourInputs: Seq[Int], ourOutputs: Seq[Int])(implicit ec: ExecutionContext): Future[ProcessPsbtResponse] = Promise().future // will never be completed
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[ByteVector32] = Future.successful(tx.txid)
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[TxId] = Future.successful(tx.txid)
override def makeFundingTx(pubkeyScript: ByteVector, amount: Satoshi, feeRatePerKw: FeeratePerKw)(implicit ec: ExecutionContext): Future[MakeFundingTxResponse] = Promise().future // will never be completed
override def commit(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = Future.successful(true)
override def getTransaction(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Transaction] = Promise().future // will never be completed
override def getTransaction(txId: TxId)(implicit ec: ExecutionContext): Future[Transaction] = Promise().future // will never be completed
override def getTxConfirmations(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]] = Promise().future // will never be completed
override def getTxConfirmations(txid: TxId)(implicit ec: ExecutionContext): Future[Option[Int]] = Promise().future // will never be completed
override def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = {
rolledback = rolledback :+ tx
@ -125,7 +126,7 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
// We create a new dummy input transaction for every funding request.
var inputs = Seq.empty[Transaction]
var rolledback = Seq.empty[Transaction]
var doubleSpent = Set.empty[ByteVector32]
var doubleSpent = Set.empty[TxId]
override def onChainBalance()(implicit ec: ExecutionContext): Future[OnChainBalance] = Future.successful(OnChainBalance(1105 sat, 561 sat))
@ -138,7 +139,7 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
val amountOut = tx.txOut.map(_.amount).sum
// We add a single input to reach the desired feerate.
val inputAmount = amountOut + 100_000.sat
val inputTx = Transaction(2, Seq(TxIn(OutPoint(randomBytes32(), 1), Nil, 0)), Seq(TxOut(inputAmount, Script.pay2wpkh(pubkey))), 0)
val inputTx = Transaction(2, Seq(TxIn(OutPoint(randomTxId(), 1), Nil, 0)), Seq(TxOut(inputAmount, Script.pay2wpkh(pubkey))), 0)
inputs = inputs :+ inputTx
val dummyWitness = Script.witnessPay2wpkh(pubkey, ByteVector.fill(73)(0))
val dummySignedTx = tx.copy(
@ -166,7 +167,7 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
Future.successful(SignTransactionResponse(signedTx, complete))
}
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[ByteVector32] = {
override def publishTransaction(tx: Transaction)(implicit ec: ExecutionContext): Future[TxId] = {
inputs = inputs :+ tx
Future.successful(tx.txid)
}
@ -174,7 +175,7 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
override def signPsbt(psbt: Psbt, ourInputs: Seq[Int], ourOutputs: Seq[Int])(implicit ec: ExecutionContext): Future[ProcessPsbtResponse] = {
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
val tx: Transaction = psbt.getGlobal.getTx
val tx: Transaction = psbt.global.tx
val signedPsbt = tx.txIn.zipWithIndex.foldLeft(new Psbt(tx)) {
case (currentPsbt, (txIn, index)) => inputs.find(_.txid == txIn.outPoint.txid) match {
case Some(inputTx) =>
@ -198,14 +199,14 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
override def commit(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = Future.successful(true)
override def getTransaction(txId: ByteVector32)(implicit ec: ExecutionContext): Future[Transaction] = synchronized {
override def getTransaction(txId: TxId)(implicit ec: ExecutionContext): Future[Transaction] = synchronized {
inputs.find(_.txid == txId) match {
case Some(tx) => Future.successful(tx)
case None => Future.failed(new RuntimeException(s"txid=$txId not found"))
}
}
override def getTxConfirmations(txid: ByteVector32)(implicit ec: ExecutionContext): Future[Option[Int]] = Future.successful(None)
override def getTxConfirmations(txid: TxId)(implicit ec: ExecutionContext): Future[Option[Int]] = Future.successful(None)
override def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = {
rolledback = rolledback :+ tx
@ -225,7 +226,7 @@ object DummyOnChainWallet {
def makeDummyFundingTx(pubkeyScript: ByteVector, amount: Satoshi): MakeFundingTxResponse = {
val fundingTx = Transaction(
version = 2,
txIn = TxIn(OutPoint(ByteVector32(ByteVector.fill(32)(1)), 42), signatureScript = Nil, sequence = SEQUENCE_FINAL) :: Nil,
txIn = TxIn(OutPoint(TxId.fromValidHex("0101010101010101010101010101010101010101010101010101010101010101"), 42), signatureScript = Nil, sequence = SEQUENCE_FINAL) :: Nil,
txOut = TxOut(amount, pubkeyScript) :: Nil,
lockTime = 0
)

View file

@ -22,8 +22,9 @@ import akka.testkit.TestProbe
import fr.acinq.bitcoin
import fr.acinq.bitcoin.psbt.{Psbt, UpdateFailure}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, Btc, BtcDouble, ByteVector32, Crypto, DeterministicWallet, MilliBtcDouble, MnemonicCode, OP_DROP, OP_PUSHDATA, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxIn, TxOut, addressFromPublicKeyScript, addressToPublicKeyScript, computeBIP84Address, computeP2PkhAddress, computeP2WpkhAddress}
import fr.acinq.bitcoin.scalacompat.{Block, Btc, BtcDouble, ByteVector32, Crypto, DeterministicWallet, MilliBtcDouble, MnemonicCode, OP_DROP, OP_PUSHDATA, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxId, TxIn, TxOut, addressFromPublicKeyScript, addressToPublicKeyScript, computeBIP84Address, computeP2PkhAddress, computeP2WpkhAddress}
import fr.acinq.bitcoin.{Bech32, SigHash, SigVersion}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.OnChainWallet.{FundTransactionResponse, MakeFundingTxResponse, OnChainBalance, ProcessPsbtResponse}
import fr.acinq.eclair.blockchain.WatcherSpec.{createSpendManyP2WPKH, createSpendP2WPKH}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.{BitcoinReq, SignTransactionResponse}
@ -198,7 +199,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
walletExternalFunds.getReceiveAddress().pipeTo(sender.ref)
val walletAddress = sender.expectMsgType[String]
defaultWallet.sendToPubkeyScript(Script.write(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, walletAddress).toOption.get), amount, FeeratePerKw(FeeratePerByte(3.sat))).pipeTo(sender.ref)
sender.expectMsgType[ByteVector32]
sender.expectMsgType[TxId]
})
// We receive more funds on an address that does not belong to our wallet.
@ -664,7 +665,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
val nonWalletWitness = ScriptWitness(Seq(nonWalletSig, nonWalletKey.publicKey.value))
val txWithSignedNonWalletInput = txWithNonWalletInput.updateWitness(0, nonWalletWitness)
val psbt = new Psbt(txWithSignedNonWalletInput)
val updated: Either[UpdateFailure, Psbt] = psbt.updateWitnessInput(psbt.getGlobal.getTx.txIn.get(0).outPoint, txToRemote.txOut(0), null, fr.acinq.bitcoin.Script.pay2pkh(nonWalletKey.publicKey), SigHash.SIGHASH_ALL, psbt.getInput(0).getDerivationPaths)
val updated: Either[UpdateFailure, Psbt] = psbt.updateWitnessInput(psbt.global.tx.txIn.get(0).outPoint, txToRemote.txOut(0), null, fr.acinq.bitcoin.Script.pay2pkh(nonWalletKey.publicKey), SigHash.SIGHASH_ALL, psbt.getInput(0).getDerivationPaths)
val Right(psbt1) = updated.flatMap(_.finalizeWitnessInput(0, nonWalletWitness))
bitcoinClient.signPsbt(psbt1, txWithSignedNonWalletInput.txIn.indices.tail, Nil).pipeTo(sender.ref)
val signTxResponse2 = sender.expectMsgType[ProcessPsbtResponse]
@ -696,7 +697,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
val nonWalletWitness = ScriptWitness(Seq(nonWalletSig, nonWalletKey.publicKey.value))
val txWithSignedUnconfirmedInput = txWithUnconfirmedInput.updateWitness(0, nonWalletWitness)
val psbt = new Psbt(txWithSignedUnconfirmedInput)
val Right(psbt1) = psbt.updateWitnessInput(psbt.getGlobal.getTx.txIn.get(0).outPoint, unconfirmedTx.txOut(0), null, fr.acinq.bitcoin.Script.pay2pkh(nonWalletKey.publicKey), SigHash.SIGHASH_ALL, psbt.getInput(0).getDerivationPaths)
val Right(psbt1) = psbt.updateWitnessInput(psbt.global.tx.txIn.get(0).outPoint, unconfirmedTx.txOut(0), null, fr.acinq.bitcoin.Script.pay2pkh(nonWalletKey.publicKey), SigHash.SIGHASH_ALL, psbt.getInput(0).getDerivationPaths)
.flatMap(_.finalizeWitnessInput(0, nonWalletWitness))
bitcoinClient.signPsbt(psbt1, txWithSignedUnconfirmedInput.txIn.indices.tail, Nil).pipeTo(sender.ref)
assert(sender.expectMsgType[ProcessPsbtResponse].complete)
@ -733,7 +734,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
val spendingTx = {
val address = getNewAddress(sender)
val pos = if (changePos == 0) 1 else 0
bitcoinrpcclient.invoke("createrawtransaction", Array(Map("txid" -> tx.txid.toHex, "vout" -> pos)), Map(address -> 5.999)).pipeTo(sender.ref)
bitcoinrpcclient.invoke("createrawtransaction", Array(Map("txid" -> tx.txid.value.toHex, "vout" -> pos)), Map(address -> 5.999)).pipeTo(sender.ref)
val JString(unsignedTxStr) = sender.expectMsgType[JValue]
val unsignedTx = Transaction.read(unsignedTxStr)
val sig = Transaction.signInput(unsignedTx, 0, Script.pay2pkh(priv.publicKey), bitcoin.SigHash.SIGHASH_ALL, 6.btc.toSatoshi, bitcoin.SigVersion.SIGVERSION_WITNESS_V0, priv)
@ -1026,7 +1027,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
signTxResponse.tx
}
def getMempoolTx(txid: ByteVector32): MempoolTx = {
def getMempoolTx(txid: TxId): MempoolTx = {
val probe = TestProbe()
bitcoinClient.getMempoolTx(txid).pipeTo(probe.ref)
probe.expectMsgType[MempoolTx]
@ -1090,7 +1091,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
test("cannot bump transaction fees (unknown transaction)") {
val sender = TestProbe()
val bitcoinClient = makeBitcoinCoreClient()
bitcoinClient.cpfp(Set(OutPoint(randomBytes32(), 0), OutPoint(randomBytes32(), 3)), FeeratePerKw(1500 sat)).pipeTo(sender.ref)
bitcoinClient.cpfp(Set(OutPoint(randomTxId(), 0), OutPoint(randomTxId(), 3)), FeeratePerKw(1500 sat)).pipeTo(sender.ref)
val failure = sender.expectMsgType[Failure]
assert(failure.cause.getMessage.contains("some transactions could not be found"))
}
@ -1328,7 +1329,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
val pubKey = randomKey().publicKey
val legacyAddress = computeP2PkhAddress(pubKey, Block.RegtestGenesisBlock.hash)
bitcoinClient.sendToPubkeyScript(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, legacyAddress).toOption.get, 150_000 sat, FeeratePerKw(FeeratePerByte(3.sat))).pipeTo(sender.ref)
val txId = sender.expectMsgType[ByteVector32]
val txId = sender.expectMsgType[TxId]
bitcoinClient.getTransaction(txId).pipeTo(sender.ref)
val tx = sender.expectMsgType[Transaction]
// We have a change output.
@ -1468,7 +1469,7 @@ class BitcoinCoreClientWithEclairSignerSpec extends BitcoinCoreClientSpec {
assert(error.cause.getMessage.contains("Private keys are disabled for this wallet"))
wallet.sendToPubkeyScript(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, address).toOption.get, 50_000.sat, FeeratePerKw(FeeratePerByte(5.sat))).pipeTo(sender.ref)
sender.expectMsgType[ByteVector32]
sender.expectMsgType[TxId]
}
}
}

View file

@ -21,7 +21,8 @@ import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOp
import akka.actor.{ActorRef, Props, typed}
import akka.pattern.pipe
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.{Block, Btc, MilliBtcDouble, OutPoint, SatoshiLong, Script, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{Block, Btc, MilliBtcDouble, OutPoint, SatoshiLong, Script, Transaction, TxId, TxOut}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.OnChainWallet.{FundTransactionResponse, MakeFundingTxResponse}
import fr.acinq.eclair.blockchain.WatcherSpec._
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.SignTransactionResponse
@ -31,7 +32,7 @@ import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.FundTransaction
import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.blockchain.{CurrentBlockHeight, NewTransaction}
import fr.acinq.eclair.{BlockHeight, RealShortChannelId, TestConstants, TestKitBaseClass, randomBytes32, randomKey}
import fr.acinq.eclair.{BlockHeight, RealShortChannelId, TestConstants, TestKitBaseClass, randomKey}
import grizzled.slf4j.Logging
import org.scalatest.BeforeAndAfterAll
import org.scalatest.funsuite.AnyFunSuiteLike
@ -104,14 +105,14 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
test("add/remove watches from/to utxo map") {
val m0 = Map.empty[OutPoint, Set[Watch[_ <: WatchTriggered]]]
val txid = randomBytes32()
val txid = randomTxId()
val outputIndex = 42
val utxo = OutPoint(txid.reverse, outputIndex)
val utxo = OutPoint(txid, outputIndex)
val w1 = WatchFundingSpent(TestProbe().ref, txid, outputIndex, hints = Set.empty)
val w2 = WatchFundingSpent(TestProbe().ref, txid, outputIndex, hints = Set.empty)
val w3 = WatchExternalChannelSpent(TestProbe().ref, txid, outputIndex, RealShortChannelId(1))
val w4 = WatchExternalChannelSpent(TestProbe().ref, randomBytes32(), 5, RealShortChannelId(1))
val w4 = WatchExternalChannelSpent(TestProbe().ref, randomTxId(), 5, RealShortChannelId(1))
val w5 = WatchFundingConfirmed(TestProbe().ref, txid, 3)
// we test as if the collection was immutable
@ -122,17 +123,17 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
val m3 = addWatchedUtxos(m2, w3)
assert(m3.keySet == Set(utxo) && m3(utxo).size == 3)
val m4 = addWatchedUtxos(m3, w4)
assert(m4.keySet == Set(utxo, OutPoint(w4.txId.reverse, w4.outputIndex)) && m3(utxo).size == 3)
assert(m4.keySet == Set(utxo, OutPoint(w4.txId, w4.outputIndex)) && m3(utxo).size == 3)
val m5 = addWatchedUtxos(m4, w5)
assert(m5.keySet == Set(utxo, OutPoint(w4.txId.reverse, w4.outputIndex)) && m5(utxo).size == 3)
assert(m5.keySet == Set(utxo, OutPoint(w4.txId, w4.outputIndex)) && m5(utxo).size == 3)
val m6 = removeWatchedUtxos(m5, w3)
assert(m6.keySet == Set(utxo, OutPoint(w4.txId.reverse, w4.outputIndex)) && m6(utxo).size == 2)
assert(m6.keySet == Set(utxo, OutPoint(w4.txId, w4.outputIndex)) && m6(utxo).size == 2)
val m7 = removeWatchedUtxos(m6, w3)
assert(m7.keySet == Set(utxo, OutPoint(w4.txId.reverse, w4.outputIndex)) && m7(utxo).size == 2)
assert(m7.keySet == Set(utxo, OutPoint(w4.txId, w4.outputIndex)) && m7(utxo).size == 2)
val m8 = removeWatchedUtxos(m7, w2)
assert(m8.keySet == Set(utxo, OutPoint(w4.txId.reverse, w4.outputIndex)) && m8(utxo).size == 1)
assert(m8.keySet == Set(utxo, OutPoint(w4.txId, w4.outputIndex)) && m8(utxo).size == 1)
val m9 = removeWatchedUtxos(m8, w1)
assert(m9.keySet == Set(OutPoint(w4.txId.reverse, w4.outputIndex)))
assert(m9.keySet == Set(OutPoint(w4.txId, w4.outputIndex)))
val m10 = removeWatchedUtxos(m9, w4)
assert(m10.isEmpty)
}
@ -281,7 +282,7 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
watcher ! StopWatching(probe.ref)
// We should still find tx2 if the provided hint is wrong
watcher ! WatchOutputSpent(probe.ref, tx1.txid, 0, Set(randomBytes32()))
watcher ! WatchOutputSpent(probe.ref, tx1.txid, 0, Set(randomTxId()))
probe.fishForMessage() { case m: WatchOutputSpentTriggered => m.spendingTx.txid == tx2.txid }
watcher ! StopWatching(probe.ref)
@ -359,11 +360,11 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
val actor1 = TestProbe()
val actor2 = TestProbe()
val txid = randomBytes32()
val txid = randomTxId()
watcher ! WatchFundingConfirmed(actor1.ref, txid, 2)
watcher ! WatchFundingConfirmed(actor1.ref, txid, 3)
watcher ! WatchFundingDeeplyBuried(actor1.ref, txid, 3)
watcher ! WatchFundingConfirmed(actor1.ref, txid.reverse, 3)
watcher ! WatchFundingConfirmed(actor1.ref, TxId(txid.value.reverse), 3)
watcher ! WatchOutputSpent(actor1.ref, txid, 0, Set.empty)
watcher ! WatchOutputSpent(actor1.ref, txid, 1, Set.empty)
watcher ! ListWatches(actor1.ref)
@ -372,7 +373,7 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
watcher ! WatchFundingConfirmed(actor2.ref, txid, 2)
watcher ! WatchFundingDeeplyBuried(actor2.ref, txid, 3)
watcher ! WatchFundingConfirmed(actor2.ref, txid.reverse, 3)
watcher ! WatchFundingConfirmed(actor2.ref, TxId(txid.value.reverse), 3)
watcher ! WatchOutputSpent(actor2.ref, txid, 0, Set.empty)
watcher ! WatchOutputSpent(actor2.ref, txid, 1, Set.empty)
watcher ! ListWatches(actor2.ref)

View file

@ -18,6 +18,7 @@ package fr.acinq.eclair.channel
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, DeterministicWallet, Satoshi, SatoshiLong, Transaction}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain.fee._
import fr.acinq.eclair.channel.Helpers.Funding
@ -30,7 +31,6 @@ import fr.acinq.eclair.wire.protocol.{IncorrectOrUnknownPaymentDetails, UpdateAd
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
import org.scalatest.{Outcome, Tag}
import java.util.concurrent.atomic.AtomicReference
import scala.concurrent.duration._
import scala.util.Random
@ -489,9 +489,9 @@ object CommitmentsSpec {
val localParams = LocalParams(randomKey().publicKey, DeterministicWallet.KeyPath(Seq(42L)), dustLimit, Long.MaxValue.msat, Some(channelReserve), 1 msat, CltvExpiryDelta(144), 50, isInitiator, None, None, Features.empty)
val remoteParams = RemoteParams(randomKey().publicKey, dustLimit, UInt64.MaxValue, Some(channelReserve), 1 msat, CltvExpiryDelta(144), 50, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, Features.empty, None)
val remoteFundingPubKey = randomKey().publicKey
val commitmentInput = Funding.makeFundingInputInfo(randomBytes32(), 0, (toLocal + toRemote).truncateToSatoshi, randomKey().publicKey, remoteFundingPubKey)
val commitmentInput = Funding.makeFundingInputInfo(randomTxId(), 0, (toLocal + toRemote).truncateToSatoshi, randomKey().publicKey, remoteFundingPubKey)
val localCommit = LocalCommit(0, CommitmentSpec(Set.empty, feeRatePerKw, toLocal, toRemote), CommitTxAndRemoteSig(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), ByteVector64.Zeroes), Nil)
val remoteCommit = RemoteCommit(0, CommitmentSpec(Set.empty, feeRatePerKw, toRemote, toLocal), randomBytes32(), randomKey().publicKey)
val remoteCommit = RemoteCommit(0, CommitmentSpec(Set.empty, feeRatePerKw, toRemote, toLocal), randomTxId(), randomKey().publicKey)
Commitments(
ChannelParams(randomBytes32(), ChannelConfig.standard, ChannelFeatures(), localParams, remoteParams, ChannelFlags(announceChannel = announceChannel)),
CommitmentChanges(LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 1, remoteNextHtlcId = 1),
@ -508,9 +508,9 @@ object CommitmentsSpec {
val localParams = LocalParams(localNodeId, DeterministicWallet.KeyPath(Seq(42L)), 0 sat, Long.MaxValue.msat, Some(channelReserve), 1 msat, CltvExpiryDelta(144), 50, isInitiator = true, None, None, Features.empty)
val remoteParams = RemoteParams(remoteNodeId, 0 sat, UInt64.MaxValue, Some(channelReserve), 1 msat, CltvExpiryDelta(144), 50, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, Features.empty, None)
val remoteFundingPubKey = randomKey().publicKey
val commitmentInput = Funding.makeFundingInputInfo(randomBytes32(), 0, (toLocal + toRemote).truncateToSatoshi, randomKey().publicKey, remoteFundingPubKey)
val commitmentInput = Funding.makeFundingInputInfo(randomTxId(), 0, (toLocal + toRemote).truncateToSatoshi, randomKey().publicKey, remoteFundingPubKey)
val localCommit = LocalCommit(0, CommitmentSpec(Set.empty, FeeratePerKw(0 sat), toLocal, toRemote), CommitTxAndRemoteSig(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), ByteVector64.Zeroes), Nil)
val remoteCommit = RemoteCommit(0, CommitmentSpec(Set.empty, FeeratePerKw(0 sat), toRemote, toLocal), randomBytes32(), randomKey().publicKey)
val remoteCommit = RemoteCommit(0, CommitmentSpec(Set.empty, FeeratePerKw(0 sat), toRemote, toLocal), randomTxId(), randomKey().publicKey)
Commitments(
ChannelParams(randomBytes32(), ChannelConfig.standard, ChannelFeatures(), localParams, remoteParams, ChannelFlags(announceChannel = announceChannel)),
CommitmentChanges(LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 1, remoteNextHtlcId = 1),

View file

@ -228,7 +228,7 @@ class HelpersSpec extends TestKitBaseClass with AnyFunSuiteLike with ChannelStat
)
def toClosingTx(txOut: Seq[TxOut]): ClosingTx = {
ClosingTx(InputInfo(OutPoint(ByteVector32.Zeroes, 0), TxOut(1000 sat, Nil), Nil), Transaction(2, Nil, txOut, 0), None)
ClosingTx(InputInfo(OutPoint(TxId(ByteVector32.Zeroes), 0), TxOut(1000 sat, Nil), Nil), Transaction(2, Nil, txOut, 0), None)
}
assert(Closing.MutualClose.checkClosingDustAmounts(toClosingTx(allOutputsAboveDust)))

View file

@ -23,7 +23,8 @@ import akka.testkit.TestProbe
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
import fr.acinq.bitcoin.psbt.Psbt
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, OP_1, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxOut, addressToPublicKeyScript}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, OP_1, OutPoint, Satoshi, SatoshiLong, Script, ScriptWitness, Transaction, TxHash, TxId, TxOut, addressToPublicKeyScript}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.OnChainWallet.{FundTransactionResponse, ProcessPsbtResponse}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.{MempoolTx, Utxo}
@ -70,7 +71,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
txid <- client.publishTransaction(signed.finalTx_opt.toOption.get)
} yield txid
f.pipeTo(probe.ref)
probe.expectMsgType[ByteVector32]
probe.expectMsgType[TxId]
}
private def createInput(channelId: ByteVector32, serialId: UInt64, amount: Satoshi): TxAddInput = {
@ -105,7 +106,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
val fundingPubkeyScript: ByteVector = Script.write(Script.pay2wsh(Scripts.multiSig2of2(fundingParamsB.remoteFundingPubKey, fundingParamsA.remoteFundingPubKey)))
def dummySharedInputB(amount: Satoshi): SharedFundingInput = {
val inputInfo = InputInfo(OutPoint(randomBytes32(), 3), TxOut(amount, fundingPubkeyScript), Nil)
val inputInfo = InputInfo(OutPoint(randomTxId(), 3), TxOut(amount, fundingPubkeyScript), Nil)
val fundingTxIndex = fundingParamsA.sharedInput_opt match {
case Some(input: Multisig2of2Input) => input.fundingTxIndex + 1
case _ => 0
@ -1034,7 +1035,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
val Right(signedTx) = probe.expectMsgType[ProcessPsbtResponse].finalTx_opt
assert(Transaction.write(signedTx).length >= 65_000)
minerWallet.publishTransaction(signedTx).pipeTo(probe.ref)
probe.expectMsgType[ByteVector32]
probe.expectMsgType[TxId]
}
generateBlocks(1)
@ -1805,7 +1806,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
val probe = TestProbe()
walletB.getP2wpkhPubkey().pipeTo(probe.ref)
walletB.sendToPubkeyScript(Script.write(Script.pay2wpkh(probe.expectMsgType[PublicKey])), 75_000 sat, FeeratePerKw(FeeratePerByte(1.sat))).pipeTo(probe.ref)
probe.expectMsgType[ByteVector32]
probe.expectMsgType[TxId]
alice ! Start(alice2bob.ref)
bob ! Start(bob2alice.ref)
@ -1841,7 +1842,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
val probe = TestProbe()
walletB.getReceiveAddress().pipeTo(probe.ref)
walletB.sendToAddress(probe.expectMsgType[String], 75_000 sat, 1).pipeTo(probe.ref)
probe.expectMsgType[ByteVector32]
probe.expectMsgType[TxId]
alice ! Start(alice2bob.ref)
bob ! Start(bob2alice.ref)
@ -2335,7 +2336,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
bob ! Start(probe.ref)
// Alice --- tx_add_input --> Bob
// The input doesn't include the previous transaction but is not the shared input.
val nonSharedInput = TxAddInput(params.channelId, UInt64(0), OutPoint(randomBytes32(), 7), 0)
val nonSharedInput = TxAddInput(params.channelId, UInt64(0), OutPoint(randomTxId(), 7), 0)
bob ! ReceiveMessage(nonSharedInput)
assert(probe.expectMsgType[RemoteFailure].cause == PreviousTxMissing(params.channelId, UInt64(0)))
}
@ -2438,7 +2439,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
probe.expectMsg(txA1.txId)
// we modify remote's input in previous txs, it won't be double spent
val fakeTxB2 = txB1.modify(_.tx.remoteInputs.at(0).outPoint.hash).setTo(randomBytes32())
val fakeTxB2 = txB1.modify(_.tx.remoteInputs.at(0).outPoint.hash).setTo(TxHash(randomBytes32()))
val aliceRbf = fixtureParams.spawnTxBuilderRbfAlice(aliceParams.copy(targetFeerate = FeeratePerKw(10_000 sat)), commitmentA1, Seq(txA1), walletA)
val bobRbf = fixtureParams.spawnTxBuilderRbfBob(bobParams.copy(targetFeerate = FeeratePerKw(10_000 sat)), commitmentB1, Seq(txB1, fakeTxB2), walletB)

View file

@ -20,7 +20,7 @@ import akka.actor.typed.ActorRef
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter}
import akka.pattern.pipe
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong, Transaction}
import fr.acinq.bitcoin.scalacompat.{SatoshiLong, Transaction, TxId}
import fr.acinq.eclair.blockchain.CurrentBlockHeight
import fr.acinq.eclair.blockchain.WatcherSpec.createSpendP2WPKH
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
@ -63,7 +63,7 @@ class FinalTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
probe.expectMsgType[Seq[Transaction]]
}
def waitTxInMempool(bitcoinClient: BitcoinCoreClient, txId: ByteVector32, probe: TestProbe): Unit = {
def waitTxInMempool(bitcoinClient: BitcoinCoreClient, txId: TxId, probe: TestProbe): Unit = {
awaitCond(getMempool(bitcoinClient, probe).exists(_.txid == txId))
}

View file

@ -21,7 +21,8 @@ import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, actorRefAdapter
import akka.pattern.pipe
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, SatoshiLong, Transaction, TxIn}
import fr.acinq.bitcoin.scalacompat.{OutPoint, SatoshiLong, Transaction, TxId, TxIn}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.WatcherSpec.{createSpendManyP2WPKH, createSpendP2WPKH}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
@ -29,7 +30,7 @@ import fr.acinq.eclair.channel.publish.MempoolTxMonitor._
import fr.acinq.eclair.channel.publish.TxPublisher.TxPublishContext
import fr.acinq.eclair.channel.publish.TxPublisher.TxRejectedReason._
import fr.acinq.eclair.channel.{TransactionConfirmed, TransactionPublished}
import fr.acinq.eclair.{TestConstants, TestKitBaseClass, randomBytes32, randomKey}
import fr.acinq.eclair.{TestConstants, TestKitBaseClass, randomKey}
import org.scalatest.BeforeAndAfterAll
import org.scalatest.funsuite.AnyFunSuiteLike
@ -69,7 +70,7 @@ class MempoolTxMonitorSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
probe.expectMsgType[Seq[Transaction]]
}
def waitTxInMempool(bitcoinClient: BitcoinCoreClient, txId: ByteVector32, probe: TestProbe): Unit = {
def waitTxInMempool(bitcoinClient: BitcoinCoreClient, txId: TxId, probe: TestProbe): Unit = {
awaitCond(getMempool(bitcoinClient, probe).exists(_.txid == txId))
}
@ -165,7 +166,7 @@ class MempoolTxMonitorSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
import f._
val tx = createSpendP2WPKH(parentTx, priv, priv.publicKey, 5_000 sat, 0, 0)
val txUnknownInput = tx.copy(txIn = tx.txIn ++ Seq(TxIn(OutPoint(randomBytes32(), 13), Nil, 0)))
val txUnknownInput = tx.copy(txIn = tx.txIn ++ Seq(TxIn(OutPoint(randomTxId(), 13), Nil, 0)))
monitor ! Publish(probe.ref, txUnknownInput, txUnknownInput.txIn.head.outPoint, "test-tx", 10 sat)
probe.expectMsg(TxRejected(txUnknownInput.txid, InputGone))
}
@ -178,7 +179,7 @@ class MempoolTxMonitorSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
generateBlocks(1)
val tx = createSpendP2WPKH(parentTx, priv, priv.publicKey, 5_000 sat, 0, 0)
val txUnknownInput = tx.copy(txIn = tx.txIn ++ Seq(TxIn(OutPoint(randomBytes32(), 13), Nil, 0)))
val txUnknownInput = tx.copy(txIn = tx.txIn ++ Seq(TxIn(OutPoint(randomTxId(), 13), Nil, 0)))
monitor ! Publish(probe.ref, txUnknownInput, txUnknownInput.txIn.head.outPoint, "test-tx", 10 sat)
probe.expectMsg(TxRejected(txUnknownInput.txid, InputGone))
}

View file

@ -16,7 +16,8 @@
package fr.acinq.eclair.channel.publish
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, OutPoint, SatoshiLong, Script, Transaction, TxIn, TxOut}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Crypto, OutPoint, SatoshiLong, Script, Transaction, TxId, TxIn, TxOut}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
import fr.acinq.eclair.channel.Helpers.Funding
import fr.acinq.eclair.channel.publish.ReplaceableTxFunder.AdjustPreviousTxOutputResult.{AddWalletInputs, TxOutputAdjusted}
@ -38,7 +39,7 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
private def createAnchorTx(): (CommitTx, ClaimLocalAnchorOutputTx) = {
val anchorScript = Scripts.anchor(PlaceHolderPubKey)
val commitInput = Funding.makeFundingInputInfo(randomBytes32(), 1, 500 sat, PlaceHolderPubKey, PlaceHolderPubKey)
val commitInput = Funding.makeFundingInputInfo(randomTxId(), 1, 500 sat, PlaceHolderPubKey, PlaceHolderPubKey)
val commitTx = Transaction(
2,
Seq(TxIn(commitInput.outPoint, commitInput.redeemScript, 0, Scripts.witness2of2(PlaceHolderSig, PlaceHolderSig, PlaceHolderPubKey, PlaceHolderPubKey))),
@ -60,7 +61,7 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
val htlcTimeoutScript = Scripts.htlcOffered(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, randomBytes32(), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
val commitTx = Transaction(
2,
Seq(TxIn(OutPoint(randomBytes32(), 1), Script.write(Script.pay2wpkh(PlaceHolderPubKey)), 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))),
Seq(TxIn(OutPoint(randomTxId(), 1), Script.write(Script.pay2wpkh(PlaceHolderPubKey)), 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))),
Seq(TxOut(5000 sat, Script.pay2wsh(htlcSuccessScript)), TxOut(4000 sat, Script.pay2wsh(htlcTimeoutScript))),
0
)
@ -86,15 +87,15 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
val htlcSuccessScript = Scripts.htlcReceived(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, paymentHash, CltvExpiry(0), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
val htlcTimeoutScript = Scripts.htlcOffered(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, randomBytes32(), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
val claimHtlcSuccess = ClaimHtlcSuccessWithWitnessData(ClaimHtlcSuccessTx(
InputInfo(OutPoint(ByteVector32.Zeroes, 3), TxOut(5000 sat, Script.pay2wsh(htlcSuccessScript)), htlcSuccessScript),
Transaction(2, Seq(TxIn(OutPoint(ByteVector32.Zeroes, 3), ByteVector.empty, 0)), Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey))), 0),
InputInfo(OutPoint(TxId(ByteVector32.Zeroes), 3), TxOut(5000 sat, Script.pay2wsh(htlcSuccessScript)), htlcSuccessScript),
Transaction(2, Seq(TxIn(OutPoint(TxId(ByteVector32.Zeroes), 3), ByteVector.empty, 0)), Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey))), 0),
paymentHash,
5,
ConfirmationTarget.Absolute(BlockHeight(0))
), preimage)
val claimHtlcTimeout = ClaimHtlcTimeoutWithWitnessData(ClaimHtlcTimeoutTx(
InputInfo(OutPoint(ByteVector32.Zeroes, 7), TxOut(5000 sat, Script.pay2wsh(htlcTimeoutScript)), htlcTimeoutScript),
Transaction(2, Seq(TxIn(OutPoint(ByteVector32.Zeroes, 7), ByteVector.empty, 0)), Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey))), 0),
InputInfo(OutPoint(TxId(ByteVector32.Zeroes), 7), TxOut(5000 sat, Script.pay2wsh(htlcTimeoutScript)), htlcTimeoutScript),
Transaction(2, Seq(TxIn(OutPoint(TxId(ByteVector32.Zeroes), 7), ByteVector.empty, 0)), Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey))), 0),
7,
ConfirmationTarget.Absolute(BlockHeight(0))
))
@ -133,8 +134,8 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
txIn = Seq(
initialAnchorTx.tx.txIn.head,
// The previous funding attempt added two wallet inputs:
TxIn(OutPoint(randomBytes32(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
TxIn(OutPoint(randomBytes32(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
TxIn(OutPoint(randomTxId(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
TxIn(OutPoint(randomTxId(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
),
// And a change output:
txOut = Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey)))
@ -173,9 +174,9 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
txIn = Seq(
initialHtlcTx.txInfo.tx.txIn.head,
// The previous funding attempt added three wallet inputs:
TxIn(OutPoint(randomBytes32(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
TxIn(OutPoint(randomBytes32(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
TxIn(OutPoint(randomBytes32(), 5), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
TxIn(OutPoint(randomTxId(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
TxIn(OutPoint(randomTxId(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
TxIn(OutPoint(randomTxId(), 5), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
),
txOut = Seq(
initialHtlcTx.txInfo.tx.txOut.head,

View file

@ -22,7 +22,7 @@ import akka.pattern.pipe
import akka.testkit.{TestFSMRef, TestProbe}
import com.softwaremill.quicklens.ModifyPimp
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, BtcAmount, ByteVector32, MilliBtcDouble, MnemonicCode, OutPoint, SatoshiLong, Transaction}
import fr.acinq.bitcoin.scalacompat.{Block, BtcAmount, MilliBtcDouble, MnemonicCode, OutPoint, SatoshiLong, Transaction, TxId}
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
@ -114,7 +114,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
})
}
def isInMempool(txid: ByteVector32): Boolean = {
def isInMempool(txid: TxId): Boolean = {
getMempool().exists(_.txid == txid)
}

View file

@ -21,6 +21,7 @@ import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter}
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.{OutPoint, SatoshiLong, Transaction, TxIn, TxOut}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.CurrentBlockHeight
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget}
import fr.acinq.eclair.channel.publish
@ -72,7 +73,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publish final tx") { f =>
import f._
val tx = Transaction(2, TxIn(OutPoint(randomBytes32(), 1), Nil, 0) :: Nil, Nil, 0)
val tx = Transaction(2, TxIn(OutPoint(randomTxId(), 1), Nil, 0) :: Nil, Nil, 0)
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
txPublisher ! cmd
val child = factory.expectMsgType[FinalTxPublisherSpawned].actor
@ -82,7 +83,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publish final tx duplicate") { f =>
import f._
val input = OutPoint(randomBytes32(), 1)
val input = OutPoint(randomTxId(), 1)
val tx1 = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
val cmd1 = PublishFinalTx(tx1, input, "final-tx", 10 sat, None)
txPublisher ! cmd1
@ -93,7 +94,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
factory.expectNoMessage(100 millis)
// But a different tx spending the same main input is allowed:
val tx2 = tx1.copy(txIn = tx1.txIn ++ Seq(TxIn(OutPoint(randomBytes32(), 0), Nil, 0)))
val tx2 = tx1.copy(txIn = tx1.txIn ++ Seq(TxIn(OutPoint(randomTxId(), 0), Nil, 0)))
val cmd2 = PublishFinalTx(tx2, input, "another-final-tx", 0 sat, None)
txPublisher ! cmd2
factory.expectMsgType[FinalTxPublisherSpawned]
@ -103,7 +104,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
import f._
val confirmBefore = ConfirmationTarget.Absolute(nodeParams.currentBlockHeight + 12)
val input = OutPoint(randomBytes32(), 3)
val input = OutPoint(randomTxId(), 3)
val cmd = PublishReplaceableTx(ClaimLocalAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), confirmBefore), null)
txPublisher ! cmd
val child = factory.expectMsgType[ReplaceableTxPublisherSpawned].actor
@ -115,7 +116,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
import f._
val confirmBefore = nodeParams.currentBlockHeight + 12
val input = OutPoint(randomBytes32(), 3)
val input = OutPoint(randomTxId(), 3)
val anchorTx = ClaimLocalAnchorOutputTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), ConfirmationTarget.Priority(ConfirmationPriority.Medium))
val cmd = PublishReplaceableTx(anchorTx, null)
txPublisher ! cmd
@ -161,14 +162,14 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("stop publishing attempts when transaction confirms") { f =>
import f._
val input = OutPoint(randomBytes32(), 3)
val input = OutPoint(randomTxId(), 3)
val tx1 = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
val cmd1 = PublishFinalTx(tx1, input, "final-tx-1", 5 sat, None)
txPublisher ! cmd1
val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned].actor
attempt1.expectMsgType[FinalTxPublisher.Publish]
val tx2 = Transaction(2, TxIn(input, Nil, 0) :: TxIn(OutPoint(randomBytes32(), 0), Nil, 3) :: Nil, Nil, 0)
val tx2 = Transaction(2, TxIn(input, Nil, 0) :: TxIn(OutPoint(randomTxId(), 0), Nil, 3) :: Nil, Nil, 0)
val cmd2 = PublishFinalTx(tx2, input, "final-tx-2", 15 sat, None)
txPublisher ! cmd2
val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned].actor
@ -189,7 +190,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (wallet input gone)") { f =>
import f._
val input = OutPoint(randomBytes32(), 3)
val input = OutPoint(randomTxId(), 3)
val tx1 = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
val cmd1 = PublishFinalTx(tx1, input, "final-tx-1", 0 sat, None)
txPublisher ! cmd1
@ -213,7 +214,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (main input gone)") { f =>
import f._
val input = OutPoint(randomBytes32(), 3)
val input = OutPoint(randomTxId(), 3)
val tx = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
val cmd = PublishFinalTx(tx, input, "final-tx", 0 sat, None)
txPublisher ! cmd
@ -234,7 +235,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
import f._
val target = nodeParams.currentBlockHeight + 12
val input = OutPoint(randomBytes32(), 7)
val input = OutPoint(randomTxId(), 7)
val paymentHash = randomBytes32()
val cmd = PublishReplaceableTx(HtlcSuccessTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), paymentHash, 3, ConfirmationTarget.Absolute(target)), null)
txPublisher ! cmd
@ -254,13 +255,13 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (transaction skipped)") { f =>
import f._
val tx1 = Transaction(2, TxIn(OutPoint(randomBytes32(), 1), Nil, 0) :: Nil, Nil, 0)
val tx1 = Transaction(2, TxIn(OutPoint(randomTxId(), 1), Nil, 0) :: Nil, Nil, 0)
val cmd1 = PublishFinalTx(tx1, tx1.txIn.head.outPoint, "final-tx-1", 0 sat, None)
txPublisher ! cmd1
val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned]
attempt1.actor.expectMsgType[FinalTxPublisher.Publish]
val tx2 = Transaction(2, TxIn(OutPoint(randomBytes32(), 0), Nil, 0) :: Nil, Nil, 0)
val tx2 = Transaction(2, TxIn(OutPoint(randomTxId(), 0), Nil, 0) :: Nil, Nil, 0)
val cmd2 = PublishFinalTx(tx2, tx2.txIn.head.outPoint, "final-tx-2", 5 sat, None)
txPublisher ! cmd2
val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned]
@ -281,7 +282,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (unconfirmed conflicting raw transaction)") { f =>
import f._
val tx = Transaction(2, TxIn(OutPoint(randomBytes32(), 1), Nil, 0) :: Nil, Nil, 0)
val tx = Transaction(2, TxIn(OutPoint(randomTxId(), 1), Nil, 0) :: Nil, Nil, 0)
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
txPublisher ! cmd
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
@ -298,7 +299,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (unconfirmed conflicting replaceable transaction)") { f =>
import f._
val input = OutPoint(randomBytes32(), 7)
val input = OutPoint(randomTxId(), 7)
val paymentHash = randomBytes32()
val cmd = PublishReplaceableTx(HtlcSuccessTx(InputInfo(input, TxOut(25_000 sat, Nil), Nil), Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0), paymentHash, 3, ConfirmationTarget.Absolute(nodeParams.currentBlockHeight)), null)
txPublisher ! cmd
@ -318,7 +319,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (confirmed conflicting transaction)") { f =>
import f._
val tx = Transaction(2, TxIn(OutPoint(randomBytes32(), 1), Nil, 0) :: Nil, Nil, 0)
val tx = Transaction(2, TxIn(OutPoint(randomTxId(), 1), Nil, 0) :: Nil, Nil, 0)
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
txPublisher ! cmd
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
@ -335,7 +336,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
test("publishing attempt fails (unknown failure)") { f =>
import f._
val tx = Transaction(2, TxIn(OutPoint(randomBytes32(), 1), Nil, 0) :: Nil, Nil, 0)
val tx = Transaction(2, TxIn(OutPoint(randomTxId(), 1), Nil, 0) :: Nil, Nil, 0)
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
txPublisher ! cmd
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.channel.states.a
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32, SatoshiLong}
import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.channel.fsm.Channel
@ -120,7 +120,7 @@ class WaitForOpenDualFundedChannelStateSpec extends TestKitBaseClass with Fixtur
test("recv OpenDualFundedChannel (invalid chain)", Tag(ChannelStateTestsTags.DualFunding), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
import f._
val open = alice2bob.expectMsgType[OpenDualFundedChannel]
val chain = randomBytes32()
val chain = BlockHash(randomBytes32())
bob ! open.copy(chainHash = chain)
val error = bob2alice.expectMsgType[Error]
assert(error == Error(open.temporaryChannelId, InvalidChainHash(open.temporaryChannelId, Block.RegtestGenesisBlock.hash, chain).getMessage))

View file

@ -18,7 +18,8 @@ package fr.acinq.eclair.channel.states.b
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, SatoshiLong, TxId}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.SingleKeyOnChainWallet
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchPublished}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
@ -29,7 +30,7 @@ import fr.acinq.eclair.channel.publish.TxPublisher
import fr.acinq.eclair.channel.states.{ChannelStateTestsBase, ChannelStateTestsTags}
import fr.acinq.eclair.io.Peer.OpenChannelResponse
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{Features, MilliSatoshiLong, TestConstants, TestKitBaseClass, ToMilliSatoshiConversion, randomBytes32}
import fr.acinq.eclair.{Features, MilliSatoshiLong, TestConstants, TestKitBaseClass, ToMilliSatoshiConversion}
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
import org.scalatest.{Outcome, Tag}
@ -219,14 +220,14 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
val bobSigs = bob2alice.expectMsgType[TxSignatures]
bob2blockchain.expectMsgType[WatchFundingConfirmed]
bob2alice.forward(alice, bobSigs.copy(txHash = randomBytes32(), witnesses = Nil))
bob2alice.forward(alice, bobSigs.copy(txId = randomTxId(), witnesses = Nil))
alice2bob.expectMsgType[Error]
awaitCond(wallet.rolledback.size == 1)
aliceListener.expectMsgType[ChannelAborted]
awaitCond(alice.stateName == CLOSED)
// Bob has sent his signatures already, so he cannot close the channel yet.
alice2bob.forward(bob, TxSignatures(channelId(alice), randomBytes32(), Nil))
alice2bob.forward(bob, TxSignatures(channelId(alice), randomTxId(), Nil))
bob2alice.expectMsgType[Error]
bob2blockchain.expectNoMessage(100 millis)
assert(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
@ -390,7 +391,7 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
assert(listener.expectMsgType[TransactionPublished].tx.txid == fundingTxId)
}
private def reconnect(f: FixtureParam, fundingTxId: ByteVector32): Unit = {
private def reconnect(f: FixtureParam, fundingTxId: TxId): Unit = {
import f._
val listener = TestProbe()

View file

@ -653,10 +653,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
// splice 1 confirms
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx1.txid)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx1.txid)
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx1.txid)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx1.txid)
bob2alice.forward(alice)
alice2blockchain.expectWatchFundingSpent(fundingTx1.txid, Some(Set(fundingTx2.txid, commitAlice1.txid, commitBob1.txid)))
bob2blockchain.expectWatchFundingSpent(fundingTx1.txid, Some(Set(fundingTx2.txid, commitAlice1.txid, commitBob1.txid)))
@ -665,10 +665,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
// splice 2 confirms
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
bob2alice.forward(alice)
alice2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
bob2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
@ -689,9 +689,9 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
bob2blockchain.expectWatchFundingConfirmed(fundingTx1.txid)
bob2blockchain.expectNoMessage(100 millis)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
alice2bob.forward(bob)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
bob2alice.forward(alice)
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
@ -709,10 +709,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
// splice 2 confirms
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
bob2alice.forward(alice)
alice2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
bob2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
@ -737,10 +737,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
// splice 1 confirms on alice, splice 2 confirms on bob
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx1.txid)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx1.txid)
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
bob2alice.forward(alice)
alice2blockchain.expectWatchFundingSpent(fundingTx1.txid)
bob2blockchain.expectWatchFundingSpent(fundingTx2.txid)
@ -751,7 +751,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
// splice 2 confirms on bob, splice 1 confirms on alice
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
bob2alice.expectNoMessage(100 millis)
@ -1236,7 +1236,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice)
val spliceTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localFundingStatus.signedTx_opt.get
alice ! WatchPublishedTriggered(spliceTx)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == spliceTxId) // Bob doesn't receive Alice's splice_locked
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == spliceTxId) // Bob doesn't receive Alice's splice_locked
disconnect(f)
val (channelReestablishAlice, channelReestablishBob) = reconnect(f, interceptFundingDeeplyBuried = false)
@ -1248,7 +1248,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
alice2bob.expectMsgType[TxSignatures]
alice2bob.forward(bob)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == spliceTx.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == spliceTx.txid)
alice2bob.forward(bob)
bob2alice.expectNoMessage(100 millis)
bob ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
@ -1343,58 +1343,58 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
// splice 1 confirms on alice's side
watchConfirmed1a.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
alice2bob.forward(bob)
alice2blockchain.expectMsgType[WatchFundingSpent]
disconnect(f)
reconnect(f)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
alice2bob.forward(bob)
// splice 2 confirms on alice's side
watchConfirmed2a.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
alice2blockchain.expectMsgType[WatchFundingSpent]
disconnect(f)
reconnect(f)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
alice2bob.expectNoMessage(100 millis)
bob2alice.expectNoMessage(100 millis)
// splice 1 confirms on bob's side
watchConfirmed1b.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
bob2alice.forward(alice)
bob2blockchain.expectMsgType[WatchFundingSpent]
disconnect(f)
reconnect(f)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
bob2alice.forward(alice)
alice2bob.expectNoMessage(100 millis)
bob2alice.expectNoMessage(100 millis)
// splice 2 confirms on bob's side
watchConfirmed2b.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
bob2blockchain.expectMsgType[WatchFundingSpent]
// NB: we disconnect *before* transmitting the splice_confirmed to alice
disconnect(f)
reconnect(f)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
// this time alice received the splice_confirmed for funding tx 2
bob2alice.forward(alice)
alice2bob.expectNoMessage(100 millis)
@ -1403,9 +1403,9 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
disconnect(f)
reconnect(f)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
alice2bob.forward(bob)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
bob2alice.forward(alice)
alice2bob.expectNoMessage(100 millis)
bob2alice.expectNoMessage(100 millis)

View file

@ -3476,7 +3476,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
alice2blockchain.expectMsgType[WatchOutputSpent], // htlc 3
alice2blockchain.expectMsgType[WatchOutputSpent], // htlc 4
alice2blockchain.expectMsgType[WatchOutputSpent], // local anchor
).map(w => OutPoint(w.txId.reverse, w.outputIndex)).toSet
).map(w => OutPoint(w.txId, w.outputIndex)).toSet
val localCommitPublished = alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get
assert(watchedOutputs == localCommitPublished.htlcTxs.keySet + localAnchor.txInfo.input.outPoint)
alice2blockchain.expectNoMessage(1 second)

View file

@ -21,6 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.ScriptFlags
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, SatoshiLong, Script, Transaction, TxIn, TxOut}
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget, FeeratePerKw, FeeratesPerKw}
import fr.acinq.eclair.channel._
@ -380,14 +381,14 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
channelUpdateListener.expectMsgType[LocalChannelDown]
// scenario 1: bob claims the htlc output from the commit tx using its preimage
val claimHtlcSuccessFromCommitTx = Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(randomBytes32(), 0), signatureScript = ByteVector.empty, sequence = 0, witness = Scripts.witnessClaimHtlcSuccessFromCommitTx(Transactions.PlaceHolderSig, ra1, ByteVector.fill(130)(33))) :: Nil, txOut = Nil, lockTime = 0)
val claimHtlcSuccessFromCommitTx = Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(randomTxId(), 0), signatureScript = ByteVector.empty, sequence = 0, witness = Scripts.witnessClaimHtlcSuccessFromCommitTx(Transactions.PlaceHolderSig, ra1, ByteVector.fill(130)(33))) :: Nil, txOut = Nil, lockTime = 0)
alice ! WatchOutputSpentTriggered(claimHtlcSuccessFromCommitTx)
val fulfill1 = alice2relayer.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]]
assert(fulfill1.htlc == htlca1)
assert(fulfill1.result.paymentPreimage == ra1)
// scenario 2: bob claims the htlc output from his own commit tx using its preimage (let's assume both parties had published their commitment tx)
val claimHtlcSuccessTx = Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(randomBytes32(), 0), signatureScript = ByteVector.empty, sequence = 0, witness = Scripts.witnessHtlcSuccess(Transactions.PlaceHolderSig, Transactions.PlaceHolderSig, ra1, ByteVector.fill(130)(33), Transactions.DefaultCommitmentFormat)) :: Nil, txOut = Nil, lockTime = 0)
val claimHtlcSuccessTx = Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(randomTxId(), 0), signatureScript = ByteVector.empty, sequence = 0, witness = Scripts.witnessHtlcSuccess(Transactions.PlaceHolderSig, Transactions.PlaceHolderSig, ra1, ByteVector.fill(130)(33), Transactions.DefaultCommitmentFormat)) :: Nil, txOut = Nil, lockTime = 0)
alice ! WatchOutputSpentTriggered(claimHtlcSuccessTx)
val fulfill2 = alice2relayer.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]]
assert(fulfill2.htlc == htlca1)
@ -1584,7 +1585,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
alice2blockchain.expectNoMessage(1 second)
// bob RBFs his htlc-success with a different transaction
val bobHtlcSuccessTx2 = bobHtlcSuccessTx1.tx.copy(txIn = TxIn(OutPoint(randomBytes32(), 0), Nil, 0) +: bobHtlcSuccessTx1.tx.txIn)
val bobHtlcSuccessTx2 = bobHtlcSuccessTx1.tx.copy(txIn = TxIn(OutPoint(randomTxId(), 0), Nil, 0) +: bobHtlcSuccessTx1.tx.txIn)
assert(bobHtlcSuccessTx2.txid !== bobHtlcSuccessTx1.tx.txid)
alice ! WatchOutputSpentTriggered(bobHtlcSuccessTx2)
awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.size == 3)
@ -1670,7 +1671,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
val bobHtlcTx = Transaction(
2,
Seq(
TxIn(OutPoint(randomBytes32(), 4), Nil, 1), // utxo used for fee bumping
TxIn(OutPoint(randomTxId(), 4), Nil, 1), // utxo used for fee bumping
bobHtlcTxs(0).tx.txIn.head,
bobHtlcTxs(1).tx.txIn.head,
bobHtlcTxs(2).tx.txIn.head,
@ -1706,7 +1707,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
alice2blockchain.expectMsgType[WatchOutputSpent],
alice2blockchain.expectMsgType[WatchOutputSpent],
alice2blockchain.expectMsgType[WatchOutputSpent]
).map(w => OutPoint(w.txId.reverse, w.outputIndex)).toSet
).map(w => OutPoint(w.txId, w.outputIndex)).toSet
assert(watchedOutpoints == spentOutpoints)
alice2blockchain.expectNoMessage(1 second)
}

View file

@ -21,7 +21,7 @@ class LocalOnChainKeyManagerSpec extends AnyFunSuite {
Base64.getDecoder.decode("cHNidP8BAHECAAAAAfZo4nGIyTg77MFmEBkQH1Au3Jl8vzB2WWQGGz/MbyssAAAAAAD9////ArAHPgUAAAAAFgAU6j9yVvLg66Zu3GM/xHbmXT0yvyiAlpgAAAAAABYAFODscQh3N7lmDYyV5yrHpGL2Zd4JAAAAAAABAH0CAAAAAaNdmqUNlziIjSaif3JUcvJWdyF0U5bYq13NMe+LbaBZAAAAAAD9////AjSp1gUAAAAAFgAUjfFMfBg8ulo/874n3+0ode7ka0BAQg8AAAAAACIAIPUn/XU17DfnvDkj8gn2twG3jtr2Z7sthy9K2MPTdYkaAAAAAAEBHzSp1gUAAAAAFgAUjfFMfBg8ulo/874n3+0ode7ka0AiBgM+PDdyxsVisa66SyBxiUvhEam8lEP64yujvVsEcGaqIxgPCfOBVAAAgAEAAIAAAACAAQAAAAMAAAAAIgIDWmAhb/sCV9+HjwFpPuy2TyEBi/Y11wrEHZUihe3N80EYDwnzgVQAAIABAACAAAAAgAEAAAAFAAAAAAA=")
).getRight
val Success(psbt1) = onChainKeyManager.sign(psbt, psbt.getInputs.toArray().indices, Seq(0))
val Success(psbt1) = onChainKeyManager.sign(psbt, psbt.inputs.toArray().indices, Seq(0))
val tx = psbt1.extract()
assert(tx.isRight)
}

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.db
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong, Script, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, SatoshiLong, Script, Transaction, TxOut}
import fr.acinq.eclair.TestDatabases.{TestPgDatabases, TestSqliteDatabases, migrationCheck}
import fr.acinq.eclair._
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
@ -865,7 +865,7 @@ class AuditDbSpec extends AnyFunSuite {
val channelId = randomBytes32()
val scid = ShortChannelId(123)
val remoteNodeId = randomKey().publicKey
val u = Announcements.makeChannelUpdate(randomBytes32(), randomKey(), remoteNodeId, scid, CltvExpiryDelta(56), 2000 msat, 1000 msat, 999, 1000000000 msat)
val u = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey(), remoteNodeId, scid, CltvExpiryDelta(56), 2000 msat, 1000 msat, 999, 1000000000 msat)
dbs.audit.addChannelUpdate(ChannelUpdateParametersChanged(null, channelId, remoteNodeId, u))
}
}

View file

@ -17,10 +17,11 @@
package fr.acinq.eclair.db
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, Satoshi, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Crypto, Satoshi, SatoshiLong, TxId}
import fr.acinq.eclair.FeatureSupport.Optional
import fr.acinq.eclair.Features.VariableLengthOnion
import fr.acinq.eclair.TestDatabases._
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.db.jdbc.JdbcUtils.using
import fr.acinq.eclair.db.pg.PgNetworkDb
import fr.acinq.eclair.db.sqlite.SqliteNetworkDb
@ -86,7 +87,7 @@ class NetworkDbSpec extends AnyFunSuite {
val db = dbs.network
val sig = ByteVector64.Zeroes
val c = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, RealShortChannelId(42), randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, sig, sig, sig, sig)
val txid = ByteVector32.fromValidHex("0001" * 16)
val txid = TxId.fromValidHex("0001" * 16)
db.addChannel(c, txid, Satoshi(42))
assert(db.listChannels() == SortedMap(c.shortChannelId -> PublicChannel(c, txid, Satoshi(42), None, None, None)))
}
@ -112,9 +113,9 @@ class NetworkDbSpec extends AnyFunSuite {
val channel_2 = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, RealShortChannelId(43), a.publicKey, c.publicKey, randomKey().publicKey, randomKey().publicKey, sig, sig, sig, sig)
val channel_3 = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, RealShortChannelId(44), b.publicKey, c.publicKey, randomKey().publicKey, randomKey().publicKey, sig, sig, sig, sig)
val txid_1 = randomBytes32()
val txid_2 = randomBytes32()
val txid_3 = randomBytes32()
val txid_1 = randomTxId()
val txid_2 = randomTxId()
val txid_3 = randomTxId()
val capacity = 10000 sat
assert(db.listChannels().toSet == Set.empty)
@ -137,9 +138,9 @@ class NetworkDbSpec extends AnyFunSuite {
channel_3.shortChannelId -> PublicChannel(channel_3, txid_3, capacity, None, None, None))
)
val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, a, b.publicKey, ShortChannelId(42), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, true)
val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, b, a.publicKey, ShortChannelId(42), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, true)
val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, b, c.publicKey, ShortChannelId(44), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, true)
val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, a, b.publicKey, ShortChannelId(42), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, isPrivate = true)
val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, b, a.publicKey, ShortChannelId(42), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, isPrivate = true)
val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, b, c.publicKey, ShortChannelId(44), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, isPrivate = true)
db.updateChannel(channel_update_1)
db.updateChannel(channel_update_1) // duplicate is ignored
@ -214,9 +215,9 @@ class NetworkDbSpec extends AnyFunSuite {
val capacity = 10000 sat
val channels = shortChannelIds.map(id => Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, id, pub, pub, pub, pub, sig, sig, sig, sig))
val template = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv, pub, ShortChannelId(42), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, true)
val template = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv, pub, ShortChannelId(42), CltvExpiryDelta(5), 7000000 msat, 50000 msat, 100, 500000000L msat, isPrivate = true)
val updates = shortChannelIds.map(id => template.copy(shortChannelId = id))
val txid = randomBytes32()
val txid = randomTxId()
channels.foreach(ca => db.addChannel(ca, txid, capacity))
updates.foreach(u => db.updateChannel(u))
assert(db.listChannels().keySet == channels.map(_.shortChannelId).toSet)
@ -391,7 +392,7 @@ object NetworkDbSpec {
data: Array[Byte])
case class ChannelTestCase(shortChannelId: ShortChannelId,
txid: ByteVector32,
txid: TxId,
channel: ChannelAnnouncement,
channel_data: Array[Byte],
capacity: Satoshi,
@ -425,7 +426,7 @@ object NetworkDbSpec {
val channel_update_2_data = channel_update_2_opt.map(channelUpdateCodec.encode(_).require.toByteArray)
ChannelTestCase(
shortChannelId = channel.shortChannelId,
txid = randomBytes32(),
txid = randomTxId(),
channel = channel,
channel_data = channel_data,
capacity = Random.nextInt(100_000).sat,

View file

@ -793,7 +793,7 @@ class PaymentsDbSpec extends AnyFunSuite {
object PaymentsDbSpec {
val (alicePriv, bobPriv, carolPriv, davePriv) = (randomKey(), randomKey(), randomKey(), randomKey())
val (alice, bob, carol, dave) = (alicePriv.publicKey, bobPriv.publicKey, carolPriv.publicKey, davePriv.publicKey)
val hop_ab = ChannelHop(ShortChannelId(42), alice, bob, HopRelayParams.FromAnnouncement(ChannelUpdate(randomBytes64(), randomBytes32(), ShortChannelId(42), 1 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(12), 1 msat, 1 msat, 1, 500_000_000 msat)))
val hop_ab = ChannelHop(ShortChannelId(42), alice, bob, HopRelayParams.FromAnnouncement(ChannelUpdate(randomBytes64(), Block.RegtestGenesisBlock.hash, ShortChannelId(42), 1 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(12), 1 msat, 1 msat, 1, 500_000_000 msat)))
val hop_bc = NodeHop(bob, carol, CltvExpiryDelta(14), 1 msat)
val (preimage1, preimage2, preimage3, preimage4) = (randomBytes32(), randomBytes32(), randomBytes32(), randomBytes32())
val (paymentHash1, paymentHash2, paymentHash3, paymentHash4) = (Crypto.sha256(preimage1), Crypto.sha256(preimage2), Crypto.sha256(preimage3), Crypto.sha256(preimage4))

View file

@ -23,7 +23,7 @@ import akka.testkit.TestProbe
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin.ScriptFlags
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, BtcDouble, ByteVector32, Crypto, OutPoint, SatoshiLong, Script, Transaction, computeBIP84Address}
import fr.acinq.bitcoin.scalacompat.{Block, BtcDouble, ByteVector32, Crypto, OutPoint, SatoshiLong, Script, Transaction, TxId, computeBIP84Address}
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.BitcoinReq
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
import fr.acinq.eclair.channel._
@ -70,7 +70,7 @@ abstract class ChannelIntegrationSpec extends IntegrationSpec {
}
/** Wait for the given transaction to be either in the mempool or confirmed. */
def waitForTxBroadcastOrConfirmed(txid: ByteVector32, bitcoinClient: BitcoinCoreClient, sender: TestProbe): Unit = {
def waitForTxBroadcastOrConfirmed(txid: TxId, bitcoinClient: BitcoinCoreClient, sender: TestProbe): Unit = {
awaitCond({
bitcoinClient.getMempool().pipeTo(sender.ref)
val inMempool = sender.expectMsgType[Seq[Transaction]].exists(_.txid == txid)

View file

@ -8,7 +8,7 @@ import akka.testkit.{TestActor, TestProbe}
import com.softwaremill.quicklens.ModifyPimp
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Satoshi, SatoshiLong, Transaction}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Satoshi, SatoshiLong, Transaction, TxId}
import fr.acinq.eclair.ShortChannelId.txIndex
import fr.acinq.eclair.blockchain.DummyOnChainWallet
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
@ -273,9 +273,9 @@ object MinimalNodeFixture extends Assertions with Eventually with IntegrationPat
* Computes a deterministic [[RealShortChannelId]] based on a txid. We need this so that watchers can verify
* transactions in a independent and stateless fashion, since there is no actual blockchain in those tests.
*/
def deterministicShortId(txId: ByteVector32): RealShortChannelId = {
val blockHeight = txId.take(3).toInt(signed = false)
val txIndex = txId.takeRight(2).toInt(signed = false)
def deterministicShortId(txId: TxId): RealShortChannelId = {
val blockHeight = txId.value.take(3).toInt(signed = false)
val txIndex = txId.value.takeRight(2).toInt(signed = false)
val outputIndex = 0 // funding txs created by the dummy wallet used in tests only have one output
RealShortChannelId(BlockHeight(blockHeight), txIndex, outputIndex)
}

View file

@ -20,7 +20,7 @@ import akka.actor.testkit.typed.scaladsl.{ScalaTestWithActorTestKit, TestProbe}
import akka.actor.typed.eventstream.EventStream.Publish
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, SatoshiLong, Transaction, TxId, TxOut}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.io.PendingChannelsRateLimiter.filterPendingChannels
import fr.acinq.eclair.router.Router
@ -74,11 +74,11 @@ class PendingChannelsRateLimiterSpec extends ScalaTestWithActorTestKit(ConfigFac
val tx = Transaction.read("010000000110f01d4a4228ef959681feb1465c2010d0135be88fd598135b2e09d5413bf6f1000000006a473044022074658623424cebdac8290488b76f893cfb17765b7a3805e773e6770b7b17200102202892cfa9dda662d5eac394ba36fcfd1ea6c0b8bb3230ab96220731967bbdb90101210372d437866d9e4ead3d362b01b615d24cc0d5152c740d51e3c55fb53f6d335d82ffffffff01408b0700000000001976a914678db9a7caa2aca887af1177eda6f3d0f702df0d88ac00000000")
val closingTx = ClosingTx(InputInfo(tx.txIn.head.outPoint, TxOut(10_000 sat, Nil), Nil), tx, None)
val channelsOnWhitelistAtLimit: Seq[PersistentChannelData] = Seq(
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerOnWhitelistAtLimit, randomBytes32()), BlockHeight(0), None, Left(FundingCreated(randomBytes32(), ByteVector32.Zeroes, 3, randomBytes64()))),
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerOnWhitelistAtLimit, randomBytes32()), BlockHeight(0), None, Left(FundingCreated(randomBytes32(), TxId(ByteVector32.Zeroes), 3, randomBytes64()))),
DATA_WAIT_FOR_CHANNEL_READY(commitments(peerOnWhitelistAtLimit, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
)
val channelsAtLimit1 = Seq(
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerAtLimit1, channelIdAtLimit1), BlockHeight(0), None, Left(FundingCreated(channelIdAtLimit1, ByteVector32.Zeroes, 3, randomBytes64()))),
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerAtLimit1, channelIdAtLimit1), BlockHeight(0), None, Left(FundingCreated(channelIdAtLimit1, TxId(ByteVector32.Zeroes), 3, randomBytes64()))),
DATA_WAIT_FOR_CHANNEL_READY(commitments(peerAtLimit1, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
)
val channelsAtLimit2 = Seq(
@ -86,7 +86,7 @@ class PendingChannelsRateLimiterSpec extends ScalaTestWithActorTestKit(ConfigFac
DATA_WAIT_FOR_DUAL_FUNDING_READY(commitments(peerAtLimit2, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
)
val channelsBelowLimit1 = Seq(
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerBelowLimit1, channelIdBelowLimit1), BlockHeight(0), None, Left(FundingCreated(channelIdBelowLimit1, ByteVector32.Zeroes, 3, randomBytes64()))),
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerBelowLimit1, channelIdBelowLimit1), BlockHeight(0), None, Left(FundingCreated(channelIdBelowLimit1, TxId(ByteVector32.Zeroes), 3, randomBytes64()))),
)
val channelsBelowLimit2 = Seq(
DATA_WAIT_FOR_DUAL_FUNDING_READY(commitments(peerBelowLimit2, channelIdBelowLimit2), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
@ -99,7 +99,7 @@ class PendingChannelsRateLimiterSpec extends ScalaTestWithActorTestKit(ConfigFac
DATA_NORMAL(commitments(privatePeer2, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None), None, null, None, None, None, SpliceStatus.NoSplice),
)
val initiatorChannels = Seq(
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerBelowLimit1, randomBytes32(), isInitiator = true), BlockHeight(0), None, Left(FundingCreated(channelIdAtLimit1, ByteVector32.Zeroes, 3, randomBytes64()))),
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(peerBelowLimit1, randomBytes32(), isInitiator = true), BlockHeight(0), None, Left(FundingCreated(channelIdAtLimit1, TxId(ByteVector32.Zeroes), 3, randomBytes64()))),
DATA_WAIT_FOR_CHANNEL_READY(commitments(peerBelowLimit1, randomBytes32(), isInitiator = true), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED(commitments(peerAtLimit1, randomBytes32(), isInitiator = true), 0 msat, 0 msat, BlockHeight(0), BlockHeight(0), RbfStatus.NoRbf, None),
DATA_WAIT_FOR_DUAL_FUNDING_READY(commitments(peerAtLimit1, randomBytes32(), isInitiator = true), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
@ -319,7 +319,7 @@ class PendingChannelsRateLimiterSpec extends ScalaTestWithActorTestKit(ConfigFac
DATA_WAIT_FOR_DUAL_FUNDING_READY(commitments(randomKey().publicKey, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
DATA_NORMAL(commitments(randomKey().publicKey, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None), None, null, None, None, None, SpliceStatus.NoSplice),
DATA_SHUTDOWN(commitments(randomKey().publicKey, randomBytes32()), Shutdown(randomBytes32(), ByteVector.empty), Shutdown(randomBytes32(), ByteVector.empty), None),
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(randomKey().publicKey, randomBytes32()), BlockHeight(0), None, Left(FundingCreated(randomBytes32(), ByteVector32.Zeroes, 3, randomBytes64()))),
DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments(randomKey().publicKey, randomBytes32()), BlockHeight(0), None, Left(FundingCreated(randomBytes32(), TxId(ByteVector32.Zeroes), 3, randomBytes64()))),
)
val limiter = testKit.spawn(PendingChannelsRateLimiter(nodeParams, router.ref, channels))

View file

@ -19,7 +19,7 @@ package fr.acinq.eclair.json
import akka.actor.ActorRef
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, Btc, ByteVector32, ByteVector64, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{Block, Btc, ByteVector32, ByteVector64, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Transaction, TxHash, TxId, TxOut}
import fr.acinq.eclair._
import fr.acinq.eclair.balance.CheckBalance
import fr.acinq.eclair.balance.CheckBalance.{ClosingBalance, GlobalBalance, MainAndHtlcBalance, PossiblyPublishedMainAndHtlcBalance, PossiblyPublishedMainBalance}
@ -44,8 +44,8 @@ import java.util.UUID
class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Matchers {
test("deserialize Map[OutPoint, ByteVector]") {
val output1 = OutPoint(ByteVector32(hex"11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
val output2 = OutPoint(ByteVector32(hex"3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
val output1 = OutPoint(TxHash.fromValidHex("11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
val output2 = OutPoint(TxHash.fromValidHex("3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
val map = Map(
output1 -> hex"dead",
output2 -> hex"beef"
@ -63,8 +63,8 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
}
test("deserialize Map[OutPoint, Transaction]") {
val output1 = OutPoint(ByteVector32(hex"11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
val output2 = OutPoint(ByteVector32(hex"3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
val output1 = OutPoint(TxHash.fromValidHex("11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
val output2 = OutPoint(TxHash.fromValidHex("3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
val map = Map(
output1 -> Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800"),
output2 -> Transaction.read("0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a362184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d875069184ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8ad1afdcf652f900000000")
@ -121,9 +121,9 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
val dummyBytes32 = ByteVector32(hex"0202020202020202020202020202020202020202020202020202020202020202")
val localParams = LocalParams(dummyPublicKey, DeterministicWallet.KeyPath(Seq(42L)), 546 sat, Long.MaxValue.msat, Some(1000 sat), 1 msat, CltvExpiryDelta(144), 50, isInitiator = true, None, None, Features.empty)
val remoteParams = RemoteParams(dummyPublicKey, 546 sat, UInt64.MaxValue, Some(1000 sat), 1 msat, CltvExpiryDelta(144), 50, dummyPublicKey, dummyPublicKey, dummyPublicKey, dummyPublicKey, Features.empty, None)
val commitmentInput = Funding.makeFundingInputInfo(dummyBytes32, 0, 150_000 sat, dummyPublicKey, dummyPublicKey)
val commitmentInput = Funding.makeFundingInputInfo(TxId(dummyBytes32), 0, 150_000 sat, dummyPublicKey, dummyPublicKey)
val localCommit = LocalCommit(0, CommitmentSpec(Set.empty, FeeratePerKw(2500 sat), 100_000_000 msat, 50_000_000 msat), CommitTxAndRemoteSig(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), ByteVector64.Zeroes), Nil)
val remoteCommit = RemoteCommit(0, CommitmentSpec(Set.empty, FeeratePerKw(2500 sat), 50_000_000 msat, 100_000_000 msat), dummyBytes32, dummyPublicKey)
val remoteCommit = RemoteCommit(0, CommitmentSpec(Set.empty, FeeratePerKw(2500 sat), 50_000_000 msat, 100_000_000 msat), TxId(dummyBytes32), dummyPublicKey)
val channelInfo = RES_GET_CHANNEL_INFO(
PublicKey(hex"0270685ca81a8e4d4d01beec5781f4cc924684072ae52c507f8ebe9daf0caaab7b"),
ByteVector32(hex"345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"),
@ -276,7 +276,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
test("InputInfo serialization") {
val inputInfo = InputInfo(
outPoint = OutPoint(ByteVector32(hex"345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"), 42),
outPoint = OutPoint(TxHash.fromValidHex("345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"), 42),
txOut = TxOut(456651 sat, hex"3c7a66997c681a3de1bae56438abeee4fc50a16554725a430ade1dc8db6bdd76704d45c6151c4051d710cf487e63"),
redeemScript = hex"00dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773"
)
@ -331,11 +331,11 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
htlcs = Btc(0.05)
), closing = ClosingBalance(
localCloseBalance = PossiblyPublishedMainAndHtlcBalance(
toLocal = Map(ByteVector32(hex"4d176ad844c363bed59edf81962b008faa6194c3b3757ffcd26ba60f95716db2") -> Btc(0.1)),
htlcs = Map(ByteVector32(hex"94b70cec5a98d67d17c6e3de5c7697f8a6cab4f698df91e633ce35efa3574d71") -> Btc(0.03), ByteVector32(hex"a844edd41ce8503864f3c95d89d850b177a09d7d35e950a7d27c14abb63adb13") -> Btc(0.06)),
toLocal = Map(TxId.fromValidHex("4d176ad844c363bed59edf81962b008faa6194c3b3757ffcd26ba60f95716db2") -> Btc(0.1)),
htlcs = Map(TxId.fromValidHex("94b70cec5a98d67d17c6e3de5c7697f8a6cab4f698df91e633ce35efa3574d71") -> Btc(0.03), TxId.fromValidHex("a844edd41ce8503864f3c95d89d850b177a09d7d35e950a7d27c14abb63adb13") -> Btc(0.06)),
htlcsUnpublished = Btc(0.01)),
mutualCloseBalance = PossiblyPublishedMainBalance(
toLocal = Map(ByteVector32(hex"7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247") -> Btc(0.1)))
toLocal = Map(TxId.fromValidHex("7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247") -> Btc(0.1)))
))
)
JsonSerializers.serialization.write(gb)(JsonSerializers.formats) shouldBe """{"total":1.0,"onChain":{"confirmed":0.4,"unconfirmed":0.05},"offChain":{"waitForFundingConfirmed":0.0,"waitForChannelReady":0.0,"normal":{"toLocal":0.2,"htlcs":0.05},"shutdown":{"toLocal":0.0,"htlcs":0.0},"negotiating":0.0,"closing":{"localCloseBalance":{"toLocal":{"4d176ad844c363bed59edf81962b008faa6194c3b3757ffcd26ba60f95716db2":0.1},"htlcs":{"94b70cec5a98d67d17c6e3de5c7697f8a6cab4f698df91e633ce35efa3574d71":0.03,"a844edd41ce8503864f3c95d89d850b177a09d7d35e950a7d27c14abb63adb13":0.06},"htlcsUnpublished":0.01},"remoteCloseBalance":{"toLocal":{},"htlcs":{},"htlcsUnpublished":0.0},"mutualCloseBalance":{"toLocal":{"7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247":0.1}},"unknownCloseBalance":{"toLocal":0.0,"htlcs":0.0}},"waitForPublishFutureCommitment":0.0}}"""
@ -353,13 +353,13 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
test("TransactionWithInputInfo serializer") {
// the input info is ignored when serializing to JSON
val dummyInputInfo = InputInfo(OutPoint(ByteVector32.Zeroes, 0), TxOut(Satoshi(0), Nil), Nil)
val dummyInputInfo = InputInfo(OutPoint(TxId(ByteVector32.Zeroes), 0), TxOut(Satoshi(0), Nil), Nil)
val htlcSuccessTx = Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800")
val htlcSuccessTxInfo = HtlcSuccessTx(dummyInputInfo, htlcSuccessTx, ByteVector32.One, 3, ConfirmationTarget.Absolute(BlockHeight(1105)))
val htlcSuccessJson =
s"""{
| "txid": "${htlcSuccessTx.txid.toHex}",
| "txid": "${htlcSuccessTx.txid.value.toHex}",
| "tx": "0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800",
| "paymentHash": "0100000000000000000000000000000000000000000000000000000000000000",
| "htlcId": 3,
@ -372,7 +372,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
val claimHtlcTimeoutTxInfo = ClaimHtlcTimeoutTx(dummyInputInfo, claimHtlcTimeoutTx, 2, ConfirmationTarget.Absolute(BlockHeight(144)))
val claimHtlcTimeoutJson =
s"""{
| "txid": "${claimHtlcTimeoutTx.txid.toHex}",
| "txid": "${claimHtlcTimeoutTx.txid.value.toHex}",
| "tx": "010000000110f01d4a4228ef959681feb1465c2010d0135be88fd598135b2e09d5413bf6f1000000006a473044022074658623424cebdac8290488b76f893cfb17765b7a3805e773e6770b7b17200102202892cfa9dda662d5eac394ba36fcfd1ea6c0b8bb3230ab96220731967bbdb90101210372d437866d9e4ead3d362b01b615d24cc0d5152c740d51e3c55fb53f6d335d82ffffffff01408b0700000000001976a914678db9a7caa2aca887af1177eda6f3d0f702df0d88ac00000000",
| "htlcId": 2,
| "confirmBeforeBlock": 144
@ -384,7 +384,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
val closingTxWithLocalOutput = ClosingTx(dummyInputInfo, closingTx, Some(OutputInfo(1, Satoshi(15000), hex"deadbeef")))
val closingTxWithLocalOutputJson =
s"""{
| "txid": "${closingTx.txid.toHex}",
| "txid": "${closingTx.txid.value.toHex}",
| "tx": "0100000001be43e9788523ed4de0b24a007a90009bc25e667ddac0e9ee83049be03e220138000000006b483045022100f74dd6ad3e6a00201d266a0ed860a6379c6e68b473970423f3fc8a15caa1ea0f022065b4852c9da230d9e036df743cb743601ca5229e1cb610efdd99769513f2a2260121020636de7755830fb4a3f136e97ecc6c58941611957ba0364f01beae164b945b2fffffffff0150f80c000000000017a9146809053148799a10480eada3d56d15edf4a648c88700000000",
| "toLocalOutput": {
| "index": 1,
@ -398,7 +398,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
val closingTxWithoutLocalOutput = ClosingTx(dummyInputInfo, closingTx, None)
val closingTxWithoutLocalOutputJson =
s"""{
| "txid": "${closingTx.txid.toHex}",
| "txid": "${closingTx.txid.value.toHex}",
| "tx": "0100000001be43e9788523ed4de0b24a007a90009bc25e667ddac0e9ee83049be03e220138000000006b483045022100f74dd6ad3e6a00201d266a0ed860a6379c6e68b473970423f3fc8a15caa1ea0f022065b4852c9da230d9e036df743cb743601ca5229e1cb610efdd99769513f2a2260121020636de7755830fb4a3f136e97ecc6c58941611957ba0364f01beae164b945b2fffffffff0150f80c000000000017a9146809053148799a10480eada3d56d15edf4a648c88700000000"
|}
""".stripMargin

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.payment
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, BtcDouble, ByteVector32, Crypto, MilliBtcDouble, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, BtcDouble, ByteVector32, Crypto, MilliBtcDouble, SatoshiLong}
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
import fr.acinq.eclair.Features.{PaymentMetadata, PaymentSecret, _}
import fr.acinq.eclair.payment.Bolt11Invoice._
@ -43,7 +43,7 @@ class Bolt11InvoiceSpec extends AnyFunSuite {
assert(nodeId == PublicKey(hex"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad"))
// Copy of Bolt11Invoice.apply that doesn't strip unknown features
def createInvoiceUnsafe(chainHash: ByteVector32,
def createInvoiceUnsafe(chainHash: BlockHash,
amount: Option[MilliSatoshi],
paymentHash: ByteVector32,
privateKey: PrivateKey,

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.payment
import fr.acinq.bitcoin.Bech32
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32}
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, ByteVector32}
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
import fr.acinq.eclair.Features.{BasicMultiPartPayment, VariableLengthOnion}
import fr.acinq.eclair.crypto.Sphinx
@ -55,7 +55,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
}
test("check invoice signature") {
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), randomBytes32())
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), BlockHash(randomBytes32()))
val offer = Offer(Some(10000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
val request = InvoiceRequest(offer, 11000 msat, 1, Features.empty, payerKey, chain)
val invoice = Bolt12Invoice(request, randomBytes32(), nodeKey, 300 seconds, Features.empty, Seq(createPaymentBlindedRoute(nodeKey.publicKey)))
@ -72,7 +72,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
}
test("check invoice signature with unknown field from invoice request") {
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), randomBytes32())
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), BlockHash(randomBytes32()))
val offer = Offer(Some(10000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
val basicRequest = InvoiceRequest(offer, 11000 msat, 1, Features.empty, payerKey, chain)
val requestWithUnknownTlv = basicRequest.copy(records = TlvStream(basicRequest.records.records, Set(GenericTlv(UInt64(87), hex"0404"))))
@ -83,7 +83,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
}
test("check that invoice matches offer") {
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), randomBytes32())
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), BlockHash(randomBytes32()))
val offer = Offer(Some(10000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
val request = InvoiceRequest(offer, 11000 msat, 1, Features.empty, payerKey, chain)
val invoice = Bolt12Invoice(request, randomBytes32(), nodeKey, 300 seconds, Features.empty, Seq(createPaymentBlindedRoute(nodeKey.publicKey)))
@ -104,7 +104,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
}
test("check that invoice matches invoice request") {
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), randomBytes32())
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), BlockHash(randomBytes32()))
val offer = Offer(Some(15000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
val request = InvoiceRequest(offer, 15000 msat, 1, Features.empty, payerKey, chain)
assert(request.quantity_opt.isEmpty) // when paying for a single item, the quantity field must not be present
@ -145,7 +145,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
}
test("check invoice expiry") {
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), randomBytes32())
val (nodeKey, payerKey, chain) = (randomKey(), randomKey(), BlockHash(randomBytes32()))
val offer = Offer(Some(5000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
val request = InvoiceRequest(offer, 5000 msat, 1, Features.empty, payerKey, chain)
val invoice = Bolt12Invoice(request, randomBytes32(), nodeKey, 300 seconds, Features.empty, Seq(createPaymentBlindedRoute(nodeKey.publicKey)))
@ -251,7 +251,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
assert(codedDecoded.paymentHash == paymentHash)
assert(codedDecoded.relativeExpiry == relativeExpiry.seconds)
assert(codedDecoded.fallbacks.contains(fallbacks))
assert(codedDecoded.records.unknown.toSet == Set(GenericTlv(UInt64(121), hex"010203"), GenericTlv(UInt64(313), hex"baba")))
assert(codedDecoded.records.unknown == Set(GenericTlv(UInt64(121), hex"010203"), GenericTlv(UInt64(313), hex"baba")))
}
test("minimal tip") {

View file

@ -21,6 +21,7 @@ import fr.acinq.bitcoin.scalacompat.DeterministicWallet.ExtendedPrivateKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Crypto, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, TxOut}
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
import fr.acinq.eclair.Features._
import fr.acinq.eclair.TestUtils.randomTxId
import fr.acinq.eclair.channel._
import fr.acinq.eclair.channel.fsm.Channel
import fr.acinq.eclair.crypto.{ShaChain, Sphinx}
@ -704,7 +705,7 @@ object PaymentPacketSpec {
val channelReserve = testCapacity * 0.01
val localParams = LocalParams(null, null, null, Long.MaxValue.msat, Some(channelReserve), null, null, 0, isInitiator = true, None, None, null)
val remoteParams = RemoteParams(randomKey().publicKey, null, UInt64.MaxValue, Some(channelReserve), null, null, maxAcceptedHtlcs = 0, null, null, null, null, null, None)
val commitInput = InputInfo(OutPoint(randomBytes32(), 1), TxOut(testCapacity, Nil), Nil)
val commitInput = InputInfo(OutPoint(randomTxId(), 1), TxOut(testCapacity, Nil), Nil)
val localCommit = LocalCommit(0, null, CommitTxAndRemoteSig(Transactions.CommitTx(commitInput, null), null), Nil)
val remoteCommit = RemoteCommit(0, null, null, randomKey().publicKey)
val localChanges = LocalChanges(Nil, Nil, Nil)

View file

@ -16,7 +16,7 @@
package fr.acinq.eclair.router
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, SatoshiLong, TxId}
import fr.acinq.eclair.RealShortChannelId
import fr.acinq.eclair.router.Router.{ChannelMeta, PublicChannel}
import fr.acinq.eclair.router.Sync._
@ -103,8 +103,8 @@ class ChannelRangeQueriesSpec extends AnyFunSuite {
val ef = RouteCalculationSpec.makeChannel(167514L, e, f)
val channels = SortedMap(
ab.shortChannelId -> PublicChannel(ab, ByteVector32.Zeroes, 0 sat, Some(uab1), Some(uab2), Some(ChannelMeta(1000 msat, 400 msat))),
cd.shortChannelId -> PublicChannel(cd, ByteVector32.Zeroes, 0 sat, Some(ucd1), None, None)
ab.shortChannelId -> PublicChannel(ab, TxId(ByteVector32.Zeroes), 0 sat, Some(uab1), Some(uab2), Some(ChannelMeta(1000 msat, 400 msat))),
cd.shortChannelId -> PublicChannel(cd, TxId(ByteVector32.Zeroes), 0 sat, Some(ucd1), None, None)
)
assert(getChannelDigestInfo(channels)(ab.shortChannelId) == (Timestamps(now, now), Checksums(3352963162L, 2581904122L)))

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.router
import com.softwaremill.quicklens.ModifyPimp
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Satoshi, SatoshiLong}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, Satoshi, SatoshiLong, TxId}
import fr.acinq.eclair.payment.relay.Relayer.RelayFees
import fr.acinq.eclair.router.Announcements.makeNodeAnnouncement
import fr.acinq.eclair.router.BaseRouterSpec.channelHopFromUpdate
@ -554,7 +554,7 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution {
val publicChannels = channels.map { case (shortChannelId, announcement) =>
val HopRelayParams.FromAnnouncement(update) = edges.find(_.desc.shortChannelId == shortChannelId).get.params
val (update_1_opt, update_2_opt) = if (update.channelFlags.isNode1) (Some(update), None) else (None, Some(update))
val pc = PublicChannel(announcement, ByteVector32.Zeroes, Satoshi(1000), update_1_opt, update_2_opt, None)
val pc = PublicChannel(announcement, TxId(ByteVector32.Zeroes), Satoshi(1000), update_1_opt, update_2_opt, None)
(shortChannelId, pc)
}
@ -903,26 +903,26 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution {
val updates = SortedMap(
RealShortChannelId(BlockHeight(565643), 1216, 0) -> PublicChannel(
ann = makeChannel(ShortChannelId.fromCoordinates("565643x1216x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")),
fundingTxid = ByteVector32.Zeroes,
fundingTxId = TxId(ByteVector32.Zeroes),
capacity = DEFAULT_CAPACITY,
update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(14), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 10, 4_294_967_295L msat)),
update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 0 msat, feeBaseMsat = 1000 msat, 100, 15_000_000_000L msat)),
update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, Block.RegtestGenesisBlock.hash, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(14), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 10, 4_294_967_295L msat)),
update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, Block.RegtestGenesisBlock.hash, ShortChannelId.fromCoordinates("565643x1216x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 0 msat, feeBaseMsat = 1000 msat, 100, 15_000_000_000L msat)),
meta_opt = None
),
RealShortChannelId(BlockHeight(542280), 2156, 0) -> PublicChannel(
ann = makeChannel(ShortChannelId.fromCoordinates("542280x2156x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")),
fundingTxid = ByteVector32.Zeroes,
fundingTxId = TxId(ByteVector32.Zeroes),
capacity = DEFAULT_CAPACITY,
update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1000 msat, feeBaseMsat = 1000 msat, 100, 16_777_000_000L msat)),
update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 667 msat, 1, 16_777_000_000L msat)),
update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, Block.RegtestGenesisBlock.hash, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1000 msat, feeBaseMsat = 1000 msat, 100, 16_777_000_000L msat)),
update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, Block.RegtestGenesisBlock.hash, ShortChannelId.fromCoordinates("542280x2156x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags(isEnabled = true, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 667 msat, 1, 16_777_000_000L msat)),
meta_opt = None
),
RealShortChannelId(BlockHeight(565779), 2711, 0) -> PublicChannel(
ann = makeChannel(ShortChannelId.fromCoordinates("565779x2711x0").success.value.toLong, PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")),
fundingTxid = ByteVector32.Zeroes,
fundingTxId = TxId(ByteVector32.Zeroes),
capacity = DEFAULT_CAPACITY,
update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, 230_000_000L msat)),
update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags(isEnabled = false, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, 230_000_000L msat)),
update_1_opt = Some(ChannelUpdate(ByteVector64.Zeroes, Block.RegtestGenesisBlock.hash, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags.DUMMY, CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, 230_000_000L msat)),
update_2_opt = Some(ChannelUpdate(ByteVector64.Zeroes, Block.RegtestGenesisBlock.hash, ShortChannelId.fromCoordinates("565779x2711x0").success.value, 0 unixsec, ChannelUpdate.MessageFlags(dontForward = false), ChannelUpdate.ChannelFlags(isEnabled = false, isNode1 = false), CltvExpiryDelta(144), htlcMinimumMsat = 1 msat, feeBaseMsat = 1000 msat, 100, 230_000_000L msat)),
meta_opt = None
)
)

View file

@ -20,7 +20,7 @@ import akka.actor.typed.scaladsl.adapter.actorRefAdapter
import akka.actor.{Actor, Props}
import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Satoshi, Script, Transaction, TxIn, TxOut}
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, Satoshi, Script, Transaction, TxId, TxIn, TxOut}
import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.RealShortChannelId
import fr.acinq.eclair._
@ -355,7 +355,7 @@ object RoutingSyncSpec {
val channelUpdate_21 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv2, priv1.publicKey, shortChannelId, cltvExpiryDelta = CltvExpiryDelta(7), 0 msat, feeBaseMsat = 766000 msat, feeProportionalMillionths = 10, 500000000L msat, timestamp = timestamp)
val nodeAnnouncement_1 = makeNodeAnnouncement(priv1, "a", Color(0, 0, 0), List(), TestConstants.Bob.nodeParams.features.nodeAnnouncementFeatures())
val nodeAnnouncement_2 = makeNodeAnnouncement(priv2, "b", Color(0, 0, 0), List(), Features.empty)
val publicChannel = PublicChannel(channelAnn_12, ByteVector32.Zeroes, Satoshi(0), Some(channelUpdate_12), Some(channelUpdate_21), None)
val publicChannel = PublicChannel(channelAnn_12, TxId(ByteVector32.Zeroes), Satoshi(0), Some(channelUpdate_12), Some(channelUpdate_21), None)
(publicChannel, nodeAnnouncement_1, nodeAnnouncement_2)
}

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.testutils
import akka.testkit.TestProbe
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi}
import fr.acinq.bitcoin.scalacompat.{Satoshi, TxId}
import fr.acinq.eclair.MilliSatoshi
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchFundingSpent, WatchPublished, WatchTxConfirmed}
import fr.acinq.eclair.channel.AvailableBalanceChanged
@ -22,19 +22,19 @@ case class PimpTestProbe(probe: TestProbe) extends Assertions {
msg
}
def expectWatchFundingSpent(txid: ByteVector32, hints_opt: Option[Set[ByteVector32]] = None): WatchFundingSpent =
def expectWatchFundingSpent(txid: TxId, hints_opt: Option[Set[TxId]] = None): WatchFundingSpent =
expectMsgTypeHaving[WatchFundingSpent](w => {
assert(w.txId == txid, "txid")
hints_opt.foreach(hints => assert(hints == w.hints))
})
def expectWatchFundingConfirmed(txid: ByteVector32): WatchFundingConfirmed =
def expectWatchFundingConfirmed(txid: TxId): WatchFundingConfirmed =
expectMsgTypeHaving[WatchFundingConfirmed](w => assert(w.txId == txid, "txid"))
def expectWatchTxConfirmed(txid: ByteVector32): WatchTxConfirmed =
def expectWatchTxConfirmed(txid: TxId): WatchTxConfirmed =
expectMsgTypeHaving[WatchTxConfirmed](w => assert(w.txId == txid, "txid"))
def expectWatchPublished(txid: ByteVector32): WatchPublished =
def expectWatchPublished(txid: TxId): WatchPublished =
expectMsgTypeHaving[WatchPublished](w => assert(w.txId == txid, "txid"))
def expectAvailableBalanceChanged(balance: MilliSatoshi, capacity: Satoshi): AvailableBalanceChanged =

Some files were not shown because too many files have changed in this diff Show more