mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-23 14:40:34 +01:00
Use typed TxId (#2742)
And explicitly encode/decode as a `tx_hash` for lightning messages.
This commit is contained in:
parent
e20b736bf3
commit
e73c1cf45c
113 changed files with 756 additions and 704 deletions
|
@ -24,7 +24,7 @@ import akka.pattern._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import com.softwaremill.quicklens.ModifyPimp
|
import com.softwaremill.quicklens.ModifyPimp
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.ApiTypes.ChannelNotFound
|
||||||
import fr.acinq.eclair.balance.CheckBalance.GlobalBalance
|
import fr.acinq.eclair.balance.CheckBalance.GlobalBalance
|
||||||
import fr.acinq.eclair.balance.{BalanceActor, ChannelsListener}
|
import fr.acinq.eclair.balance.{BalanceActor, ChannelsListener}
|
||||||
|
@ -58,7 +58,7 @@ import java.util.UUID
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
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])
|
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 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]
|
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 {
|
val feeRate = confirmationTargetOrFeerate match {
|
||||||
case Left(blocks) =>
|
case Left(blocks) =>
|
||||||
if (blocks < 3) appKit.nodeParams.currentFeerates.fast
|
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 {
|
appKit.wallet match {
|
||||||
case w: BitcoinCoreClient => w.cpfp(outpoints, FeeratePerKw(targetFeeratePerByte)).map(_.txid)
|
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"))
|
case _ => Future.failed(new IllegalArgumentException("this call is only available with a bitcoin core backend"))
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair
|
||||||
|
|
||||||
import com.typesafe.config.{Config, ConfigFactory, ConfigValueType}
|
import com.typesafe.config.{Config, ConfigFactory, ConfigValueType}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.Setup.Seeds
|
||||||
import fr.acinq.eclair.blockchain.fee._
|
import fr.acinq.eclair.blockchain.fee._
|
||||||
import fr.acinq.eclair.channel.ChannelFlags
|
import fr.acinq.eclair.channel.ChannelFlags
|
||||||
|
@ -73,7 +73,7 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
|
||||||
autoReconnect: Boolean,
|
autoReconnect: Boolean,
|
||||||
initialRandomReconnectDelay: FiniteDuration,
|
initialRandomReconnectDelay: FiniteDuration,
|
||||||
maxReconnectInterval: FiniteDuration,
|
maxReconnectInterval: FiniteDuration,
|
||||||
chainHash: ByteVector32,
|
chainHash: BlockHash,
|
||||||
invoiceExpiry: FiniteDuration,
|
invoiceExpiry: FiniteDuration,
|
||||||
multiPartPaymentExpiry: FiniteDuration,
|
multiPartPaymentExpiry: FiniteDuration,
|
||||||
peerConnectionConf: PeerConnection.Conf,
|
peerConnectionConf: PeerConnection.Conf,
|
||||||
|
@ -184,16 +184,16 @@ object NodeParams extends Logging {
|
||||||
Seeds(nodeSeed, channelSeed)
|
Seeds(nodeSeed, channelSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val chain2Hash: Map[String, ByteVector32] = Map(
|
private val chain2Hash: Map[String, BlockHash] = Map(
|
||||||
"regtest" -> Block.RegtestGenesisBlock.hash,
|
"regtest" -> Block.RegtestGenesisBlock.hash,
|
||||||
"testnet" -> Block.TestnetGenesisBlock.hash,
|
"testnet" -> Block.TestnetGenesisBlock.hash,
|
||||||
"signet" -> Block.SignetGenesisBlock.hash,
|
"signet" -> Block.SignetGenesisBlock.hash,
|
||||||
"mainnet" -> Block.LivenetGenesisBlock.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] = {
|
def parseSocks5ProxyParams(config: Config): Option[Socks5ProxyParams] = {
|
||||||
if (config.getBoolean("socks5.enabled")) {
|
if (config.getBoolean("socks5.enabled")) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import akka.actor.{ActorRef, ActorSystem, Props, SupervisorStrategy, typed}
|
||||||
import akka.pattern.after
|
import akka.pattern.after
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.Setup.Seeds
|
||||||
import fr.acinq.eclair.balance.{BalanceActor, ChannelsListener}
|
import fr.acinq.eclair.balance.{BalanceActor, ChannelsListener}
|
||||||
import fr.acinq.eclair.blockchain._
|
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 "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 {
|
def getBitcoinStatus(bitcoinClient: BasicBitcoinJsonRPCClient): Future[BitcoinStatus] = for {
|
||||||
json <- bitcoinClient.invoke("getblockchaininfo").recover { case e => throw BitcoinRPCConnectionException(e) }
|
json <- bitcoinClient.invoke("getblockchaininfo").recover { case e => throw BitcoinRPCConnectionException(e) }
|
||||||
|
@ -147,7 +147,8 @@ class Setup(val datadir: File,
|
||||||
ibd = (json \ "initialblockdownload").extract[Boolean]
|
ibd = (json \ "initialblockdownload").extract[Boolean]
|
||||||
blocks = (json \ "blocks").extract[Long]
|
blocks = (json \ "blocks").extract[Long]
|
||||||
headers = (json \ "headers").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])
|
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) }
|
unspentAddresses <- bitcoinClient.invoke("listunspent").recover { _ => if (wallet.isEmpty && wallets.length > 1) throw BitcoinDefaultWalletException(wallets) else throw BitcoinWalletNotLoadedException(wallet.getOrElse(""), wallets) }
|
||||||
.collect { case JArray(values) =>
|
.collect { case JArray(values) =>
|
||||||
|
|
|
@ -3,7 +3,7 @@ package fr.acinq.eclair.balance
|
||||||
import akka.actor.typed.eventstream.EventStream
|
import akka.actor.typed.eventstream.EventStream
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
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
|
||||||
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
||||||
import fr.acinq.eclair.balance.BalanceActor._
|
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 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"
|
val JInt(ancestorCount) = json \ "ancestorcount"
|
||||||
(utxo.txid, ancestorCount.toLong)
|
(utxo.txid, ancestorCount.toLong)
|
||||||
}).recover {
|
}).recover {
|
||||||
|
@ -55,7 +55,7 @@ object BalanceActor {
|
||||||
(utxo.txid, 0)
|
(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 {
|
for {
|
||||||
utxos <- bitcoinClient.listUnspent()
|
utxos <- bitcoinClient.listUnspent()
|
||||||
|
@ -134,7 +134,7 @@ private class BalanceActor(context: ActorContext[Command],
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
case WrappedUtxoInfo(res) =>
|
case WrappedUtxoInfo(res) =>
|
||||||
res match {
|
res match {
|
||||||
case Success(UtxoInfo(utxos: Seq[Utxo], ancestorCount: Map[ByteVector32, Long])) =>
|
case Success(UtxoInfo(utxos, ancestorCount)) =>
|
||||||
val filteredByStatus: Map[String, Seq[Utxo]] = Map(
|
val filteredByStatus: Map[String, Seq[Utxo]] = Map(
|
||||||
Monitoring.Tags.UtxoStatuses.Confirmed -> utxos.filter(utxo => utxo.confirmations > 0),
|
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.
|
// We cannot create chains of unconfirmed transactions with more than 25 elements, so we ignore such utxos.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.acinq.eclair.balance
|
package fr.acinq.eclair.balance
|
||||||
|
|
||||||
import com.softwaremill.quicklens._
|
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.blockchain.bitcoind.rpc.BitcoinCoreClient
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing
|
import fr.acinq.eclair.channel.Helpers.Closing
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing.{CurrentRemoteClose, LocalClose, NextRemoteClose, RemoteClose}
|
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
|
* 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.
|
* 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
|
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 totalToLocal: Btc = toLocal.values.map(_.toSatoshi).sum
|
||||||
val totalHtlcs: Btc = htlcs.values.map(_.toSatoshi).sum
|
val totalHtlcs: Btc = htlcs.values.map(_.toSatoshi).sum
|
||||||
val total: Btc = totalToLocal + totalHtlcs + htlcsUnpublished
|
val total: Btc = totalToLocal + totalHtlcs + htlcsUnpublished
|
||||||
|
@ -153,7 +153,7 @@ object CheckBalance {
|
||||||
val finalScriptPubKey = Script.write(Script.pay2wpkh(c.params.localParams.walletStaticPaymentBasepoint.get))
|
val finalScriptPubKey = Script.write(Script.pay2wpkh(c.params.localParams.walletStaticPaymentBasepoint.get))
|
||||||
Transactions.findPubKeyScriptIndex(remoteCommitPublished.commitTx, finalScriptPubKey) match {
|
Transactions.findPubKeyScriptIndex(remoteCommitPublished.commitTx, finalScriptPubKey) match {
|
||||||
case Right(outputIndex) => Map(remoteCommitPublished.commitTx.txid -> remoteCommitPublished.commitTx.txOut(outputIndex).amount.toBtc)
|
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 {
|
} else {
|
||||||
remoteCommitPublished.claimMainOutputTx.toSeq.map(c => c.tx.txid -> c.tx.txOut.head.amount.toBtc).toMap
|
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] = {
|
def prunePublishedTransactions(br: OffChainBalance, bitcoinClient: BitcoinCoreClient)(implicit ec: ExecutionContext): Future[OffChainBalance] = {
|
||||||
for {
|
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.localCloseBalance.htlcs.keys ++
|
||||||
br.closing.remoteCloseBalance.toLocal.keys ++
|
br.closing.remoteCloseBalance.toLocal.keys ++
|
||||||
br.closing.remoteCloseBalance.htlcs.keys ++
|
br.closing.remoteCloseBalance.htlcs.keys ++
|
||||||
br.closing.mutualCloseBalance.toLocal.keys)
|
br.closing.mutualCloseBalance.toLocal.keys)
|
||||||
.map(txid => bitcoinClient.getTxConfirmations(txid).map(_ map { confirmations => txid -> confirmations })))
|
.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 {
|
} yield {
|
||||||
br
|
br
|
||||||
.modifyAll(
|
.modifyAll(
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.blockchain
|
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.BlockHeight
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
|
import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw
|
||||||
|
|
||||||
sealed trait BlockchainEvent
|
sealed trait BlockchainEvent
|
||||||
|
|
||||||
case class NewBlock(blockHash: ByteVector32) extends BlockchainEvent
|
case class NewBlock(blockId: BlockId) extends BlockchainEvent
|
||||||
|
|
||||||
case class NewTransaction(tx: Transaction) extends BlockchainEvent
|
case class NewTransaction(tx: Transaction) extends BlockchainEvent
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.blockchain
|
||||||
|
|
||||||
import fr.acinq.bitcoin.psbt.Psbt
|
import fr.acinq.bitcoin.psbt.Psbt
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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 fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ trait OnChainChannelFunder {
|
||||||
* Publish a transaction on the bitcoin network.
|
* Publish a transaction on the bitcoin network.
|
||||||
* This method must be idempotent: if the tx was already published, it must return a success.
|
* 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. */
|
/** Create a fully signed channel funding transaction with the provided pubkeyScript. */
|
||||||
def makeFundingTx(pubkeyScript: ByteVector, amount: Satoshi, feeRate: FeeratePerKw)(implicit ec: ExecutionContext): Future[MakeFundingTxResponse]
|
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]
|
def commit(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean]
|
||||||
|
|
||||||
/** Return the transaction if it exists, either in the blockchain or in the mempool. */
|
/** 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. */
|
/** 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". */
|
/** 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]
|
def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean]
|
||||||
|
@ -139,9 +139,9 @@ object OnChainWallet {
|
||||||
|
|
||||||
/** Transaction with all available witnesses. */
|
/** Transaction with all available witnesses. */
|
||||||
val partiallySignedTx: Transaction = {
|
val partiallySignedTx: Transaction = {
|
||||||
var tx = psbt.getGlobal.getTx
|
var tx = psbt.global.tx
|
||||||
for (i <- 0 until psbt.getInputs.size()) {
|
for (i <- 0 until psbt.inputs.size()) {
|
||||||
Option(psbt.getInputs.get(i).getScriptWitness).foreach { witness =>
|
Option(psbt.inputs.get(i).getScriptWitness).foreach { witness =>
|
||||||
tx = tx.updateWitness(i, witness)
|
tx = tx.updateWitness(i, witness)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,14 +61,14 @@ object ZmqWatcher {
|
||||||
private case class GetBlockCountFailed(t: Throwable) extends Command
|
private case class GetBlockCountFailed(t: Throwable) extends Command
|
||||||
private case class CheckBlockHeight(current: BlockHeight) extends Command
|
private case class CheckBlockHeight(current: BlockHeight) extends Command
|
||||||
private case class PublishBlockHeight(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
|
private case class ProcessNewTransaction(tx: Transaction) extends Command
|
||||||
|
|
||||||
final case class ValidateRequest(replyTo: ActorRef[ValidateResult], ann: ChannelAnnouncement) 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 ValidateResult(c: ChannelAnnouncement, fundingTx: Either[Throwable, (Transaction, UtxoStatus)])
|
||||||
|
|
||||||
final case class GetTxWithMeta(replyTo: ActorRef[GetTxWithMetaResponse], txid: ByteVector32) extends Command
|
final case class GetTxWithMeta(replyTo: ActorRef[GetTxWithMetaResponse], txid: TxId) extends Command
|
||||||
final case class GetTxWithMetaResponse(txid: ByteVector32, tx_opt: Option[Transaction], lastBlockTimestamp: TimestampSecond)
|
final case class GetTxWithMetaResponse(txid: TxId, tx_opt: Option[Transaction], lastBlockTimestamp: TimestampSecond)
|
||||||
|
|
||||||
sealed trait UtxoStatus
|
sealed trait UtxoStatus
|
||||||
object UtxoStatus {
|
object UtxoStatus {
|
||||||
|
@ -79,7 +79,7 @@ object ZmqWatcher {
|
||||||
/** Watch for confirmation of a given transaction. */
|
/** Watch for confirmation of a given transaction. */
|
||||||
sealed trait WatchConfirmed[T <: WatchConfirmedTriggered] extends Watch[T] {
|
sealed trait WatchConfirmed[T <: WatchConfirmedTriggered] extends Watch[T] {
|
||||||
/** TxId of the transaction to watch. */
|
/** TxId of the transaction to watch. */
|
||||||
def txId: ByteVector32
|
def txId: TxId
|
||||||
/** Number of confirmations. */
|
/** Number of confirmations. */
|
||||||
def minDepth: Long
|
def minDepth: Long
|
||||||
}
|
}
|
||||||
|
@ -93,14 +93,14 @@ object ZmqWatcher {
|
||||||
*/
|
*/
|
||||||
sealed trait WatchSpent[T <: WatchSpentTriggered] extends Watch[T] {
|
sealed trait WatchSpent[T <: WatchSpentTriggered] extends Watch[T] {
|
||||||
/** TxId of the outpoint to watch. */
|
/** TxId of the outpoint to watch. */
|
||||||
def txId: ByteVector32
|
def txId: TxId
|
||||||
/** Index of the outpoint to watch. */
|
/** Index of the outpoint to watch. */
|
||||||
def outputIndex: Int
|
def outputIndex: Int
|
||||||
/**
|
/**
|
||||||
* TxIds of potential spending transactions; most of the time we know the txs, and it allows for optimizations.
|
* 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.
|
* 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] {
|
sealed trait WatchSpentBasic[T <: WatchSpentBasicTriggered] extends Watch[T] {
|
||||||
/** TxId of the outpoint to watch. */
|
/** TxId of the outpoint to watch. */
|
||||||
def txId: ByteVector32
|
def txId: TxId
|
||||||
/** Index of the outpoint to watch. */
|
/** Index of the outpoint to watch. */
|
||||||
def outputIndex: Int
|
def outputIndex: Int
|
||||||
}
|
}
|
||||||
|
@ -136,32 +136,32 @@ object ZmqWatcher {
|
||||||
/** This event is sent when a [[WatchSpentBasic]] condition is met. */
|
/** This event is sent when a [[WatchSpentBasic]] condition is met. */
|
||||||
sealed trait WatchSpentBasicTriggered extends WatchTriggered
|
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 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 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
|
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. */
|
/** 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 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 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 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 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 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
|
case class WatchAlternativeCommitTxConfirmedTriggered(blockHeight: BlockHeight, txIndex: Int, tx: Transaction) extends WatchConfirmedTriggered
|
||||||
|
|
||||||
private sealed trait AddWatchResult
|
private sealed trait AddWatchResult
|
||||||
|
@ -171,7 +171,7 @@ object ZmqWatcher {
|
||||||
|
|
||||||
def apply(nodeParams: NodeParams, blockCount: AtomicLong, client: BitcoinCoreClient): Behavior[Command] =
|
def apply(nodeParams: NodeParams, blockCount: AtomicLong, client: BitcoinCoreClient): Behavior[Command] =
|
||||||
Behaviors.setup { context =>
|
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)))
|
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NewTransaction](t => ProcessNewTransaction(t.tx)))
|
||||||
Behaviors.withTimers { timers =>
|
Behaviors.withTimers { timers =>
|
||||||
// we initialize block count
|
// we initialize block count
|
||||||
|
@ -184,8 +184,8 @@ object ZmqWatcher {
|
||||||
|
|
||||||
private def utxo(w: GenericWatch): Option[OutPoint] = {
|
private def utxo(w: GenericWatch): Option[OutPoint] = {
|
||||||
w match {
|
w match {
|
||||||
case w: WatchSpent[_] => Some(OutPoint(w.txId.reverse, w.outputIndex))
|
case w: WatchSpent[_] => Some(OutPoint(w.txId, w.outputIndex))
|
||||||
case w: WatchSpentBasic[_] => Some(OutPoint(w.txId.reverse, w.outputIndex))
|
case w: WatchSpentBasic[_] => Some(OutPoint(w.txId, w.outputIndex))
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.blockchain.bitcoind.rpc
|
||||||
|
|
||||||
import fr.acinq.eclair.KamonExt
|
import fr.acinq.eclair.KamonExt
|
||||||
import fr.acinq.eclair.blockchain.Monitoring.{Metrics, Tags}
|
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.JsonAST.JValue
|
||||||
import org.json4s.jackson.{JacksonSerialization, Serialization}
|
import org.json4s.jackson.{JacksonSerialization, Serialization}
|
||||||
import org.json4s.{DefaultFormats, Formats}
|
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 {
|
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 scheme = if (ssl) "https" else "http"
|
||||||
private val serviceUri = wallet match {
|
private val serviceUri = wallet match {
|
||||||
|
|
|
@ -64,15 +64,15 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
|
|
||||||
//------------------------- TRANSACTIONS -------------------------//
|
//------------------------- 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))
|
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 {
|
rpcClient.invoke("getrawtransaction", txid).collect {
|
||||||
case JString(raw) => raw
|
case JString(raw) => raw
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTransactionMeta(txid: ByteVector32)(implicit ec: ExecutionContext): Future[GetTxWithMetaResponse] =
|
def getTransactionMeta(txid: TxId)(implicit ec: ExecutionContext): Future[GetTxWithMetaResponse] =
|
||||||
for {
|
for {
|
||||||
tx_opt <- getTransaction(txid).map(Some(_)).recover { case _ => None }
|
tx_opt <- getTransaction(txid).map(Some(_)).recover { case _ => None }
|
||||||
blockchainInfo <- rpcClient.invoke("getblockchaininfo")
|
blockchainInfo <- rpcClient.invoke("getblockchaininfo")
|
||||||
|
@ -80,7 +80,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
} yield GetTxWithMetaResponse(txid, tx_opt, TimestampSecond(timestamp.toLong))
|
} yield GetTxWithMetaResponse(txid, tx_opt, TimestampSecond(timestamp.toLong))
|
||||||
|
|
||||||
/** Get the number of confirmations of a given 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]] =
|
||||||
rpcClient.invoke("getrawtransaction", txid, 1 /* verbose output is needed to get the number of confirmations */)
|
rpcClient.invoke("getrawtransaction", txid, 1 /* verbose output is needed to get the number of confirmations */)
|
||||||
.map(json => Some((json \ "confirmations").extractOrElse[Int](0)))
|
.map(json => Some((json \ "confirmations").extractOrElse[Int](0)))
|
||||||
.recover {
|
.recover {
|
||||||
|
@ -88,9 +88,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the hash of the block containing a given transaction. */
|
/** 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 */)
|
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 {
|
.recover {
|
||||||
case t: JsonRPCError if t.error.code == -5 => None // Invalid or non-wallet transaction id (code: -5)
|
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
|
* @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.
|
* 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 {
|
for {
|
||||||
Some(blockHash) <- getTxBlockHash(txid)
|
Some(blockId) <- getTxBlockId(txid)
|
||||||
json <- rpcClient.invoke("getblock", blockHash)
|
json <- rpcClient.invoke("getblock", blockId)
|
||||||
JInt(height) = json \ "height"
|
JInt(height) = json \ "height"
|
||||||
JArray(txs) = json \ "tx"
|
JArray(txs) = json \ "tx"
|
||||||
index = txs.indexOf(JString(txid.toHex))
|
index = txs.indexOf(JString(txid.value.toHex))
|
||||||
} yield (BlockHeight(height.toInt), index)
|
} 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
|
* (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]].
|
* 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 {
|
for {
|
||||||
json <- rpcClient.invoke("gettxout", txid, outputIndex, includeMempool)
|
json <- rpcClient.invoke("gettxout", txid, outputIndex, includeMempool)
|
||||||
} yield json != JNull
|
} 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.
|
* 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.
|
* 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 {
|
getTxConfirmations(txid).flatMap {
|
||||||
case Some(confirmations) if confirmations > 0 =>
|
case Some(confirmations) if confirmations > 0 =>
|
||||||
// There is an important limitation when using isTransactionOutputSpendable: if it returns false, it can mean a
|
// 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
|
} yield doubleSpent
|
||||||
|
|
||||||
/** Search for mempool transaction spending a given output. */
|
/** 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 {
|
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 =>
|
}.flatMap { spendingTxIds =>
|
||||||
spendingTxIds.headOption match {
|
spendingTxIds.headOption match {
|
||||||
case Some(spendingTxId) => getTransaction(spendingTxId)
|
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
|
* 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.
|
* 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 txid id of the transaction output that has been spent.
|
||||||
* @param outputIndex index 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.
|
* @param limit maximum number of previous blocks to scan.
|
||||||
* @return the transaction spending the given output.
|
* @return the transaction spending the given output.
|
||||||
*/
|
*/
|
||||||
def lookForSpendingTx(blockhash_opt: Option[ByteVector32], txid: 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] = {
|
||||||
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] =
|
|
||||||
for {
|
for {
|
||||||
blockhash <- blockhash_opt match {
|
blockId <- blockHash_opt match {
|
||||||
case Some(b) => Future.successful(b)
|
case Some(blockHash) => Future.successful(BlockId(blockHash))
|
||||||
case None => rpcClient.invoke("getbestblockhash").collect { case JString(b) => ByteVector32.fromValidHex(b) }
|
// 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
|
// with a verbosity of 0, getblock returns the raw serialized block
|
||||||
block <- rpcClient.invoke("getblock", blockhash, 0).collect { case JString(b) => Block.read(b) }
|
block <- rpcClient.invoke("getblock", blockId, 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 == KotlinUtils.scala2kmp(txid) && i.outPoint.index == outputIndex)) match {
|
||||||
res <- block.tx.asScala.find(tx => tx.txIn.asScala.exists(i => i.outPoint.txid == txid && i.outPoint.index == outputIndex)) match {
|
|
||||||
case Some(tx) => Future.successful(KotlinUtils.kmp2scala(tx))
|
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"))
|
case None => Future.failed(new RuntimeException(s"couldn't find tx spending $txid:$outputIndex in the blockchain"))
|
||||||
}
|
}
|
||||||
} yield res
|
} yield res
|
||||||
|
}
|
||||||
|
|
||||||
def listTransactions(count: Int, skip: Int)(implicit ec: ExecutionContext): Future[List[WalletTx]] = rpcClient.invoke("listtransactions", "*", count, skip).map {
|
def listTransactions(count: Int, skip: Int)(implicit ec: ExecutionContext): Future[List[WalletTx]] = rpcClient.invoke("listtransactions", "*", count, skip).map {
|
||||||
case JArray(txs) => txs.map(tx => {
|
case JArray(txs) => txs.map(tx => {
|
||||||
|
@ -222,14 +219,14 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
case _ => Satoshi(0)
|
case _ => Satoshi(0)
|
||||||
}
|
}
|
||||||
val JInt(confirmations) = tx \ "confirmations"
|
val JInt(confirmations) = tx \ "confirmations"
|
||||||
// while transactions are still in the mempool, block hash will no be included
|
// while transactions are still in the mempool, blockId will not be included
|
||||||
val blockHash = tx \ "blockhash" match {
|
val blockId_opt = tx \ "blockhash" match {
|
||||||
case JString(blockHash) => ByteVector32.fromValidHex(blockHash)
|
case JString(blockId) => Some(BlockId(ByteVector32.fromValidHex(blockId)))
|
||||||
case _ => ByteVector32.Zeroes
|
case _ => None
|
||||||
}
|
}
|
||||||
val JString(txid) = tx \ "txid"
|
val JString(txid) = tx \ "txid"
|
||||||
val JInt(timestamp) = tx \ "time"
|
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
|
}).reverse
|
||||||
case _ => Nil
|
case _ => Nil
|
||||||
}
|
}
|
||||||
|
@ -409,9 +406,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Recursively fetch unconfirmed parents and return the complete unconfirmed ancestors tree. */
|
/** 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 => {
|
Future.sequence(leaves.map(txid => getMempoolTx(txid))).flatMap(txs => {
|
||||||
val current2 = current.concat(txs.map(tx => tx.txid -> tx))
|
val current2 = current.concat(txs.map(tx => tx.txid -> tx))
|
||||||
val remainingParents = txs.flatMap(_.unconfirmedParents) -- current2.keySet
|
val remainingParents = txs.flatMap(_.unconfirmedParents) -- current2.keySet
|
||||||
|
@ -468,9 +465,9 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
*
|
*
|
||||||
* @return the transaction id (txid)
|
* @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 {
|
rpcClient.invoke("sendrawtransaction", tx.toString()).collect {
|
||||||
case JString(txid) => ByteVector32.fromValidHex(txid)
|
case JString(txid) => TxId.fromValidHex(txid)
|
||||||
}.recoverWith {
|
}.recoverWith {
|
||||||
case JsonRPCError(Error(-27, _)) =>
|
case JsonRPCError(Error(-27, _)) =>
|
||||||
// "transaction already in block chain (code: -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.
|
* 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.
|
* 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)
|
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 => {
|
case JArray(locks) => locks.map(item => {
|
||||||
val JString(txid) = item \ "txid"
|
val JString(txid) = item \ "txid"
|
||||||
val JInt(vout) = item \ "vout"
|
val JInt(vout) = item \ "vout"
|
||||||
OutPoint(ByteVector32.fromValidHex(txid).reverse, vout.toInt)
|
OutPoint(TxId.fromValidHex(txid), vout.toInt)
|
||||||
}).toSet
|
}).toSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -544,9 +541,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
// and that the address and public key match
|
// and that the address and public key match
|
||||||
onChainKeyManager_opt match {
|
onChainKeyManager_opt match {
|
||||||
case Some(keyManager) =>
|
case Some(keyManager) =>
|
||||||
// TODO: bitcoin-kmp doesn't accept 'h' for hardened indexes, we should fix this.
|
val computed = keyManager.derivePublicKey(DeterministicWallet.KeyPath(keyPath))
|
||||||
val keyPath1 = keyPath.replace('h', '\'')
|
|
||||||
val computed = keyManager.derivePublicKey(DeterministicWallet.KeyPath(keyPath1))
|
|
||||||
if (computed != (extracted, address)) return Future.failed(new RuntimeException("cannot recompute pubkey generated by bitcoin core"))
|
if (computed != (extracted, address)) return Future.failed(new RuntimeException("cannot recompute pubkey generated by bitcoin core"))
|
||||||
case None => ()
|
case None => ()
|
||||||
}
|
}
|
||||||
|
@ -583,7 +578,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
* @param feeratePerKw fee rate
|
* @param feeratePerKw fee rate
|
||||||
* @return the txid of the sending tx.
|
* @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._
|
import KotlinUtils._
|
||||||
|
|
||||||
val theirOutput = TxOut(amount, pubkeyScript)
|
val theirOutput = TxOut(amount, pubkeyScript)
|
||||||
|
@ -603,10 +598,10 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
} yield txid
|
} 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. */
|
/** 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(
|
rpcClient.invoke(
|
||||||
"sendtoaddress",
|
"sendtoaddress",
|
||||||
address,
|
address,
|
||||||
|
@ -616,7 +611,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
false, // subtractfeefromamount
|
false, // subtractfeefromamount
|
||||||
true, // replaceable
|
true, // replaceable
|
||||||
confirmationTarget).collect {
|
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]] =
|
def getMempool()(implicit ec: ExecutionContext): Future[Seq[Transaction]] =
|
||||||
for {
|
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.
|
// 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 }))
|
txs <- Future.sequence(txids.map(getTransaction(_).map(Some(_)).recover { case _ => None }))
|
||||||
} yield txs.flatten
|
} 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 => {
|
rpcClient.invoke("getmempoolentry", txid).map(json => {
|
||||||
val JInt(vsize) = json \ "vsize"
|
val JInt(vsize) = json \ "vsize"
|
||||||
val JInt(weight) = json \ "weight"
|
val JInt(weight) = json \ "weight"
|
||||||
|
@ -639,7 +634,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
val JDecimal(ancestorFees) = json \ "fees" \ "ancestor"
|
val JDecimal(ancestorFees) = json \ "fees" \ "ancestor"
|
||||||
val JDecimal(descendantFees) = json \ "fees" \ "descendant"
|
val JDecimal(descendantFees) = json \ "fees" \ "descendant"
|
||||||
val JBool(replaceable) = json \ "bip125-replaceable"
|
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.
|
// 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)
|
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] = {
|
def validate(c: ChannelAnnouncement)(implicit ec: ExecutionContext): Future[ValidateResult] = {
|
||||||
val TxCoordinates(blockHeight, txIndex, outputIndex) = coordinates(c.shortChannelId)
|
val TxCoordinates(blockHeight, txIndex, outputIndex) = coordinates(c.shortChannelId)
|
||||||
for {
|
for {
|
||||||
blockHash <- rpcClient.invoke("getblockhash", blockHeight.toInt).map(_.extractOpt[String].map(ByteVector32.fromValidHex).getOrElse(ByteVector32.Zeroes))
|
blockId <- rpcClient.invoke("getblockhash", blockHeight.toInt).map(_.extractOpt[String].map(b => BlockId(ByteVector32.fromValidHex(b))).getOrElse(BlockId(ByteVector32.Zeroes)))
|
||||||
txid: ByteVector32 <- rpcClient.invoke("getblock", blockHash).map(json => Try {
|
txid <- rpcClient.invoke("getblock", blockId).map(json => Try {
|
||||||
val JArray(txs) = json \ "tx"
|
val JArray(txs) = json \ "tx"
|
||||||
ByteVector32.fromValidHex(txs(txIndex).extract[String])
|
TxId.fromValidHex(txs(txIndex).extract[String])
|
||||||
}.getOrElse(ByteVector32.Zeroes))
|
}.getOrElse(TxId(ByteVector32.Zeroes)))
|
||||||
tx <- getRawTransaction(txid)
|
tx <- getRawTransaction(txid)
|
||||||
unspent <- isTransactionOutputSpendable(txid, outputIndex, includeMempool = true)
|
unspent <- isTransactionOutputSpendable(txid, outputIndex, includeMempool = true)
|
||||||
fundingTxStatus <- if (unspent) {
|
fundingTxStatus <- if (unspent) {
|
||||||
|
@ -690,7 +685,7 @@ class BitcoinCoreClient(val rpcClient: BitcoinJsonRPCClient, val onChainKeyManag
|
||||||
case JString(label) => Some(label)
|
case JString(label) => Some(label)
|
||||||
case _ => None
|
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)
|
case class InputWeight(txid: String, vout: Long, weight: Long)
|
||||||
|
|
||||||
object InputWeight {
|
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]])
|
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 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.
|
* @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. */
|
/** 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)
|
def toSatoshi(btcAmount: BigDecimal): Satoshi = Satoshi(btcAmount.bigDecimal.scaleByPowerOfTen(8).longValue)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.blockchain.bitcoind.zmq
|
||||||
|
|
||||||
import akka.Done
|
import akka.Done
|
||||||
import akka.actor.{Actor, ActorLogging}
|
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 fr.acinq.eclair.blockchain.{NewBlock, NewTransaction}
|
||||||
import org.zeromq.ZMQ.Event
|
import org.zeromq.ZMQ.Event
|
||||||
import org.zeromq.{SocketType, ZContext, ZMQ, ZMsg}
|
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 msg: ZMsg => msg.popString() match {
|
||||||
case "hashblock" =>
|
case "hashblock" =>
|
||||||
val blockHash = ByteVector32(ByteVector(msg.pop().getData))
|
val blockId = BlockId(ByteVector32(ByteVector(msg.pop().getData)))
|
||||||
log.debug("received blockhash={}", blockHash)
|
log.debug("received blockId={}", blockId)
|
||||||
context.system.eventStream.publish(NewBlock(blockHash))
|
context.system.eventStream.publish(NewBlock(blockId))
|
||||||
case "rawtx" =>
|
case "rawtx" =>
|
||||||
val tx = Transaction.read(msg.pop().getData)
|
val tx = Transaction.read(msg.pop().getData)
|
||||||
log.debug("received txid={}", tx.txid)
|
log.debug("received txid={}", tx.txid)
|
||||||
|
|
|
@ -104,10 +104,10 @@ object BlockchainWatchdog {
|
||||||
case CheckLatestHeaders(blockHeight) =>
|
case CheckLatestHeaders(blockHeight) =>
|
||||||
val id = UUID.randomUUID()
|
val id = UUID.randomUUID()
|
||||||
if (headersOverDnsEnabled) {
|
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 =>
|
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
|
Behaviors.same
|
||||||
case headers@LatestHeaders(blockHeight, blockHeaders, source) =>
|
case headers@LatestHeaders(blockHeight, blockHeaders, source) =>
|
||||||
|
|
|
@ -21,7 +21,7 @@ import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
import akka.actor.typed.{ActorRef, Behavior}
|
||||||
import akka.pattern.after
|
import akka.pattern.after
|
||||||
import fr.acinq.bitcoin.BlockHeader
|
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.BlockchainWatchdog.{BlockHeaderAt, LatestHeaders}
|
||||||
import fr.acinq.eclair.blockchain.watchdogs.Monitoring.{Metrics, Tags}
|
import fr.acinq.eclair.blockchain.watchdogs.Monitoring.{Metrics, Tags}
|
||||||
import fr.acinq.eclair.tor.Socks5ProxyParams
|
import fr.acinq.eclair.tor.Socks5ProxyParams
|
||||||
|
@ -56,7 +56,7 @@ object ExplorerApi {
|
||||||
/** Explorer friendly-name. */
|
/** Explorer friendly-name. */
|
||||||
def name: String
|
def name: String
|
||||||
/** Map from chainHash to explorer API URI. */
|
/** Map from chainHash to explorer API URI. */
|
||||||
def baseUris: Map[ByteVector32, Uri]
|
def baseUris: Map[BlockHash, Uri]
|
||||||
/** Fetch latest headers from the explorer. */
|
/** Fetch latest headers from the explorer. */
|
||||||
def getLatestHeaders(baseUri: Uri, currentBlockHeight: BlockHeight)(implicit context: ActorContext[Command]): Future[LatestHeaders]
|
def getLatestHeaders(baseUri: Uri, currentBlockHeight: BlockHeight)(implicit context: ActorContext[Command]): Future[LatestHeaders]
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
@ -69,7 +69,7 @@ object ExplorerApi {
|
||||||
private case class WrappedFailure(e: Throwable) extends Command
|
private case class WrappedFailure(e: Throwable) extends Command
|
||||||
// @formatter:on
|
// @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.setup { context =>
|
||||||
Behaviors.receiveMessage {
|
Behaviors.receiveMessage {
|
||||||
case CheckLatestHeaders(replyTo) =>
|
case CheckLatestHeaders(replyTo) =>
|
||||||
|
@ -164,9 +164,9 @@ object ExplorerApi {
|
||||||
val JString(time) = block \ "time"
|
val JString(time) = block \ "time"
|
||||||
val JInt(bits) = block \ "bits"
|
val JInt(bits) = block \ "bits"
|
||||||
val JInt(nonce) = block \ "nonce"
|
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 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))
|
Seq(BlockHeaderAt(BlockHeight(height.toLong), header))
|
||||||
})
|
})
|
||||||
} yield header
|
} yield header
|
||||||
|
@ -193,9 +193,9 @@ object ExplorerApi {
|
||||||
val JInt(time) = block \ "timestamp"
|
val JInt(time) = block \ "timestamp"
|
||||||
val JInt(bits) = block \ "bits"
|
val JInt(bits) = block \ "bits"
|
||||||
val JInt(nonce) = block \ "nonce"
|
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 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)
|
BlockHeaderAt(BlockHeight(height.toLong), header)
|
||||||
}))
|
}))
|
||||||
.map(headers => LatestHeaders(currentBlockHeight, headers.filter(_.blockHeight >= currentBlockHeight).toSet, name))
|
.map(headers => LatestHeaders(currentBlockHeight, headers.filter(_.blockHeight >= currentBlockHeight).toSet, name))
|
||||||
|
|
|
@ -22,8 +22,8 @@ import akka.actor.typed.scaladsl.adapter.TypedActorRefOps
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
import akka.actor.typed.{ActorRef, Behavior}
|
||||||
import akka.io.dns.{AAAARecord, DnsProtocol}
|
import akka.io.dns.{AAAARecord, DnsProtocol}
|
||||||
import akka.io.{Dns, IO}
|
import akka.io.{Dns, IO}
|
||||||
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32}
|
|
||||||
import fr.acinq.bitcoin.BlockHeader
|
import fr.acinq.bitcoin.BlockHeader
|
||||||
|
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash}
|
||||||
import fr.acinq.eclair.BlockHeight
|
import fr.acinq.eclair.BlockHeight
|
||||||
import fr.acinq.eclair.blockchain.watchdogs.BlockchainWatchdog.BlockHeaderAt
|
import fr.acinq.eclair.blockchain.watchdogs.BlockchainWatchdog.BlockHeaderAt
|
||||||
import fr.acinq.eclair.blockchain.watchdogs.Monitoring.{Metrics, Tags}
|
import fr.acinq.eclair.blockchain.watchdogs.Monitoring.{Metrics, Tags}
|
||||||
|
@ -46,7 +46,7 @@ object HeadersOverDns {
|
||||||
private case class WrappedDnsFailed(cause: Throwable) extends Command
|
private case class WrappedDnsFailed(cause: Throwable) extends Command
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
def apply(chainHash: ByteVector32, currentBlockHeight: BlockHeight): Behavior[Command] = {
|
def apply(chainHash: BlockHash, currentBlockHeight: BlockHeight): Behavior[Command] = {
|
||||||
Behaviors.setup { context =>
|
Behaviors.setup { context =>
|
||||||
val dnsAdapters = {
|
val dnsAdapters = {
|
||||||
context.messageAdapter[DnsProtocol.Resolved](WrappedDnsResolved)
|
context.messageAdapter[DnsProtocol.Resolved](WrappedDnsResolved)
|
||||||
|
@ -87,7 +87,7 @@ object HeadersOverDns {
|
||||||
stopOrCollect(replyTo, currentBlockHeight, received1, remaining - 1)
|
stopOrCollect(replyTo, currentBlockHeight, received1, remaining - 1)
|
||||||
|
|
||||||
case WrappedDnsFailed(ex) =>
|
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)
|
stopOrCollect(replyTo, currentBlockHeight, received, remaining - 1)
|
||||||
|
|
||||||
case _ => Behaviors.unhandled
|
case _ => Behaviors.unhandled
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.channel
|
||||||
|
|
||||||
import akka.actor.{ActorRef, PossiblyHarmful, typed}
|
import akka.actor.{ActorRef, PossiblyHarmful, typed}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
|
||||||
import fr.acinq.eclair.channel.LocalFundingStatus.DualFundedUnconfirmedFundingTx
|
import fr.acinq.eclair.channel.LocalFundingStatus.DualFundedUnconfirmedFundingTx
|
||||||
import fr.acinq.eclair.channel.fund.InteractiveTxBuilder._
|
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]
|
final case class RES_ADD_SETTLED[+O <: Origin, +R <: HtlcResult](origin: O, htlc: UpdateAddHtlc, result: R) extends CommandSuccess[CMD_ADD_HTLC]
|
||||||
|
|
||||||
/** other specific responses */
|
/** 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_BUMP_FUNDING_FEE(rbfIndex: Int, fundingTxId: TxId, 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_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_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_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]
|
final case class RES_GET_CHANNEL_INFO(nodeId: PublicKey, channelId: ByteVector32, channel: ActorRef, state: ChannelState, data: ChannelData) extends CommandSuccess[CMD_GET_CHANNEL_INFO]
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.channel
|
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.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.wire.protocol
|
import fr.acinq.eclair.wire.protocol
|
||||||
import fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, InteractiveTxMessage, UpdateAddHtlc}
|
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)
|
class ChannelException(val channelId: ByteVector32, message: String) extends RuntimeException(message)
|
||||||
|
|
||||||
// @formatter:off
|
// @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 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 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")
|
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 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 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 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 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: ByteVector32, previousTxOutput: Long) extends ChannelException(channelId, s"invalid 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: ByteVector32, previousTxOutput: Long, sequence: Long) extends ChannelException(channelId, s"$previousTxId:$previousTxOutput is not replaceable (serial_id=${serialId.toByteVector.toHex}, nSequence=$sequence)")
|
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: ByteVector32, previousTxOutput: Long) extends ChannelException(channelId, s"$previousTxId:$previousTxOutput is not a native segwit input (serial_id=${serialId.toByteVector.toHex})")
|
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 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 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})")
|
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 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 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 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 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 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")
|
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 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 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 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 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 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 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 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 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 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 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: ByteVector32) extends ChannelException(channelId, s"invalid htlc signature: txId=$txId")
|
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: ByteVector32) extends ChannelException(channelId, s"invalid close 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: ByteVector32) extends ChannelException(channelId, s"invalid closing tx: some outputs are below dust: 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 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 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")
|
case class ForcedLocalCommit (override val channelId: ByteVector32) extends ChannelException(channelId, s"forced local commit")
|
||||||
|
|
|
@ -3,7 +3,7 @@ package fr.acinq.eclair.channel
|
||||||
import akka.event.LoggingAdapter
|
import akka.event.LoggingAdapter
|
||||||
import com.softwaremill.quicklens.ModifyPimp
|
import com.softwaremill.quicklens.ModifyPimp
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.blockchain.fee.{FeeratePerByte, FeeratePerKw, FeeratesPerKw, OnChainFeeConf}
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing
|
import fr.acinq.eclair.channel.Helpers.Closing
|
||||||
import fr.acinq.eclair.channel.Monitoring.{Metrics, Tags}
|
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])
|
case class LocalCommit(index: Long, spec: CommitmentSpec, commitTxAndRemoteSig: CommitTxAndRemoteSig, htlcTxsAndRemoteSigs: List[HtlcTxAndRemoteSig])
|
||||||
|
|
||||||
object LocalCommit {
|
object LocalCommit {
|
||||||
def fromCommitSig(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxId: ByteVector32,
|
def fromCommitSig(keyManager: ChannelKeyManager, params: ChannelParams, fundingTxId: TxId,
|
||||||
fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo,
|
fundingTxIndex: Long, remoteFundingPubKey: PublicKey, commitInput: InputInfo,
|
||||||
commit: CommitSig, localCommitIndex: Long, spec: CommitmentSpec, localPerCommitmentPoint: PublicKey): Either[ChannelException, LocalCommit] = {
|
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)
|
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. */
|
/** 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 = {
|
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 (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)
|
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,
|
localFundingStatus: LocalFundingStatus, remoteFundingStatus: RemoteFundingStatus,
|
||||||
localCommit: LocalCommit, remoteCommit: RemoteCommit, nextRemoteCommit_opt: Option[NextRemoteCommit]) {
|
localCommit: LocalCommit, remoteCommit: RemoteCommit, nextRemoteCommit_opt: Option[NextRemoteCommit]) {
|
||||||
val commitInput: InputInfo = localCommit.commitTxAndRemoteSig.commitTx.input
|
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
|
val capacity: Satoshi = commitInput.txOut.amount
|
||||||
|
|
||||||
/** Channel reserve that applies to our funds. */
|
/** 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
|
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)
|
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
|
* @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.
|
* 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 {
|
all.find(_.fundingTxId == fundingTxId) match {
|
||||||
case Some(commitment) =>
|
case Some(commitment) =>
|
||||||
val commitments1 = copy(
|
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, _ => {
|
updateFundingStatus(fundingTxId, _ => {
|
||||||
case c if c.fundingTxId == fundingTxId =>
|
case c if c.fundingTxId == fundingTxId =>
|
||||||
log.info(s"setting localFundingStatus=${status.getClass.getSimpleName} for fundingTxId=${c.fundingTxId} fundingTxIndex=${c.fundingTxIndex}")
|
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
|
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 => {
|
updateFundingStatus(fundingTxId, fundingTxIndex => {
|
||||||
// all funding older than this one are considered locked
|
// all funding older than this one are considered locked
|
||||||
case c if c.fundingTxId == fundingTxId || c.fundingTxIndex < fundingTxIndex =>
|
case c if c.fundingTxId == fundingTxId || c.fundingTxIndex < fundingTxIndex =>
|
||||||
|
|
|
@ -352,7 +352,7 @@ object Helpers {
|
||||||
|
|
||||||
object Funding {
|
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 fundingScript = multiSig2of2(fundingPubkey1, fundingPubkey2)
|
||||||
val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript))
|
val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript))
|
||||||
InputInfo(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript))
|
InputInfo(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript))
|
||||||
|
@ -368,7 +368,7 @@ object Helpers {
|
||||||
localFundingAmount: Satoshi, remoteFundingAmount: Satoshi,
|
localFundingAmount: Satoshi, remoteFundingAmount: Satoshi,
|
||||||
localPushAmount: MilliSatoshi, remotePushAmount: MilliSatoshi,
|
localPushAmount: MilliSatoshi, remotePushAmount: MilliSatoshi,
|
||||||
commitTxFeerate: FeeratePerKw,
|
commitTxFeerate: FeeratePerKw,
|
||||||
fundingTxHash: ByteVector32, fundingTxOutputIndex: Int,
|
fundingTxId: TxId, fundingTxOutputIndex: Int,
|
||||||
remoteFundingPubKey: PublicKey,
|
remoteFundingPubKey: PublicKey,
|
||||||
remoteFirstPerCommitmentPoint: PublicKey): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx)] = {
|
remoteFirstPerCommitmentPoint: PublicKey): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx)] = {
|
||||||
makeCommitTxs(keyManager, params,
|
makeCommitTxs(keyManager, params,
|
||||||
|
@ -378,7 +378,7 @@ object Helpers {
|
||||||
localHtlcs = Set.empty,
|
localHtlcs = Set.empty,
|
||||||
commitTxFeerate,
|
commitTxFeerate,
|
||||||
fundingTxIndex = 0,
|
fundingTxIndex = 0,
|
||||||
fundingTxHash, fundingTxOutputIndex,
|
fundingTxId, fundingTxOutputIndex,
|
||||||
remoteFundingPubKey = remoteFundingPubKey, remotePerCommitmentPoint = remoteFirstPerCommitmentPoint,
|
remoteFundingPubKey = remoteFundingPubKey, remotePerCommitmentPoint = remoteFirstPerCommitmentPoint,
|
||||||
localCommitmentIndex = 0, remoteCommitmentIndex = 0).map {
|
localCommitmentIndex = 0, remoteCommitmentIndex = 0).map {
|
||||||
case (localSpec, localCommit, remoteSpec, remoteCommit, _) => (localSpec, localCommit, remoteSpec, remoteCommit)
|
case (localSpec, localCommit, remoteSpec, remoteCommit, _) => (localSpec, localCommit, remoteSpec, remoteCommit)
|
||||||
|
@ -396,7 +396,7 @@ object Helpers {
|
||||||
localHtlcs: Set[DirectedHtlc],
|
localHtlcs: Set[DirectedHtlc],
|
||||||
commitTxFeerate: FeeratePerKw,
|
commitTxFeerate: FeeratePerKw,
|
||||||
fundingTxIndex: Long,
|
fundingTxIndex: Long,
|
||||||
fundingTxHash: ByteVector32, fundingTxOutputIndex: Int,
|
fundingTxId: TxId, fundingTxOutputIndex: Int,
|
||||||
remoteFundingPubKey: PublicKey,
|
remoteFundingPubKey: PublicKey,
|
||||||
remotePerCommitmentPoint: PublicKey,
|
remotePerCommitmentPoint: PublicKey,
|
||||||
localCommitmentIndex: Long, remoteCommitmentIndex: Long): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx, Seq[HtlcTx])] = {
|
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 fundingPubKey = keyManager.fundingPublicKey(localParams.fundingKeyPath, fundingTxIndex)
|
||||||
val channelKeyPath = keyManager.keyPath(localParams, channelConfig)
|
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 localPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, localCommitmentIndex)
|
||||||
val (localCommitTx, _) = Commitment.makeLocalTxs(keyManager, channelConfig, channelFeatures, localCommitmentIndex, localParams, remoteParams, fundingTxIndex, remoteFundingPubKey, commitmentInput, localPerCommitmentPoint, localSpec)
|
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)
|
val (remoteCommitTx, htlcTxs) = Commitment.makeRemoteTxs(keyManager, channelConfig, channelFeatures, remoteCommitmentIndex, localParams, remoteParams, fundingTxIndex, remoteFundingPubKey, commitmentInput, remotePerCommitmentPoint, remoteSpec)
|
||||||
|
|
|
@ -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.actor.{Actor, ActorContext, ActorRef, FSM, OneForOneStrategy, PossiblyHarmful, Props, SupervisorStrategy, typed}
|
||||||
import akka.event.Logging.MDC
|
import akka.event.Logging.MDC
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.Logs.LogCategory
|
||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.blockchain.OnChainWallet.MakeFundingTxResponse
|
import fr.acinq.eclair.blockchain.OnChainWallet.MakeFundingTxResponse
|
||||||
|
@ -161,7 +161,7 @@ object Channel {
|
||||||
private[channel] sealed trait BitcoinEvent extends PossiblyHarmful
|
private[channel] sealed trait BitcoinEvent extends PossiblyHarmful
|
||||||
private[channel] case object BITCOIN_FUNDING_PUBLISH_FAILED extends BitcoinEvent
|
private[channel] case object BITCOIN_FUNDING_PUBLISH_FAILED extends BitcoinEvent
|
||||||
private[channel] case object BITCOIN_FUNDING_TIMEOUT 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
|
// @formatter:on
|
||||||
|
|
||||||
case object TickChannelOpenTimeout
|
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)
|
watchFundingConfirmed(w.tx.txid, Some(nodeParams.channelConf.minDepthBlocks), delay_opt = None)
|
||||||
maybeEmitEventsPostSplice(d.shortIds, d.commitments, commitments1)
|
maybeEmitEventsPostSplice(d.shortIds, d.commitments, commitments1)
|
||||||
maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, 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()
|
case Left(_) => stay()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,7 +1144,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
||||||
case Right((commitments1, commitment)) =>
|
case Right((commitments1, commitment)) =>
|
||||||
val toSend = if (d.commitments.all.exists(c => c.fundingTxId == commitment.fundingTxId && c.localFundingStatus.isInstanceOf[LocalFundingStatus.NotLocked])) {
|
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
|
// this commitment just moved from NotLocked to Locked
|
||||||
Some(SpliceLocked(d.channelId, w.tx.hash))
|
Some(SpliceLocked(d.channelId, w.tx.txid))
|
||||||
} else {
|
} else {
|
||||||
// this was a zero-conf splice and we already sent our splice_locked
|
// this was a zero-conf splice and we already sent our splice_locked
|
||||||
None
|
None
|
||||||
|
@ -1156,7 +1156,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
||||||
}
|
}
|
||||||
|
|
||||||
case Event(msg: SpliceLocked, d: DATA_NORMAL) =>
|
case Event(msg: SpliceLocked, d: DATA_NORMAL) =>
|
||||||
d.commitments.updateRemoteFundingStatus(msg.fundingTxid) match {
|
d.commitments.updateRemoteFundingStatus(msg.fundingTxId) match {
|
||||||
case Right((commitments1, _)) =>
|
case Right((commitments1, _)) =>
|
||||||
maybeEmitEventsPostSplice(d.shortIds, d.commitments, commitments1)
|
maybeEmitEventsPostSplice(d.shortIds, d.commitments, commitments1)
|
||||||
maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, commitments1)
|
maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, commitments1)
|
||||||
|
@ -1808,7 +1808,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
||||||
activeConnection = r
|
activeConnection = r
|
||||||
val channelKeyPath = keyManager.keyPath(d.channelParams.localParams, d.channelParams.channelConfig)
|
val channelKeyPath = keyManager.keyPath(d.channelParams.localParams, d.channelParams.channelConfig)
|
||||||
val myFirstPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, 0)
|
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(
|
val channelReestablish = ChannelReestablish(
|
||||||
channelId = d.channelId,
|
channelId = d.channelId,
|
||||||
nextLocalCommitmentNumber = 1,
|
nextLocalCommitmentNumber = 1,
|
||||||
|
@ -1828,16 +1828,16 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
||||||
val myCurrentPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, d.commitments.localCommitIndex)
|
val myCurrentPerCommitmentPoint = keyManager.commitmentPoint(channelKeyPath, d.commitments.localCommitIndex)
|
||||||
val rbfTlv: Set[ChannelReestablishTlv] = d match {
|
val rbfTlv: Set[ChannelReestablishTlv] = d match {
|
||||||
case d: DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED => d.rbfStatus 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 _ => 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 _: InteractiveTxBuilder.FullySignedSharedTransaction => Set.empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case d: DATA_NORMAL => d.spliceStatus match {
|
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 _ => 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
|
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
|
.filter(c => c.fundingTxIndex > 0) // only consider splice txs
|
||||||
.collectFirst { case c if c.localFundingStatus.isInstanceOf[LocalFundingStatus.Locked] =>
|
.collectFirst { case c if c.localFundingStatus.isInstanceOf[LocalFundingStatus.Locked] =>
|
||||||
log.debug("re-sending splice_locked for fundingTxId={}", c.fundingTxId)
|
log.debug("re-sending splice_locked for fundingTxId={}", c.fundingTxId)
|
||||||
SpliceLocked(d.channelId, c.fundingTxId.reverse)
|
SpliceLocked(d.channelId, c.fundingTxId)
|
||||||
}
|
}
|
||||||
sendQueue = sendQueue ++ spliceLocked
|
sendQueue = sendQueue ++ spliceLocked
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.fsm
|
||||||
import akka.actor.Status
|
import akka.actor.Status
|
||||||
import akka.actor.typed.scaladsl.adapter.actorRefAdapter
|
import akka.actor.typed.scaladsl.adapter.actorRefAdapter
|
||||||
import akka.pattern.pipe
|
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.OnChainWallet.MakeFundingTxResponse
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
|
||||||
import fr.acinq.eclair.channel.Helpers.Funding
|
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)) =>
|
case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), d@DATA_WAIT_FOR_FUNDING_INTERNAL(params, fundingAmount, pushMsat, commitTxFeerate, remoteFundingPubKey, remoteFirstPerCommitmentPoint, replyTo)) =>
|
||||||
val temporaryChannelId = params.channelId
|
val temporaryChannelId = params.channelId
|
||||||
// let's create the first commitment tx that spends the yet uncommitted funding tx
|
// 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 Left(ex) => handleLocalError(ex, d, None)
|
||||||
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
|
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
|
||||||
require(fundingTx.txOut(fundingTxOutputIndex).publicKeyScript == localCommitTx.input.txOut.publicKeyScript, s"pubkey script mismatch!")
|
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
|
// signature of their initial commitment tx that pays remote pushMsat
|
||||||
val fundingCreated = FundingCreated(
|
val fundingCreated = FundingCreated(
|
||||||
temporaryChannelId = temporaryChannelId,
|
temporaryChannelId = temporaryChannelId,
|
||||||
fundingTxHash = fundingTx.hash,
|
fundingTxId = fundingTx.txid,
|
||||||
fundingOutputIndex = fundingTxOutputIndex,
|
fundingOutputIndex = fundingTxOutputIndex,
|
||||||
signature = localSigOfRemoteTx
|
signature = localSigOfRemoteTx
|
||||||
)
|
)
|
||||||
val channelId = toLongId(fundingTx.hash, fundingTxOutputIndex)
|
val channelId = toLongId(fundingTx.txid, fundingTxOutputIndex)
|
||||||
val params1 = params.copy(channelId = channelId)
|
val params1 = params.copy(channelId = channelId)
|
||||||
peer ! ChannelIdAssigned(self, remoteNodeId, temporaryChannelId, channelId) // we notify the peer asap so it knows how to route messages
|
peer ! ChannelIdAssigned(self, remoteNodeId, temporaryChannelId, channelId) // we notify the peer asap so it knows how to route messages
|
||||||
txPublisher ! SetChannelId(remoteNodeId, channelId)
|
txPublisher ! SetChannelId(remoteNodeId, channelId)
|
||||||
|
@ -256,10 +256,10 @@ trait ChannelOpenSingleFunded extends SingleFundingHandlers with ErrorHandlers {
|
||||||
})
|
})
|
||||||
|
|
||||||
when(WAIT_FOR_FUNDING_CREATED)(handleExceptions {
|
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
|
val temporaryChannelId = params.channelId
|
||||||
// they fund the channel with their funding tx, so the money is theirs (but we are paid pushMsat)
|
// 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 Left(ex) => handleLocalError(ex, d, None)
|
||||||
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
|
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
|
||||||
// check remote signature validity
|
// 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 localSigOfLocalTx = keyManager.sign(localCommitTx, fundingPubKey, TxOwner.Local, params.commitmentFormat)
|
||||||
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, fundingPubKey.publicKey, remoteFundingPubKey, localSigOfLocalTx, remoteSig)
|
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, fundingPubKey.publicKey, remoteFundingPubKey, localSigOfLocalTx, remoteSig)
|
||||||
Transactions.checkSpendable(signedLocalCommitTx) match {
|
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(_) =>
|
case Success(_) =>
|
||||||
val localSigOfRemoteTx = keyManager.sign(remoteCommitTx, fundingPubKey, TxOwner.Remote, params.commitmentFormat)
|
val localSigOfRemoteTx = keyManager.sign(remoteCommitTx, fundingPubKey, TxOwner.Remote, params.commitmentFormat)
|
||||||
val channelId = toLongId(fundingTxHash, fundingTxOutputIndex)
|
val channelId = toLongId(fundingTxId, fundingTxOutputIndex)
|
||||||
val fundingSigned = FundingSigned(
|
val fundingSigned = FundingSigned(
|
||||||
channelId = channelId,
|
channelId = channelId,
|
||||||
signature = localSigOfRemoteTx
|
signature = localSigOfRemoteTx
|
||||||
|
|
|
@ -19,14 +19,13 @@ package fr.acinq.eclair.channel.fsm
|
||||||
import akka.actor.typed.scaladsl.adapter.{TypedActorRefOps, actorRefAdapter}
|
import akka.actor.typed.scaladsl.adapter.{TypedActorRefOps, actorRefAdapter}
|
||||||
import com.softwaremill.quicklens.ModifyPimp
|
import com.softwaremill.quicklens.ModifyPimp
|
||||||
import fr.acinq.bitcoin.ScriptFlags
|
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.ShortChannelId
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
|
||||||
import fr.acinq.eclair.channel.Helpers.getRelayFees
|
import fr.acinq.eclair.channel.Helpers.getRelayFees
|
||||||
import fr.acinq.eclair.channel.LocalFundingStatus.{ConfirmedFundingTx, DualFundedUnconfirmedFundingTx, SingleFundedUnconfirmedFundingTx}
|
import fr.acinq.eclair.channel.LocalFundingStatus.{ConfirmedFundingTx, DualFundedUnconfirmedFundingTx, SingleFundedUnconfirmedFundingTx}
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.channel.fsm.Channel.{ANNOUNCEMENTS_MINCONF, BroadcastChannelUpdate, PeriodicRefresh, REFRESH_CHANNEL_UPDATE_INTERVAL}
|
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 fr.acinq.eclair.wire.protocol.{AnnouncementSignatures, ChannelReady, ChannelReadyTlv, TlvStream}
|
||||||
|
|
||||||
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
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.
|
* @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 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)
|
val watch = WatchFundingSpent(self, commitment.commitInput.outPoint.txid, commitment.commitInput.outPoint.index.toInt, knownSpendingTxs)
|
||||||
delay_opt match {
|
delay_opt match {
|
||||||
|
@ -55,7 +54,7 @@ trait CommonFundingHandlers extends CommonHandlers {
|
||||||
/**
|
/**
|
||||||
* @param delay_opt optional delay to reduce herd effect at startup.
|
* @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 {
|
val watch = minDepth_opt match {
|
||||||
case Some(fundingMinDepth) => WatchFundingConfirmed(self, fundingTxId, fundingMinDepth)
|
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
|
// When using 0-conf, we make sure that the transaction was successfully published, otherwise there is a risk
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package fr.acinq.eclair.channel.fsm
|
package fr.acinq.eclair.channel.fsm
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.{Transaction, TxIn}
|
import fr.acinq.bitcoin.scalacompat.{Transaction, TxIn}
|
||||||
|
import fr.acinq.eclair.NotificationsLogger
|
||||||
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
||||||
import fr.acinq.eclair.blockchain.CurrentBlockHeight
|
import fr.acinq.eclair.blockchain.CurrentBlockHeight
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing
|
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._
|
||||||
import fr.acinq.eclair.channel.fund.{InteractiveTxBuilder, InteractiveTxSigningSession}
|
import fr.acinq.eclair.channel.fund.{InteractiveTxBuilder, InteractiveTxSigningSession}
|
||||||
import fr.acinq.eclair.wire.protocol.{ChannelReady, Error}
|
import fr.acinq.eclair.wire.protocol.{ChannelReady, Error}
|
||||||
import fr.acinq.eclair.{Features, NotificationsLogger}
|
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import akka.event.LoggingAdapter
|
||||||
import fr.acinq.bitcoin.ScriptFlags
|
import fr.acinq.bitcoin.ScriptFlags
|
||||||
import fr.acinq.bitcoin.psbt.Psbt
|
import fr.acinq.bitcoin.psbt.Psbt
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.OnChainChannelFunder
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
|
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
|
||||||
|
@ -308,13 +308,13 @@ object InteractiveTxBuilder {
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
sealed trait SignedSharedTransaction {
|
sealed trait SignedSharedTransaction {
|
||||||
def txId: ByteVector32
|
def txId: TxId
|
||||||
def tx: SharedTransaction
|
def tx: SharedTransaction
|
||||||
def localSigs: TxSignatures
|
def localSigs: TxSignatures
|
||||||
def signedTx_opt: Option[Transaction]
|
def signedTx_opt: Option[Transaction]
|
||||||
}
|
}
|
||||||
case class PartiallySignedSharedTransaction(tx: SharedTransaction, localSigs: TxSignatures) extends SignedSharedTransaction {
|
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
|
override val signedTx_opt: Option[Transaction] = None
|
||||||
}
|
}
|
||||||
case class FullySignedSharedTransaction(tx: SharedTransaction, localSigs: TxSignatures, remoteSigs: TxSignatures, sharedSigs_opt: Option[ScriptWitness]) extends SignedSharedTransaction {
|
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)
|
val outputs = (Seq(sharedTxOut) ++ localTxOut ++ remoteTxOut).sortBy(_._1).map(_._2)
|
||||||
Transaction(2, inputs, outputs, lockTime)
|
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)
|
override val signedTx_opt: Option[Transaction] = Some(signedTx)
|
||||||
val feerate: FeeratePerKw = Transactions.fee2rate(tx.fees, signedTx.weight())
|
val feerate: FeeratePerKw = Transactions.fee2rate(tx.fees, signedTx.weight())
|
||||||
}
|
}
|
||||||
|
@ -746,7 +746,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
|
||||||
localHtlcs = purpose.localHtlcs,
|
localHtlcs = purpose.localHtlcs,
|
||||||
purpose.commitTxFeerate,
|
purpose.commitTxFeerate,
|
||||||
fundingTxIndex = purpose.fundingTxIndex,
|
fundingTxIndex = purpose.fundingTxIndex,
|
||||||
fundingTx.hash, fundingOutputIndex,
|
fundingTx.txid, fundingOutputIndex,
|
||||||
remotePerCommitmentPoint = purpose.remotePerCommitmentPoint, remoteFundingPubKey = fundingParams.remoteFundingPubKey,
|
remotePerCommitmentPoint = purpose.remotePerCommitmentPoint, remoteFundingPubKey = fundingParams.remoteFundingPubKey,
|
||||||
localCommitmentIndex = purpose.localCommitIndex, remoteCommitmentIndex = purpose.remoteCommitIndex) match {
|
localCommitmentIndex = purpose.localCommitIndex, remoteCommitmentIndex = purpose.remoteCommitIndex) match {
|
||||||
case Left(cause) =>
|
case Left(cause) =>
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.publish
|
||||||
import akka.actor.typed.eventstream.EventStream
|
import akka.actor.typed.eventstream.EventStream
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
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.CurrentBlockHeight
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
||||||
import fr.acinq.eclair.channel.publish.TxPublisher.{TxPublishContext, TxRejectedReason}
|
import fr.acinq.eclair.channel.publish.TxPublisher.{TxPublishContext, TxRejectedReason}
|
||||||
|
@ -59,14 +59,14 @@ object MempoolTxMonitor {
|
||||||
sealed trait TxResult
|
sealed trait TxResult
|
||||||
sealed trait IntermediateTxResult extends TxResult
|
sealed trait IntermediateTxResult extends TxResult
|
||||||
/** The transaction is still unconfirmed and available in the mempool. */
|
/** 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. */
|
/** 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
|
sealed trait FinalTxResult extends TxResult
|
||||||
/** The transaction is confirmed and has reached min depth. */
|
/** The transaction is confirmed and has reached min depth. */
|
||||||
case class TxDeeplyBuried(tx: Transaction) extends FinalTxResult
|
case class TxDeeplyBuried(tx: Transaction) extends FinalTxResult
|
||||||
/** The transaction has been evicted from the mempool. */
|
/** 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
|
// @formatter:on
|
||||||
|
|
||||||
def apply(nodeParams: NodeParams, bitcoinClient: BitcoinCoreClient, txPublishContext: TxPublishContext): Behavior[Command] = {
|
def apply(nodeParams: NodeParams, bitcoinClient: BitcoinCoreClient, txPublishContext: TxPublishContext): Behavior[Command] = {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import akka.actor.typed.eventstream.EventStream
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
import akka.actor.typed.{ActorRef, Behavior}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.CurrentBlockHeight
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
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
|
* @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).
|
* 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 {
|
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. */
|
/** Publish an unsigned transaction that can be RBF-ed. */
|
||||||
case class PublishReplaceableTx(txInfo: ReplaceableTransactionWithInputInfo, commitment: FullCommitment) extends PublishTx {
|
case class PublishReplaceableTx(txInfo: ReplaceableTransactionWithInputInfo, commitment: FullCommitment) extends PublishTx {
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.channel.publish
|
||||||
import akka.actor.typed.eventstream.EventStream
|
import akka.actor.typed.eventstream.EventStream
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors, TimerScheduler}
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
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.CurrentBlockHeight
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchParentTxConfirmed, WatchParentTxConfirmedTriggered}
|
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
|
case class CheckTx(replyTo: ActorRef[TimeLocksOk], tx: Transaction, desc: String) extends Command
|
||||||
final case class WrappedCurrentBlockHeight(currentBlockHeight: BlockHeight) extends Command
|
final case class WrappedCurrentBlockHeight(currentBlockHeight: BlockHeight) extends Command
|
||||||
private case object CheckRelativeTimeLock 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
|
// @formatter:on
|
||||||
|
|
||||||
def apply(nodeParams: NodeParams, watcher: ActorRef[ZmqWatcher.Command], txPublishContext: TxPublishContext): Behavior[Command] = {
|
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 {
|
Behaviors.receiveMessagePartial {
|
||||||
case ParentTxConfirmed(parentTxId) =>
|
case ParentTxConfirmed(parentTxId) =>
|
||||||
log.debug("parent tx of {} has been confirmed (parent txid={})", cmd.desc, parentTxId)
|
log.debug("parent tx of {} has been confirmed (parent txid={})", cmd.desc, parentTxId)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.eventstream.EventStream
|
import akka.actor.typed.eventstream.EventStream
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.TimestampMilli
|
||||||
import fr.acinq.eclair.blockchain.NewBlock
|
import fr.acinq.eclair.blockchain.NewBlock
|
||||||
import fr.acinq.eclair.channel.ChannelSignatureReceived
|
import fr.acinq.eclair.channel.ChannelSignatureReceived
|
||||||
|
@ -47,7 +47,7 @@ object WeakEntropyPool {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
sealed trait Command
|
sealed trait Command
|
||||||
private case object FlushEntropy extends 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 WrappedPaymentRelayed(paymentHash: ByteVector32, relayedAt: TimestampMilli) extends Command
|
||||||
private case class WrappedPeerConnected(nodeId: PublicKey) extends Command
|
private case class WrappedPeerConnected(nodeId: PublicKey) extends Command
|
||||||
private case class WrappedChannelSignature(wtxid: ByteVector32) extends Command
|
private case class WrappedChannelSignature(wtxid: ByteVector32) extends Command
|
||||||
|
@ -56,7 +56,7 @@ object WeakEntropyPool {
|
||||||
|
|
||||||
def apply(collector: EntropyCollector): Behavior[Command] = {
|
def apply(collector: EntropyCollector): Behavior[Command] = {
|
||||||
Behaviors.setup { context =>
|
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[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[PeerConnected](e => WrappedPeerConnected(e.nodeId)))
|
||||||
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NodeUpdated](e => WrappedNodeUpdated(e.ann.signature)))
|
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter[NodeUpdated](e => WrappedNodeUpdated(e.ann.signature)))
|
||||||
|
@ -79,7 +79,7 @@ object WeakEntropyPool {
|
||||||
Behaviors.same
|
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)))
|
case WrappedPaymentRelayed(paymentHash, relayedAt) => collecting(collector, collect(entropy_opt, paymentHash ++ ByteVector.fromLong(relayedAt.toLong)))
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.crypto.keymanager
|
||||||
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet._
|
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.Generators
|
||||||
import fr.acinq.eclair.crypto.Monitoring.{Metrics, Tags}
|
import fr.acinq.eclair.crypto.Monitoring.{Metrics, Tags}
|
||||||
import fr.acinq.eclair.router.Announcements
|
import fr.acinq.eclair.router.Announcements
|
||||||
|
@ -31,7 +31,7 @@ import kamon.tag.TagSet
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
object LocalChannelKeyManager {
|
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.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
|
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
|
* @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 master = DeterministicWallet.generate(seed)
|
||||||
|
|
||||||
private val privateKeys: LoadingCache[KeyPath, ExtendedPrivateKey] = CacheBuilder.newBuilder()
|
private val privateKeys: LoadingCache[KeyPath, ExtendedPrivateKey] = CacheBuilder.newBuilder()
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.crypto.keymanager
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.ExtendedPrivateKey
|
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 fr.acinq.eclair.router.Announcements
|
||||||
import grizzled.slf4j.Logging
|
import grizzled.slf4j.Logging
|
||||||
import scodec.bits.ByteVector
|
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!!!
|
// 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
|
// 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
|
// 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.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
|
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
|
* @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)
|
private val master = DeterministicWallet.generate(seed)
|
||||||
|
|
||||||
override val nodeKey: ExtendedPrivateKey = DeterministicWallet.derivePrivateKey(master, LocalNodeKeyManager.keyBasePath(chainHash))
|
override val nodeKey: ExtendedPrivateKey = DeterministicWallet.derivePrivateKey(master, LocalNodeKeyManager.keyBasePath(chainHash))
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.crypto.keymanager
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import fr.acinq.bitcoin.psbt.{Psbt, UpdateFailure}
|
import fr.acinq.bitcoin.psbt.{Psbt, UpdateFailure}
|
||||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet._
|
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.TimestampSecond
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.{Descriptor, Descriptors}
|
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.{Descriptor, Descriptors}
|
||||||
import grizzled.slf4j.Logging
|
import grizzled.slf4j.Logging
|
||||||
|
@ -38,7 +38,7 @@ object LocalOnChainKeyManager extends Logging {
|
||||||
* @param chainHash chain we're on
|
* @param chainHash chain we're on
|
||||||
* @return a LocalOnChainKeyManager instance if a configuration file exists
|
* @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
|
// 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
|
// 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")
|
val file = new File(datadir, "eclair-signer.conf")
|
||||||
|
@ -63,7 +63,7 @@ object LocalOnChainKeyManager extends Logging {
|
||||||
* Eclair is in charge of signing transactions.
|
* Eclair is in charge of signing transactions.
|
||||||
* This is an advanced feature particularly suited when Eclair runs in a secure runtime.
|
* 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:
|
// 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
|
// - 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 master = DeterministicWallet.generate(seed)
|
||||||
private val fingerprint = DeterministicWallet.fingerprint(master) & 0xFFFFFFFFL
|
private val fingerprint = DeterministicWallet.fingerprint(master) & 0xFFFFFFFFL
|
||||||
private val fingerPrintHex = String.format("%8s", fingerprint.toHexString).replace(' ', '0')
|
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 {
|
private val rootPath = chainHash match {
|
||||||
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => "84'/1'"
|
case Block.RegtestGenesisBlock.hash | Block.TestnetGenesisBlock.hash | Block.SignetGenesisBlock.hash => "84h/1h"
|
||||||
case Block.LivenetGenesisBlock.hash => "84'/0'"
|
case Block.LivenetGenesisBlock.hash => "84h/0h"
|
||||||
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
|
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
|
||||||
}
|
}
|
||||||
private val rootKey = DeterministicWallet.derivePrivateKey(master, KeyPath(rootPath))
|
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 Block.LivenetGenesisBlock.hash => zpub
|
||||||
case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash")
|
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)))
|
val accountPub = DeterministicWallet.publicKey(DeterministicWallet.derivePrivateKey(rootKey, hardened(account)))
|
||||||
DeterministicWallet.encode(accountPub, prefix)
|
DeterministicWallet.encode(accountPub, prefix)
|
||||||
}
|
}
|
||||||
|
@ -98,16 +98,15 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
|
||||||
}
|
}
|
||||||
|
|
||||||
override def descriptors(account: Long): Descriptors = {
|
override def descriptors(account: Long): Descriptors = {
|
||||||
// TODO: we should use 'h' everywhere once bitcoin-kmp supports it.
|
val keyPath = s"$rootPath/${account}h"
|
||||||
val keyPath = s"$rootPath/$account'".replace('\'', 'h')
|
|
||||||
val prefix = chainHash match {
|
val prefix = chainHash match {
|
||||||
case Block.LivenetGenesisBlock.hash => xpub
|
case Block.LivenetGenesisBlock.hash => xpub
|
||||||
case _ => tpub
|
case _ => tpub
|
||||||
}
|
}
|
||||||
val accountPub = DeterministicWallet.publicKey(DeterministicWallet.derivePrivateKey(rootKey, hardened(account)))
|
val accountPub = DeterministicWallet.publicKey(DeterministicWallet.derivePrivateKey(rootKey, hardened(account)))
|
||||||
// descriptors for account 0 are:
|
// descriptors for account 0 are:
|
||||||
// 84'/{0'/1'}/0'/0/* for main addresses
|
// 84h/{0h/1h}/0h/0/* for main addresses
|
||||||
// 84'/{0'/1'}/0'/1/* for change addresses
|
// 84h/{0h/1h}/0h/1/* for change addresses
|
||||||
val receiveDesc = s"wpkh([$fingerPrintHex/$keyPath]${encode(accountPub, prefix)}/0/*)"
|
val receiveDesc = s"wpkh([$fingerPrintHex/$keyPath]${encode(accountPub, prefix)}/0/*)"
|
||||||
val changeDesc = s"wpkh([$fingerPrintHex/$keyPath]${encode(accountPub, prefix)}/1/*)"
|
val changeDesc = s"wpkh([$fingerPrintHex/$keyPath]${encode(accountPub, prefix)}/1/*)"
|
||||||
Descriptors(wallet_name = walletName, descriptors = List(
|
Descriptors(wallet_name = walletName, descriptors = List(
|
||||||
|
@ -120,7 +119,7 @@ class LocalOnChainKeyManager(override val walletName: String, seed: ByteVector,
|
||||||
for {
|
for {
|
||||||
spent <- spentAmount(psbt, ourInputs)
|
spent <- spentAmount(psbt, ourInputs)
|
||||||
change <- changeAmount(psbt, ourOutputs)
|
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 {
|
_ <- Try {
|
||||||
ourOutputs.foreach(i => require(isOurOutput(psbt, i), s"could not verify output $i: bitcoin core may be malicious"))
|
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 {
|
private def changeAmount(psbt: Psbt, ourOutputs: Seq[Int]): Try[Satoshi] = Try {
|
||||||
ourOutputs.map(i => {
|
ourOutputs.map(i => {
|
||||||
require(psbt.getGlobal.getTx.txOut.size() > i, s"output $i is missing from psbt: bitcoin core may be malicious")
|
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.getGlobal.getTx.txOut.get(i).amount)
|
fr.acinq.bitcoin.scalacompat.KotlinUtils.kmp2scala(psbt.global.tx.txOut.get(i).amount)
|
||||||
}).sum
|
}).sum
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check that an output belongs to us (i.e. we can recompute its public key from its bip32 path). */
|
/** 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 = {
|
private def isOurOutput(psbt: Psbt, outputIndex: Int): Boolean = {
|
||||||
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
|
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
|
return false
|
||||||
}
|
}
|
||||||
val output = psbt.getOutputs.get(outputIndex)
|
val output = psbt.outputs.get(outputIndex)
|
||||||
val txOut = psbt.getGlobal.getTx.txOut.get(outputIndex)
|
val txOut = psbt.global.tx.txOut.get(outputIndex)
|
||||||
output.getDerivationPaths.asScala.headOption match {
|
output.getDerivationPaths.asScala.headOption match {
|
||||||
case Some((pub, keypath)) =>
|
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))
|
val expectedScript = Script.write(Script.pay2wpkh(expectedPubKey))
|
||||||
if (expectedPubKey != kmp2scala(pub)) {
|
if (expectedPubKey != kmp2scala(pub)) {
|
||||||
logger.warn(s"public key mismatch (expected=$expectedPubKey, actual=$pub): bitcoin core may be malicious")
|
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.
|
// 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.
|
// 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 != 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.txid == psbt.global.tx.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.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.
|
// We must use SIGHASH_ALL, otherwise we would be vulnerable to "signature reuse" attacks.
|
||||||
// When unspecified, the sighash used will be SIGHASH_ALL.
|
// 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.
|
// 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")
|
require(input.getDerivationPaths.size() == 1, "bip32 derivation path is missing: bitcoin core may be malicious")
|
||||||
val (pub, keypath) = input.getDerivationPaths.asScala.toSeq.head
|
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")
|
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)))
|
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")
|
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.
|
// 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 signed = updated.flatMap(_.sign(priv, pos))
|
||||||
val finalized = signed.flatMap(s => {
|
val finalized = signed.flatMap(s => {
|
||||||
require(s.getSig.last.toInt == SigHash.SIGHASH_ALL, "signature must end with SIGHASH_ALL")
|
require(s.getSig.last.toInt == SigHash.SIGHASH_ALL, "signature must end with SIGHASH_ALL")
|
||||||
|
|
|
@ -2,7 +2,7 @@ package fr.acinq.eclair.db
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.channel._
|
||||||
import fr.acinq.eclair.db.Databases.{FileBackup, PostgresDatabases, SqliteDatabases}
|
import fr.acinq.eclair.db.Databases.{FileBackup, PostgresDatabases, SqliteDatabases}
|
||||||
import fr.acinq.eclair.db.DbEventHandler.ChannelEvent
|
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.payment.relay.Relayer.RelayFees
|
||||||
import fr.acinq.eclair.router.Router
|
import fr.acinq.eclair.router.Router
|
||||||
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelUpdate, NodeAddress, NodeAnnouncement}
|
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 grizzled.slf4j.Logging
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -99,7 +99,7 @@ case class DualNetworkDb(primary: NetworkDb, secondary: NetworkDb) extends Netwo
|
||||||
primary.listNodes()
|
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))
|
runAsync(secondary.addChannel(c, txid, capacity))
|
||||||
primary.addChannel(c, txid, capacity)
|
primary.addChannel(c, txid, capacity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.db
|
package fr.acinq.eclair.db
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.router.Router.PublicChannel
|
||||||
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement}
|
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement}
|
||||||
import fr.acinq.eclair.{RealShortChannelId, ShortChannelId}
|
import fr.acinq.eclair.{RealShortChannelId, ShortChannelId}
|
||||||
|
@ -36,7 +36,7 @@ trait NetworkDb {
|
||||||
|
|
||||||
def listNodes(): Seq[NodeAnnouncement]
|
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
|
def updateChannel(u: ChannelUpdate): Unit
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
override def add(e: TransactionPublished): Unit = withMetrics("audit/add-transaction-published", DbBackends.Postgres) {
|
||||||
inTransaction { pg =>
|
inTransaction { pg =>
|
||||||
using(pg.prepareStatement("INSERT INTO audit.transactions_published VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING")) { statement =>
|
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(2, e.channelId.toHex)
|
||||||
statement.setString(3, e.remoteNodeId.value.toHex)
|
statement.setString(3, e.remoteNodeId.value.toHex)
|
||||||
statement.setLong(4, e.miningFee.toLong)
|
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) {
|
override def add(e: TransactionConfirmed): Unit = withMetrics("audit/add-transaction-confirmed", DbBackends.Postgres) {
|
||||||
inTransaction { pg =>
|
inTransaction { pg =>
|
||||||
using(pg.prepareStatement("INSERT INTO audit.transactions_confirmed VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING")) { statement =>
|
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(2, e.channelId.toHex)
|
||||||
statement.setString(3, e.remoteNodeId.value.toHex)
|
statement.setString(3, e.remoteNodeId.value.toHex)
|
||||||
statement.setTimestamp(4, Timestamp.from(Instant.now()))
|
statement.setTimestamp(4, Timestamp.from(Instant.now()))
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.db.pg
|
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.Metrics.withMetrics
|
||||||
import fr.acinq.eclair.db.Monitoring.Tags.DbBackends
|
import fr.acinq.eclair.db.Monitoring.Tags.DbBackends
|
||||||
import fr.acinq.eclair.db.NetworkDb
|
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 =>
|
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")) {
|
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 =>
|
||||||
statement.setLong(1, c.shortChannelId.toLong)
|
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.setBytes(3, channelAnnouncementCodec.encode(c).require.toByteArray)
|
||||||
statement.setLong(4, capacity.toLong)
|
statement.setLong(4, capacity.toLong)
|
||||||
statement.setString(5, serialization.write(c))
|
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 = {
|
private def parseChannel(rs: ResultSet): PublicChannel = {
|
||||||
val ann = channelAnnouncementCodec.decode(rs.getBitVectorOpt("channel_announcement").get).require.value
|
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 capacity = rs.getLong("capacity_sat")
|
||||||
val channel_update_1_opt = rs.getBitVectorOpt("channel_update_1").map(channelUpdateCodec.decode(_).require.value)
|
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)
|
val channel_update_2_opt = rs.getBitVectorOpt("channel_update_2").map(channelUpdateCodec.decode(_).require.value)
|
||||||
|
|
|
@ -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) {
|
override def add(e: TransactionPublished): Unit = withMetrics("audit/add-transaction-published", DbBackends.Sqlite) {
|
||||||
using(sqlite.prepareStatement("INSERT OR IGNORE INTO transactions_published VALUES (?, ?, ?, ?, ?, ?)")) { statement =>
|
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(2, e.channelId.toArray)
|
||||||
statement.setBytes(3, e.remoteNodeId.value.toArray)
|
statement.setBytes(3, e.remoteNodeId.value.toArray)
|
||||||
statement.setLong(4, e.miningFee.toLong)
|
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) {
|
override def add(e: TransactionConfirmed): Unit = withMetrics("audit/add-transaction-confirmed", DbBackends.Sqlite) {
|
||||||
using(sqlite.prepareStatement("INSERT OR IGNORE INTO transactions_confirmed VALUES (?, ?, ?, ?)")) { statement =>
|
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(2, e.channelId.toArray)
|
||||||
statement.setBytes(3, e.remoteNodeId.value.toArray)
|
statement.setBytes(3, e.remoteNodeId.value.toArray)
|
||||||
statement.setLong(4, TimestampMilli.now().toLong)
|
statement.setLong(4, TimestampMilli.now().toLong)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.db.sqlite
|
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.Metrics.withMetrics
|
||||||
import fr.acinq.eclair.db.Monitoring.Tags.DbBackends
|
import fr.acinq.eclair.db.Monitoring.Tags.DbBackends
|
||||||
import fr.acinq.eclair.db.NetworkDb
|
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 =>
|
using(sqlite.prepareStatement("INSERT OR IGNORE INTO channels VALUES (?, ?, ?, ?, NULL, NULL)")) { statement =>
|
||||||
statement.setLong(1, c.shortChannelId.toLong)
|
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.setBytes(3, channelAnnouncementCodec.encode(c).require.toByteArray)
|
||||||
statement.setLong(4, capacity.toLong)
|
statement.setLong(4, capacity.toLong)
|
||||||
statement.executeUpdate()
|
statement.executeUpdate()
|
||||||
|
@ -136,7 +136,7 @@ class SqliteNetworkDb(val sqlite: Connection) extends NetworkDb with Logging {
|
||||||
|
|
||||||
private def parseChannel(rs: ResultSet): PublicChannel = {
|
private def parseChannel(rs: ResultSet): PublicChannel = {
|
||||||
val ann = channelAnnouncementCodec.decode(rs.getBitVectorOpt("channel_announcement").get).require.value
|
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 capacity = rs.getLong("capacity_sat")
|
||||||
val channel_update_1_opt = rs.getBitVectorOpt("channel_update_1").map(channelUpdateCodec.decode(_).require.value)
|
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)
|
val channel_update_2_opt = rs.getBitVectorOpt("channel_update_2").map(channelUpdateCodec.decode(_).require.value)
|
||||||
|
|
|
@ -23,7 +23,7 @@ import akka.event.Logging.MDC
|
||||||
import akka.event.{BusLogging, DiagnosticLoggingAdapter}
|
import akka.event.{BusLogging, DiagnosticLoggingAdapter}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.Logs.LogCategory
|
||||||
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
||||||
import fr.acinq.eclair._
|
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
|
* double-spend the funding transaction. Callers must wait for on-chain confirmations if they want guarantees that
|
||||||
* the channel has been opened.
|
* 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 class Rejected(reason: String) extends OpenChannelResponse { override def toString = reason }
|
||||||
case object Cancelled extends OpenChannelResponse { override def toString = "channel creation cancelled" }
|
case object Cancelled extends OpenChannelResponse { override def toString = "channel creation cancelled" }
|
||||||
case object Disconnected extends OpenChannelResponse { override def toString = "disconnected" }
|
case object Disconnected extends OpenChannelResponse { override def toString = "disconnected" }
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.io
|
||||||
|
|
||||||
import akka.actor.{ActorRef, FSM, OneForOneStrategy, PoisonPill, Props, Stash, SupervisorStrategy, Terminated}
|
import akka.actor.{ActorRef, FSM, OneForOneStrategy, PoisonPill, Props, Stash, SupervisorStrategy, Terminated}
|
||||||
import akka.event.Logging.MDC
|
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.bitcoin.scalacompat.Crypto.PublicKey
|
||||||
import fr.acinq.eclair.Logs.LogCategory
|
import fr.acinq.eclair.Logs.LogCategory
|
||||||
import fr.acinq.eclair.crypto.Noise.KeyPair
|
import fr.acinq.eclair.crypto.Noise.KeyPair
|
||||||
|
@ -555,8 +555,8 @@ object PeerConnection {
|
||||||
case object Nothing extends Data
|
case object Nothing extends Data
|
||||||
case class AuthenticatingData(pendingAuth: PendingAuth, transport: ActorRef, isPersistent: Boolean) extends Data with HasTransport
|
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 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 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: 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 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 ExpectedPong(ping: Ping, timestamp: TimestampMilli = TimestampMilli.now())
|
||||||
case class PingTimeout(ping: Ping)
|
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
|
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 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
|
case class ConnectionReady(peerConnection: ActorRef, remoteNodeId: PublicKey, address: NodeAddress, outgoing: Boolean, localInit: protocol.Init, remoteInit: protocol.Init) extends RemoteTypes
|
||||||
|
|
||||||
sealed trait ConnectionResult extends RemoteTypes
|
sealed trait ConnectionResult extends RemoteTypes
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.json
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
|
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.balance.CheckBalance.{CorrectedOnChainBalance, GlobalBalance, OffChainBalance}
|
||||||
import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
|
import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
|
@ -118,6 +118,34 @@ object ByteVector32KmpSerializer extends MinimalSerializer({
|
||||||
case x: fr.acinq.bitcoin.ByteVector32 => JString(x.toHex)
|
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({
|
object ByteVector64Serializer extends MinimalSerializer({
|
||||||
case x: ByteVector64 => JString(x.toHex)
|
case x: ByteVector64 => JString(x.toHex)
|
||||||
})
|
})
|
||||||
|
@ -217,7 +245,7 @@ object CommandResponseSerializer extends MinimalSerializer({
|
||||||
|
|
||||||
object TransactionSerializer extends MinimalSerializer({
|
object TransactionSerializer extends MinimalSerializer({
|
||||||
case x: Transaction => JObject(List(
|
case x: Transaction => JObject(List(
|
||||||
JField("txid", JString(x.txid.toHex)),
|
JField("txid", JString(x.txid.value.toHex)),
|
||||||
JField("tx", JString(x.toString()))
|
JField("tx", JString(x.toString()))
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
@ -228,34 +256,34 @@ object KeyPathSerializer extends MinimalSerializer({
|
||||||
|
|
||||||
object TransactionWithInputInfoSerializer extends MinimalSerializer({
|
object TransactionWithInputInfoSerializer extends MinimalSerializer({
|
||||||
case x: HtlcSuccessTx => JObject(List(
|
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("tx", JString(x.tx.toString())),
|
||||||
JField("paymentHash", JString(x.paymentHash.toString())),
|
JField("paymentHash", JString(x.paymentHash.toString())),
|
||||||
JField("htlcId", JLong(x.htlcId)),
|
JField("htlcId", JLong(x.htlcId)),
|
||||||
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
||||||
))
|
))
|
||||||
case x: HtlcTimeoutTx => JObject(List(
|
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("tx", JString(x.tx.toString())),
|
||||||
JField("htlcId", JLong(x.htlcId)),
|
JField("htlcId", JLong(x.htlcId)),
|
||||||
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
||||||
))
|
))
|
||||||
case x: ClaimHtlcSuccessTx => JObject(List(
|
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("tx", JString(x.tx.toString())),
|
||||||
JField("paymentHash", JString(x.paymentHash.toString())),
|
JField("paymentHash", JString(x.paymentHash.toString())),
|
||||||
JField("htlcId", JLong(x.htlcId)),
|
JField("htlcId", JLong(x.htlcId)),
|
||||||
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
||||||
))
|
))
|
||||||
case x: ClaimHtlcTx => JObject(List(
|
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("tx", JString(x.tx.toString())),
|
||||||
JField("htlcId", JLong(x.htlcId)),
|
JField("htlcId", JLong(x.htlcId)),
|
||||||
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
JField("confirmBeforeBlock", JLong(x.confirmationTarget.confirmBefore.toLong))
|
||||||
))
|
))
|
||||||
case x: ClosingTx =>
|
case x: ClosingTx =>
|
||||||
val txFields = List(
|
val txFields = List(
|
||||||
JField("txid", JString(x.tx.txid.toHex)),
|
JField("txid", JString(x.tx.txid.value.toHex)),
|
||||||
JField("tx", JString(x.tx.toString()))
|
JField("tx", JString(x.tx.toString()))
|
||||||
)
|
)
|
||||||
x.toLocalOutput match {
|
x.toLocalOutput match {
|
||||||
|
@ -269,7 +297,7 @@ object TransactionWithInputInfoSerializer extends MinimalSerializer({
|
||||||
case None => JObject(txFields)
|
case None => JObject(txFields)
|
||||||
}
|
}
|
||||||
case x: ReplaceableTransactionWithInputInfo => JObject(List(
|
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())),
|
JField("tx", JString(x.tx.toString())),
|
||||||
x.confirmationTarget match {
|
x.confirmationTarget match {
|
||||||
case ConfirmationTarget.Absolute(confirmBefore) => JField("confirmBeforeBlock", JLong(confirmBefore.toLong))
|
case ConfirmationTarget.Absolute(confirmBefore) => JField("confirmBeforeBlock", JLong(confirmBefore.toLong))
|
||||||
|
@ -278,7 +306,7 @@ object TransactionWithInputInfoSerializer extends MinimalSerializer({
|
||||||
|
|
||||||
))
|
))
|
||||||
case x: TransactionWithInputInfo => JObject(List(
|
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()))
|
JField("tx", JString(x.tx.toString()))
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
@ -528,7 +556,7 @@ object ShortIdsSerializer extends ConvertClassSerializer[ShortIds](s => ShortIds
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
// @formatter:off
|
// @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]({
|
object FundingTxStatusSerializer extends ConvertClassSerializer[LocalFundingStatus]({
|
||||||
case s: LocalFundingStatus.UnconfirmedFundingTx => FundingTxStatusJson("unconfirmed", s.signedTx_opt.map(_.txid))
|
case s: LocalFundingStatus.UnconfirmedFundingTx => FundingTxStatusJson("unconfirmed", s.signedTx_opt.map(_.txid))
|
||||||
case s: LocalFundingStatus.ConfirmedFundingTx => FundingTxStatusJson("confirmed", s.signedTx_opt.map(_.txid))
|
case s: LocalFundingStatus.ConfirmedFundingTx => FundingTxStatusJson("confirmed", s.signedTx_opt.map(_.txid))
|
||||||
|
@ -634,6 +662,9 @@ object JsonSerializers {
|
||||||
TypedActorRefSerializer +
|
TypedActorRefSerializer +
|
||||||
ByteVectorSerializer +
|
ByteVectorSerializer +
|
||||||
ByteVector32Serializer +
|
ByteVector32Serializer +
|
||||||
|
TxIdSerializer +
|
||||||
|
BlockIdSerializer +
|
||||||
|
BlockHashSerializer +
|
||||||
ByteVector64Serializer +
|
ByteVector64Serializer +
|
||||||
ChannelEventSerializer +
|
ChannelEventSerializer +
|
||||||
UInt64Serializer +
|
UInt64Serializer +
|
||||||
|
@ -676,6 +707,7 @@ object JsonSerializers {
|
||||||
JavaUUIDSerializer +
|
JavaUUIDSerializer +
|
||||||
OriginSerializer +
|
OriginSerializer +
|
||||||
ByteVector32KeySerializer +
|
ByteVector32KeySerializer +
|
||||||
|
TxIdKeySerializer +
|
||||||
GlobalBalanceSerializer +
|
GlobalBalanceSerializer +
|
||||||
PeerInfoSerializer +
|
PeerInfoSerializer +
|
||||||
PaymentFailedSummarySerializer +
|
PaymentFailedSummarySerializer +
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package fr.acinq
|
package fr.acinq
|
||||||
|
|
||||||
import fr.acinq.bitcoin.Bitcoin
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
|
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
|
||||||
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
|
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
|
||||||
import fr.acinq.bitcoin.scalacompat._
|
import fr.acinq.bitcoin.scalacompat._
|
||||||
|
@ -25,8 +24,6 @@ import fr.acinq.eclair.payment.relay.Relayer.RelayFees
|
||||||
import scodec.Attempt
|
import scodec.Attempt
|
||||||
import scodec.bits.{BitVector, ByteVector}
|
import scodec.bits.{BitVector, ByteVector}
|
||||||
|
|
||||||
import scala.jdk.CollectionConverters.CollectionHasAsScala
|
|
||||||
|
|
||||||
package object eclair {
|
package object eclair {
|
||||||
|
|
||||||
val randomGen = new StrongRandom()
|
val randomGen = new StrongRandom()
|
||||||
|
@ -45,8 +42,9 @@ package object eclair {
|
||||||
|
|
||||||
def randomLong(): Long = randomGen.nextLong()
|
def randomLong(): Long = randomGen.nextLong()
|
||||||
|
|
||||||
def toLongId(fundingTxHash: ByteVector32, fundingOutputIndex: Int): ByteVector32 = {
|
def toLongId(fundingTxId: TxId, fundingOutputIndex: Int): ByteVector32 = {
|
||||||
require(fundingOutputIndex < 65536, "fundingOutputIndex must not be greater than FFFF")
|
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)
|
val channelId = ByteVector32(fundingTxHash.take(30) :+ (fundingTxHash(30) ^ (fundingOutputIndex >> 8)).toByte :+ (fundingTxHash(31) ^ fundingOutputIndex).toByte)
|
||||||
channelId
|
channelId
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.payment
|
package fr.acinq.eclair.payment
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.bitcoin.{Base58, Base58Check, Bech32}
|
||||||
import fr.acinq.eclair.{Bolt11Feature, CltvExpiryDelta, Feature, FeatureSupport, Features, InvoiceFeature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TimestampSecond, randomBytes32}
|
import fr.acinq.eclair.{Bolt11Feature, CltvExpiryDelta, Feature, FeatureSupport, Features, InvoiceFeature, MilliSatoshi, MilliSatoshiLong, ShortChannelId, TimestampSecond, randomBytes32}
|
||||||
import scodec.bits.{BitVector, ByteOrdering, ByteVector}
|
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))
|
val defaultFeatures: Features[Bolt11Feature] = Features((Features.VariableLengthOnion, FeatureSupport.Mandatory), (Features.PaymentSecret, FeatureSupport.Mandatory))
|
||||||
|
|
||||||
def apply(chainHash: ByteVector32,
|
def apply(chainHash: BlockHash,
|
||||||
amount: Option[MilliSatoshi],
|
amount: Option[MilliSatoshi],
|
||||||
paymentHash: ByteVector32,
|
paymentHash: ByteVector32,
|
||||||
privateKey: PrivateKey,
|
privateKey: PrivateKey,
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.payment
|
||||||
|
|
||||||
import fr.acinq.bitcoin.Bech32
|
import fr.acinq.bitcoin.Bech32
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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
|
||||||
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute
|
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute
|
||||||
import fr.acinq.eclair.wire.protocol.OfferTypes._
|
import fr.acinq.eclair.wire.protocol.OfferTypes._
|
||||||
|
@ -191,7 +191,7 @@ object MinimalBolt12Invoice {
|
||||||
val hrp = "lndi"
|
val hrp = "lndi"
|
||||||
|
|
||||||
def apply(offer: Offer,
|
def apply(offer: Offer,
|
||||||
chain: ByteVector32,
|
chain: BlockHash,
|
||||||
amount: MilliSatoshi,
|
amount: MilliSatoshi,
|
||||||
quantity: Long,
|
quantity: Long,
|
||||||
paymentHash: ByteVector32,
|
paymentHash: ByteVector32,
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.payment.send
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
|
||||||
import akka.actor.{ActorRef, typed}
|
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.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
||||||
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute
|
import fr.acinq.eclair.crypto.Sphinx.RouteBlinding.BlindedRoute
|
||||||
import fr.acinq.eclair.message.Postman.{OnionMessageResponse, SendMessage}
|
import fr.acinq.eclair.message.Postman.{OnionMessageResponse, SendMessage}
|
||||||
|
@ -40,7 +40,7 @@ object OfferPayment {
|
||||||
|
|
||||||
case class UnsupportedFeatures(features: Features[InvoiceFeature]) extends Failure
|
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
|
case class ExpiredOffer(expiryDate: TimestampSecond) extends Failure
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ object EclairInternalsSerializer {
|
||||||
|
|
||||||
def initializeConnectionCodec(system: ExtendedActorSystem): Codec[PeerConnection.InitializeConnection] = (
|
def initializeConnectionCodec(system: ExtendedActorSystem): Codec[PeerConnection.InitializeConnection] = (
|
||||||
("peer" | actorRefCodec(system)) ::
|
("peer" | actorRefCodec(system)) ::
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("features" | variableSizeBytes(uint16, initFeaturesCodec)) ::
|
("features" | variableSizeBytes(uint16, initFeaturesCodec)) ::
|
||||||
("doSync" | bool(8))).as[PeerConnection.InitializeConnection]
|
("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])))
|
val optionQueryChannelRangeTlv: Codec[Option[QueryChannelRangeTlv]] = variableSizeBytes(uint16, optional(bool(8), variableSizeBytesLong(varintoverflow, queryFlagsCodec.upcast[QueryChannelRangeTlv])))
|
||||||
|
|
||||||
def sendChannelQueryCodec(system: ExtendedActorSystem): Codec[SendChannelQuery] = (
|
def sendChannelQueryCodec(system: ExtendedActorSystem): Codec[SendChannelQuery] = (
|
||||||
("chainsHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("remoteNodeId" | publicKey) ::
|
("remoteNodeId" | publicKey) ::
|
||||||
("to" | actorRefCodec(system)) ::
|
("to" | actorRefCodec(system)) ::
|
||||||
("replacePrevious" | bool(8)) ::
|
("replacePrevious" | bool(8)) ::
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.router
|
package fr.acinq.eclair.router
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, sha256, verifySignature}
|
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.wire.protocol._
|
||||||
import fr.acinq.eclair.{CltvExpiryDelta, Feature, Features, MilliSatoshi, NodeFeature, RealShortChannelId, ShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult}
|
import fr.acinq.eclair.{CltvExpiryDelta, Feature, Features, MilliSatoshi, NodeFeature, RealShortChannelId, ShortChannelId, TimestampSecond, TimestampSecondLong, serializationResult}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
@ -28,16 +28,16 @@ import shapeless.HNil
|
||||||
*/
|
*/
|
||||||
object Announcements {
|
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))))
|
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 =
|
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))))
|
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))))
|
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)) {
|
if (isNode1(localNodeId, remoteNodeId)) {
|
||||||
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, features, TlvStream.empty)
|
channelAnnouncementWitnessEncode(chainHash, shortChannelId, localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, features, TlvStream.empty)
|
||||||
} else {
|
} else {
|
||||||
|
@ -46,7 +46,7 @@ object Announcements {
|
||||||
|
|
||||||
def signChannelAnnouncement(witness: ByteVector, key: PrivateKey): ByteVector64 = Crypto.sign(witness, key)
|
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) =
|
val (nodeId1, nodeId2, bitcoinKey1, bitcoinKey2, nodeSignature1, nodeSignature2, bitcoinSignature1, bitcoinSignature2) =
|
||||||
if (isNode1(localNodeId, remoteNodeId)) {
|
if (isNode1(localNodeId, remoteNodeId)) {
|
||||||
(localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, localNodeSignature, remoteNodeSignature, localBitcoinSignature, remoteBitcoinSignature)
|
(localNodeId, remoteNodeId, localFundingKey, remoteFundingKey, localNodeSignature, remoteNodeSignature, localBitcoinSignature, remoteBitcoinSignature)
|
||||||
|
@ -118,7 +118,7 @@ object Announcements {
|
||||||
u1.htlcMinimumMsat == u2.htlcMinimumMsat &&
|
u1.htlcMinimumMsat == u2.htlcMinimumMsat &&
|
||||||
u1.htlcMaximumMsat == u2.htlcMaximumMsat
|
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 messageFlags = ChannelUpdate.MessageFlags(isPrivate)
|
||||||
val channelFlags = ChannelUpdate.ChannelFlags(isNode1 = isNode1(nodeSecret.publicKey, remoteNodeId), isEnabled = enable)
|
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)
|
val witness = channelUpdateWitnessEncode(chainHash, shortChannelId, timestamp, messageFlags, channelFlags, cltvExpiryDelta, htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, htlcMaximumMsat, TlvStream.empty)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import akka.actor.{Actor, ActorLogging, ActorRef, Cancellable, Props, Terminated
|
||||||
import akka.event.DiagnosticLoggingAdapter
|
import akka.event.DiagnosticLoggingAdapter
|
||||||
import akka.event.Logging.MDC
|
import akka.event.Logging.MDC
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.Logs.LogCategory
|
||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
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
|
// 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
|
// 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 =>
|
(channels.values ++ pruned.values).foreach { pc =>
|
||||||
val txid = pc.fundingTxid
|
val txid = pc.fundingTxId
|
||||||
val outputIndex = ShortChannelId.coordinates(pc.ann.shortChannelId).outputIndex
|
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
|
// 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) {
|
context.system.scheduler.scheduleOnce(Random.nextLong(nodeParams.routerConf.watchSpentWindow.toSeconds).seconds) {
|
||||||
|
@ -395,7 +395,7 @@ object Router {
|
||||||
def updateBalances(commitments: Commitments): KnownChannel
|
def updateBalances(commitments: Commitments): KnownChannel
|
||||||
def applyChannelUpdate(update: Either[LocalChannelUpdate, RemoteChannelUpdate]): 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_1_opt.foreach(u => assert(u.channelFlags.isNode1))
|
||||||
update_2_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:on
|
||||||
|
|
||||||
// @formatter:off
|
// @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 object GetRoutingState
|
||||||
case class RoutingState(channels: Iterable[PublicChannel], nodes: Iterable[NodeAnnouncement])
|
case class RoutingState(channels: Iterable[PublicChannel], nodes: Iterable[NodeAnnouncement])
|
||||||
case object GetRoutingStateStreaming extends RemoteTypes
|
case object GetRoutingStateStreaming extends RemoteTypes
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.router
|
||||||
|
|
||||||
import akka.actor.{ActorContext, ActorRef}
|
import akka.actor.{ActorContext, ActorRef}
|
||||||
import akka.event.LoggingAdapter
|
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.bitcoin.scalacompat.Crypto.PublicKey
|
||||||
import fr.acinq.eclair.crypto.TransportHandler
|
import fr.acinq.eclair.crypto.TransportHandler
|
||||||
import fr.acinq.eclair.router.Monitoring.{Metrics, Tags}
|
import fr.acinq.eclair.router.Monitoring.{Metrics, Tags}
|
||||||
|
@ -481,7 +481,7 @@ object Sync {
|
||||||
* @param channels channels map
|
* @param channels channels map
|
||||||
* @return a ReplyChannelRange object
|
* @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 encoding = if (chunk.shortChannelIds.isEmpty) EncodingType.UNCOMPRESSED else defaultEncoding
|
||||||
val (timestamps, checksums) = queryFlags_opt match {
|
val (timestamps, checksums) = queryFlags_opt match {
|
||||||
case Some(extension) if extension.wantChecksums | extension.wantTimestamps =>
|
case Some(extension) if extension.wantChecksums | extension.wantTimestamps =>
|
||||||
|
|
|
@ -21,7 +21,7 @@ import akka.actor.{ActorContext, ActorRef, typed}
|
||||||
import akka.event.{DiagnosticLoggingAdapter, LoggingAdapter}
|
import akka.event.{DiagnosticLoggingAdapter, LoggingAdapter}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
||||||
import fr.acinq.bitcoin.scalacompat.Script.{pay2wsh, write}
|
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.ShortChannelId.outputIndex
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult, WatchExternalChannelSpent}
|
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
|
implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors
|
||||||
val fundingOutputIndex = outputIndex(ann.shortChannelId)
|
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))
|
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(
|
val pubChan = PublicChannel(
|
||||||
ann = ann,
|
ann = ann,
|
||||||
fundingTxid = fundingTxid,
|
fundingTxId = fundingTxId,
|
||||||
capacity = capacity,
|
capacity = capacity,
|
||||||
update_1_opt = privChan_opt.flatMap(_.update_1_opt),
|
update_1_opt = privChan_opt.flatMap(_.update_1_opt),
|
||||||
update_2_opt = privChan_opt.flatMap(_.update_2_opt),
|
update_2_opt = privChan_opt.flatMap(_.update_2_opt),
|
||||||
|
|
|
@ -108,11 +108,11 @@ object Scripts {
|
||||||
/**
|
/**
|
||||||
* @return the number of confirmations of each parent before which the given transaction can be published.
|
* @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) {
|
if (tx.version < 2) {
|
||||||
Map.empty
|
Map.empty
|
||||||
} else {
|
} 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)
|
val csvTimeout = sequenceToBlockHeight(txIn.sequence)
|
||||||
if (csvTimeout > 0) {
|
if (csvTimeout > 0) {
|
||||||
val maxCsvTimeout = math.max(csvTimeout, current.getOrElse(txIn.outPoint.txid, 0L))
|
val maxCsvTimeout = math.max(csvTimeout, current.getOrElse(txIn.outPoint.txid, 0L))
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
package fr.acinq.eclair.wire.internal.channel.version0
|
package fr.acinq.eclair.wire.internal.channel.version0
|
||||||
|
|
||||||
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
|
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
|
||||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
|
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
|
||||||
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxOut}
|
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxId, TxOut}
|
||||||
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
|
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
|
||||||
import fr.acinq.eclair.channel.LocalFundingStatus.SingleFundedUnconfirmedFundingTx
|
import fr.acinq.eclair.channel.LocalFundingStatus.SingleFundedUnconfirmedFundingTx
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
|
@ -172,7 +172,7 @@ private[channel] object ChannelCodecs0 {
|
||||||
val remoteCommitCodec: Codec[RemoteCommit] = (
|
val remoteCommitCodec: Codec[RemoteCommit] = (
|
||||||
("index" | uint64overflow) ::
|
("index" | uint64overflow) ::
|
||||||
("spec" | commitmentSpecCodec) ::
|
("spec" | commitmentSpecCodec) ::
|
||||||
("txid" | bytes32) ::
|
("txid" | txId) ::
|
||||||
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit].decodeOnly
|
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit].decodeOnly
|
||||||
|
|
||||||
val updateFulfillHtlcCodec: Codec[UpdateFulfillHtlc] = (
|
val updateFulfillHtlcCodec: Codec[UpdateFulfillHtlc] = (
|
||||||
|
@ -260,10 +260,10 @@ private[channel] object ChannelCodecs0 {
|
||||||
(wire: BitVector) => originsListCodec.decode(wire).map(_.map(_.toMap))
|
(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]](
|
val spentMapCodec: Codec[Map[OutPoint, TxId]] = Codec[Map[OutPoint, TxId]](
|
||||||
(map: Map[OutPoint, ByteVector32]) => spentListCodec.encode(map.toList),
|
(map: Map[OutPoint, TxId]) => spentListCodec.encode(map.toList),
|
||||||
(wire: BitVector) => spentListCodec.decode(wire).map(_.map(_.toMap))
|
(wire: BitVector) => spentListCodec.decode(wire).map(_.map(_.toMap))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ private[channel] object ChannelCodecs0 {
|
||||||
|
|
||||||
val fundingCreatedCodec: Codec[FundingCreated] = (
|
val fundingCreatedCodec: Codec[FundingCreated] = (
|
||||||
("temporaryChannelId" | bytes32) ::
|
("temporaryChannelId" | bytes32) ::
|
||||||
("fundingTxid" | bytes32) ::
|
("fundingTxHash" | txIdAsHash) ::
|
||||||
("fundingOutputIndex" | uint16) ::
|
("fundingOutputIndex" | uint16) ::
|
||||||
("signature" | bytes64) ::
|
("signature" | bytes64) ::
|
||||||
("tlvStream" | provide(TlvStream.empty[FundingCreatedTlv]))).as[FundingCreated]
|
("tlvStream" | provide(TlvStream.empty[FundingCreatedTlv]))).as[FundingCreated]
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.wire.internal.channel.version0
|
||||||
|
|
||||||
import com.softwaremill.quicklens._
|
import com.softwaremill.quicklens._
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.blockchain.fee.ConfirmationTarget
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.crypto.ShaChain
|
import fr.acinq.eclair.crypto.ShaChain
|
||||||
|
@ -51,10 +51,10 @@ private[channel] object ChannelTypes0 {
|
||||||
InputInfo(input, parentTx.txOut(input.index.toInt), Nil)
|
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 = {
|
def migrate(): channel.LocalCommitPublished = {
|
||||||
val htlcTxs = htlcSuccessTxs ++ htlcTimeoutTxs
|
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
|
// 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.
|
// 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)) }
|
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 = {
|
def migrate(): channel.RemoteCommitPublished = {
|
||||||
val claimHtlcTxs = claimHtlcSuccessTxs ::: claimHtlcTimeoutTxs
|
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
|
// 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.
|
// 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)) }
|
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 = {
|
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
|
// 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.
|
// 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)) }
|
val irrevocablySpentNew = irrevocablySpent.collect { case (outpoint, txid) if knownTxs.contains(txid) => (outpoint, knownTxs(txid)) }
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
package fr.acinq.eclair.wire.internal.channel.version1
|
package fr.acinq.eclair.wire.internal.channel.version1
|
||||||
|
|
||||||
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
|
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
|
||||||
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
|
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.KeyPath
|
||||||
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Transaction, TxOut}
|
import fr.acinq.bitcoin.scalacompat.{OutPoint, Transaction, TxId, TxOut}
|
||||||
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
|
import fr.acinq.eclair.blockchain.fee.ConfirmationTarget
|
||||||
import fr.acinq.eclair.channel.LocalFundingStatus.SingleFundedUnconfirmedFundingTx
|
import fr.acinq.eclair.channel.LocalFundingStatus.SingleFundedUnconfirmedFundingTx
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
|
@ -135,7 +135,7 @@ private[channel] object ChannelCodecs1 {
|
||||||
val remoteCommitCodec: Codec[RemoteCommit] = (
|
val remoteCommitCodec: Codec[RemoteCommit] = (
|
||||||
("index" | uint64overflow) ::
|
("index" | uint64overflow) ::
|
||||||
("spec" | commitmentSpecCodec) ::
|
("spec" | commitmentSpecCodec) ::
|
||||||
("txid" | bytes32) ::
|
("txid" | txId) ::
|
||||||
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
||||||
|
|
||||||
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))
|
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 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] = (
|
val commitmentsCodec: Codec[Commitments] = (
|
||||||
("channelVersion" | channelVersionCodec) >>:~ { channelVersion =>
|
("channelVersion" | channelVersionCodec) >>:~ { channelVersion =>
|
||||||
|
|
|
@ -162,7 +162,7 @@ private[channel] object ChannelCodecs2 {
|
||||||
val remoteCommitCodec: Codec[RemoteCommit] = (
|
val remoteCommitCodec: Codec[RemoteCommit] = (
|
||||||
("index" | uint64overflow) ::
|
("index" | uint64overflow) ::
|
||||||
("spec" | commitmentSpecCodec) ::
|
("spec" | commitmentSpecCodec) ::
|
||||||
("txid" | bytes32) ::
|
("txid" | txId) ::
|
||||||
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
||||||
|
|
||||||
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))
|
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))
|
||||||
|
|
|
@ -214,7 +214,7 @@ private[channel] object ChannelCodecs3 {
|
||||||
val remoteCommitCodec: Codec[RemoteCommit] = (
|
val remoteCommitCodec: Codec[RemoteCommit] = (
|
||||||
("index" | uint64overflow) ::
|
("index" | uint64overflow) ::
|
||||||
("spec" | commitmentSpecCodec) ::
|
("spec" | commitmentSpecCodec) ::
|
||||||
("txid" | bytes32) ::
|
("txid" | txId) ::
|
||||||
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
||||||
|
|
||||||
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))
|
val updateMessageCodec: Codec[UpdateMessage] = lengthDelimited(lightningMessageCodec.narrow[UpdateMessage](f => Attempt.successful(f.asInstanceOf[UpdateMessage]), g => g))
|
||||||
|
|
|
@ -395,7 +395,7 @@ private[channel] object ChannelCodecs4 {
|
||||||
private def remoteCommitCodec(commitmentSpecCodec: Codec[CommitmentSpec]): Codec[RemoteCommit] = (
|
private def remoteCommitCodec(commitmentSpecCodec: Codec[CommitmentSpec]): Codec[RemoteCommit] = (
|
||||||
("index" | uint64overflow) ::
|
("index" | uint64overflow) ::
|
||||||
("spec" | commitmentSpecCodec) ::
|
("spec" | commitmentSpecCodec) ::
|
||||||
("txid" | bytes32) ::
|
("txid" | txId) ::
|
||||||
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
("remotePerCommitmentPoint" | publicKey)).as[RemoteCommit]
|
||||||
|
|
||||||
private def nextRemoteCommitCodec(commitmentSpecCodec: Codec[CommitmentSpec]): Codec[NextRemoteCommit] = (
|
private def nextRemoteCommitCodec(commitmentSpecCodec: Codec[CommitmentSpec]): Codec[NextRemoteCommit] = (
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.wire.protocol
|
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.channel.{ChannelType, ChannelTypes}
|
||||||
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
||||||
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream, tmillisatoshi}
|
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream, tmillisatoshi}
|
||||||
|
@ -165,10 +165,10 @@ sealed trait ChannelReestablishTlv extends Tlv
|
||||||
|
|
||||||
object ChannelReestablishTlv {
|
object ChannelReestablishTlv {
|
||||||
|
|
||||||
case class NextFundingTlv(txHash: ByteVector32) extends ChannelReestablishTlv
|
case class NextFundingTlv(txId: TxId) extends ChannelReestablishTlv
|
||||||
|
|
||||||
object NextFundingTlv {
|
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)
|
val channelReestablishTlvCodec: Codec[TlvStream[ChannelReestablishTlv]] = tlvStream(discriminated[ChannelReestablishTlv].by(varint)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.wire.protocol
|
package fr.acinq.eclair.wire.protocol
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.channel.{ChannelFlags, RealScidStatus, ShortIds}
|
import fr.acinq.eclair.channel.{ChannelFlags, RealScidStatus, ShortIds}
|
||||||
import fr.acinq.eclair.crypto.Mac32
|
import fr.acinq.eclair.crypto.Mac32
|
||||||
|
@ -107,6 +107,13 @@ object CommonCodecs {
|
||||||
|
|
||||||
val sha256: Codec[ByteVector32] = bytes32
|
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 varsizebinarydata: Codec[ByteVector] = variableSizeBytes(uint16, bytes)
|
||||||
|
|
||||||
val listofsignatures: Codec[List[ByteVector64]] = listOfN(uint16, bytes64)
|
val listofsignatures: Codec[List[ByteVector64]] = listOfN(uint16, bytes64)
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.wire.protocol
|
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.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 fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
import scodec.codecs.discriminated
|
import scodec.codecs.discriminated
|
||||||
|
@ -31,11 +31,11 @@ sealed trait TxAddInputTlv extends Tlv
|
||||||
|
|
||||||
object TxAddInputTlv {
|
object TxAddInputTlv {
|
||||||
/** When doing a splice, the initiator must provide the previous funding txId instead of the whole transaction. */
|
/** 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)
|
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.
|
// 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]))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ object LightningMessageCodecs {
|
||||||
("tlvStream" | ChannelReestablishTlv.channelReestablishTlvCodec)).as[ChannelReestablish]
|
("tlvStream" | ChannelReestablishTlv.channelReestablishTlvCodec)).as[ChannelReestablish]
|
||||||
|
|
||||||
val openChannelCodec: Codec[OpenChannel] = (
|
val openChannelCodec: Codec[OpenChannel] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("temporaryChannelId" | bytes32) ::
|
("temporaryChannelId" | bytes32) ::
|
||||||
("fundingSatoshis" | satoshi) ::
|
("fundingSatoshis" | satoshi) ::
|
||||||
("pushMsat" | millisatoshi) ::
|
("pushMsat" | millisatoshi) ::
|
||||||
|
@ -90,7 +90,7 @@ object LightningMessageCodecs {
|
||||||
("tlvStream" | OpenChannelTlv.openTlvCodec)).as[OpenChannel]
|
("tlvStream" | OpenChannelTlv.openTlvCodec)).as[OpenChannel]
|
||||||
|
|
||||||
val openDualFundedChannelCodec: Codec[OpenDualFundedChannel] = (
|
val openDualFundedChannelCodec: Codec[OpenDualFundedChannel] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("temporaryChannelId" | bytes32) ::
|
("temporaryChannelId" | bytes32) ::
|
||||||
("fundingFeerate" | feeratePerKw) ::
|
("fundingFeerate" | feeratePerKw) ::
|
||||||
("commitmentFeerate" | feeratePerKw) ::
|
("commitmentFeerate" | feeratePerKw) ::
|
||||||
|
@ -148,7 +148,7 @@ object LightningMessageCodecs {
|
||||||
|
|
||||||
val fundingCreatedCodec: Codec[FundingCreated] = (
|
val fundingCreatedCodec: Codec[FundingCreated] = (
|
||||||
("temporaryChannelId" | bytes32) ::
|
("temporaryChannelId" | bytes32) ::
|
||||||
("fundingTxid" | bytes32) ::
|
("fundingTxHash" | txIdAsHash) ::
|
||||||
("fundingOutputIndex" | uint16) ::
|
("fundingOutputIndex" | uint16) ::
|
||||||
("signature" | bytes64) ::
|
("signature" | bytes64) ::
|
||||||
("tlvStream" | FundingCreatedTlv.fundingCreatedTlvCodec)).as[FundingCreated]
|
("tlvStream" | FundingCreatedTlv.fundingCreatedTlvCodec)).as[FundingCreated]
|
||||||
|
@ -197,7 +197,7 @@ object LightningMessageCodecs {
|
||||||
|
|
||||||
val txSignaturesCodec: Codec[TxSignatures] = (
|
val txSignaturesCodec: Codec[TxSignatures] = (
|
||||||
("channelId" | bytes32) ::
|
("channelId" | bytes32) ::
|
||||||
("txHash" | sha256) ::
|
("txHash" | txIdAsHash) ::
|
||||||
("witnesses" | witnessesCodec) ::
|
("witnesses" | witnessesCodec) ::
|
||||||
("tlvStream" | TxSignaturesTlv.txSignaturesTlvCodec)).as[TxSignatures]
|
("tlvStream" | TxSignaturesTlv.txSignaturesTlvCodec)).as[TxSignatures]
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ object LightningMessageCodecs {
|
||||||
|
|
||||||
val channelAnnouncementWitnessCodec =
|
val channelAnnouncementWitnessCodec =
|
||||||
("features" | lengthPrefixedFeaturesCodec) ::
|
("features" | lengthPrefixedFeaturesCodec) ::
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("shortChannelId" | realshortchannelid) ::
|
("shortChannelId" | realshortchannelid) ::
|
||||||
("nodeId1" | publicKey) ::
|
("nodeId1" | publicKey) ::
|
||||||
("nodeId2" | publicKey) ::
|
("nodeId2" | publicKey) ::
|
||||||
|
@ -317,7 +317,7 @@ object LightningMessageCodecs {
|
||||||
val channelFlagsCodec = ("channelFlags" | (ignore(6) :: reverseBool :: reverseBool)).as[ChannelUpdate.ChannelFlags]
|
val channelFlagsCodec = ("channelFlags" | (ignore(6) :: reverseBool :: reverseBool)).as[ChannelUpdate.ChannelFlags]
|
||||||
|
|
||||||
val channelUpdateChecksumCodec =
|
val channelUpdateChecksumCodec =
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("shortChannelId" | shortchannelid) ::
|
("shortChannelId" | shortchannelid) ::
|
||||||
messageFlagsCodec ::
|
messageFlagsCodec ::
|
||||||
channelFlagsCodec ::
|
channelFlagsCodec ::
|
||||||
|
@ -328,7 +328,7 @@ object LightningMessageCodecs {
|
||||||
("htlcMaximumMsat" | millisatoshi)
|
("htlcMaximumMsat" | millisatoshi)
|
||||||
|
|
||||||
val channelUpdateWitnessCodec =
|
val channelUpdateWitnessCodec =
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("shortChannelId" | shortchannelid) ::
|
("shortChannelId" | shortchannelid) ::
|
||||||
("timestamp" | timestampSecond) ::
|
("timestamp" | timestampSecond) ::
|
||||||
messageFlagsCodec ::
|
messageFlagsCodec ::
|
||||||
|
@ -355,23 +355,23 @@ object LightningMessageCodecs {
|
||||||
}((provide[EncodingType](EncodingType.COMPRESSED_ZLIB) :: zlib(list(realshortchannelid))).as[EncodedShortChannelIds])
|
}((provide[EncodingType](EncodingType.COMPRESSED_ZLIB) :: zlib(list(realshortchannelid))).as[EncodedShortChannelIds])
|
||||||
|
|
||||||
val queryShortChannelIdsCodec: Codec[QueryShortChannelIds] = (
|
val queryShortChannelIdsCodec: Codec[QueryShortChannelIds] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("shortChannelIds" | variableSizeBytes(uint16, encodedShortChannelIdsCodec)) ::
|
("shortChannelIds" | variableSizeBytes(uint16, encodedShortChannelIdsCodec)) ::
|
||||||
("tlvStream" | QueryShortChannelIdsTlv.codec)).as[QueryShortChannelIds]
|
("tlvStream" | QueryShortChannelIdsTlv.codec)).as[QueryShortChannelIds]
|
||||||
|
|
||||||
val replyShortChannelIdsEndCodec: Codec[ReplyShortChannelIdsEnd] = (
|
val replyShortChannelIdsEndCodec: Codec[ReplyShortChannelIdsEnd] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("complete" | byte) ::
|
("complete" | byte) ::
|
||||||
("tlvStream" | ReplyShortChannelIdsEndTlv.replyShortChannelIdsEndTlvCodec)).as[ReplyShortChannelIdsEnd]
|
("tlvStream" | ReplyShortChannelIdsEndTlv.replyShortChannelIdsEndTlvCodec)).as[ReplyShortChannelIdsEnd]
|
||||||
|
|
||||||
val queryChannelRangeCodec: Codec[QueryChannelRange] = (
|
val queryChannelRangeCodec: Codec[QueryChannelRange] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("firstBlock" | blockHeight) ::
|
("firstBlock" | blockHeight) ::
|
||||||
("numberOfBlocks" | uint32) ::
|
("numberOfBlocks" | uint32) ::
|
||||||
("tlvStream" | QueryChannelRangeTlv.codec)).as[QueryChannelRange]
|
("tlvStream" | QueryChannelRangeTlv.codec)).as[QueryChannelRange]
|
||||||
|
|
||||||
val replyChannelRangeCodec: Codec[ReplyChannelRange] = (
|
val replyChannelRangeCodec: Codec[ReplyChannelRange] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("firstBlock" | blockHeight) ::
|
("firstBlock" | blockHeight) ::
|
||||||
("numberOfBlocks" | uint32) ::
|
("numberOfBlocks" | uint32) ::
|
||||||
("complete" | byte) ::
|
("complete" | byte) ::
|
||||||
|
@ -379,7 +379,7 @@ object LightningMessageCodecs {
|
||||||
("tlvStream" | ReplyChannelRangeTlv.codec)).as[ReplyChannelRange]
|
("tlvStream" | ReplyChannelRangeTlv.codec)).as[ReplyChannelRange]
|
||||||
|
|
||||||
val gossipTimestampFilterCodec: Codec[GossipTimestampFilter] = (
|
val gossipTimestampFilterCodec: Codec[GossipTimestampFilter] = (
|
||||||
("chainHash" | bytes32) ::
|
("chainHash" | blockHash) ::
|
||||||
("firstTimestamp" | timestampSecond) ::
|
("firstTimestamp" | timestampSecond) ::
|
||||||
("timestampRange" | uint32) ::
|
("timestampRange" | uint32) ::
|
||||||
("tlvStream" | GossipTimestampFilterTlv.gossipTimestampFilterTlvCodec)).as[GossipTimestampFilter]
|
("tlvStream" | GossipTimestampFilterTlv.gossipTimestampFilterTlvCodec)).as[GossipTimestampFilter]
|
||||||
|
@ -418,7 +418,7 @@ object LightningMessageCodecs {
|
||||||
|
|
||||||
val spliceLockedCodec: Codec[SpliceLocked] = (
|
val spliceLockedCodec: Codec[SpliceLocked] = (
|
||||||
("channelId" | bytes32) ::
|
("channelId" | bytes32) ::
|
||||||
("fundingTxid" | bytes32) ::
|
("fundingTxHash" | txIdAsHash) ::
|
||||||
("tlvStream" | SpliceLockedTlv.spliceLockedTlvCodec)).as[SpliceLocked]
|
("tlvStream" | SpliceLockedTlv.spliceLockedTlvCodec)).as[SpliceLocked]
|
||||||
|
|
||||||
val stfuCodec: Codec[Stfu] = (
|
val stfuCodec: Codec[Stfu] = (
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.wire.protocol
|
||||||
import com.google.common.base.Charsets
|
import com.google.common.base.Charsets
|
||||||
import com.google.common.net.InetAddresses
|
import com.google.common.net.InetAddresses
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.channel.{ChannelFlags, ChannelType}
|
import fr.acinq.eclair.channel.{ChannelFlags, ChannelType}
|
||||||
import fr.acinq.eclair.payment.relay.Relayer
|
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 HasTimestamp extends LightningMessage { def timestamp: TimestampSecond }
|
||||||
sealed trait HasTemporaryChannelId extends LightningMessage { def temporaryChannelId: ByteVector32 } // <- not in the spec
|
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 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 HasSerialId extends LightningMessage { def serialId: UInt64 } // <- not in the spec
|
||||||
sealed trait ForbiddenMessageDuringSplice extends LightningMessage // <- not in the spec
|
sealed trait ForbiddenMessageDuringSplice extends LightningMessage // <- not in the spec
|
||||||
sealed trait UpdateMessage extends HtlcMessage with ForbiddenMessageDuringSplice // <- 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,
|
previousTxOutput: Long,
|
||||||
sequence: Long,
|
sequence: Long,
|
||||||
tlvStream: TlvStream[TxAddInputTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId with HasSerialId {
|
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 {
|
object TxAddInput {
|
||||||
|
@ -116,16 +116,15 @@ case class TxComplete(channelId: ByteVector32,
|
||||||
tlvStream: TlvStream[TxCompleteTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId
|
tlvStream: TlvStream[TxCompleteTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId
|
||||||
|
|
||||||
case class TxSignatures(channelId: ByteVector32,
|
case class TxSignatures(channelId: ByteVector32,
|
||||||
txHash: ByteVector32,
|
txId: TxId,
|
||||||
witnesses: Seq[ScriptWitness],
|
witnesses: Seq[ScriptWitness],
|
||||||
tlvStream: TlvStream[TxSignaturesTlv] = TlvStream.empty) extends InteractiveTxMessage with HasChannelId {
|
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)
|
val previousFundingTxSig_opt: Option[ByteVector64] = tlvStream.get[TxSignaturesTlv.PreviousFundingTxSig].map(_.sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
object TxSignatures {
|
object TxSignatures {
|
||||||
def apply(channelId: ByteVector32, tx: Transaction, witnesses: Seq[ScriptWitness], previousFundingSig_opt: Option[ByteVector64]): 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,
|
yourLastPerCommitmentSecret: PrivateKey,
|
||||||
myCurrentPerCommitmentPoint: PublicKey,
|
myCurrentPerCommitmentPoint: PublicKey,
|
||||||
tlvStream: TlvStream[ChannelReestablishTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
|
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,
|
temporaryChannelId: ByteVector32,
|
||||||
fundingSatoshis: Satoshi,
|
fundingSatoshis: Satoshi,
|
||||||
pushMsat: MilliSatoshi,
|
pushMsat: MilliSatoshi,
|
||||||
|
@ -213,7 +212,7 @@ case class AcceptChannel(temporaryChannelId: ByteVector32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: this message is named open_channel2 in the specification.
|
// NB: this message is named open_channel2 in the specification.
|
||||||
case class OpenDualFundedChannel(chainHash: ByteVector32,
|
case class OpenDualFundedChannel(chainHash: BlockHash,
|
||||||
temporaryChannelId: ByteVector32,
|
temporaryChannelId: ByteVector32,
|
||||||
fundingFeerate: FeeratePerKw,
|
fundingFeerate: FeeratePerKw,
|
||||||
commitmentFeerate: FeeratePerKw,
|
commitmentFeerate: FeeratePerKw,
|
||||||
|
@ -263,7 +262,7 @@ case class AcceptDualFundedChannel(temporaryChannelId: ByteVector32,
|
||||||
}
|
}
|
||||||
|
|
||||||
case class FundingCreated(temporaryChannelId: ByteVector32,
|
case class FundingCreated(temporaryChannelId: ByteVector32,
|
||||||
fundingTxHash: ByteVector32,
|
fundingTxId: TxId,
|
||||||
fundingOutputIndex: Int,
|
fundingOutputIndex: Int,
|
||||||
signature: ByteVector64,
|
signature: ByteVector64,
|
||||||
tlvStream: TlvStream[FundingCreatedTlv] = TlvStream.empty) extends ChannelMessage with HasTemporaryChannelId
|
tlvStream: TlvStream[FundingCreatedTlv] = TlvStream.empty) extends ChannelMessage with HasTemporaryChannelId
|
||||||
|
@ -319,9 +318,8 @@ object SpliceAck {
|
||||||
}
|
}
|
||||||
|
|
||||||
case class SpliceLocked(channelId: ByteVector32,
|
case class SpliceLocked(channelId: ByteVector32,
|
||||||
fundingTxHash: ByteVector32,
|
fundingTxId: TxId,
|
||||||
tlvStream: TlvStream[SpliceLockedTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
|
tlvStream: TlvStream[SpliceLockedTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId {
|
||||||
val fundingTxid: ByteVector32 = fundingTxHash.reverse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Shutdown(channelId: ByteVector32,
|
case class Shutdown(channelId: ByteVector32,
|
||||||
|
@ -401,7 +399,7 @@ case class ChannelAnnouncement(nodeSignature1: ByteVector64,
|
||||||
bitcoinSignature1: ByteVector64,
|
bitcoinSignature1: ByteVector64,
|
||||||
bitcoinSignature2: ByteVector64,
|
bitcoinSignature2: ByteVector64,
|
||||||
features: Features[Feature],
|
features: Features[Feature],
|
||||||
chainHash: ByteVector32,
|
chainHash: BlockHash,
|
||||||
shortChannelId: RealShortChannelId,
|
shortChannelId: RealShortChannelId,
|
||||||
nodeId1: PublicKey,
|
nodeId1: PublicKey,
|
||||||
nodeId2: PublicKey,
|
nodeId2: PublicKey,
|
||||||
|
@ -489,7 +487,7 @@ case class NodeAnnouncement(signature: ByteVector64,
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ChannelUpdate(signature: ByteVector64,
|
case class ChannelUpdate(signature: ByteVector64,
|
||||||
chainHash: ByteVector32,
|
chainHash: BlockHash,
|
||||||
shortChannelId: ShortChannelId,
|
shortChannelId: ShortChannelId,
|
||||||
timestamp: TimestampSecond,
|
timestamp: TimestampSecond,
|
||||||
messageFlags: ChannelUpdate.MessageFlags,
|
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})"
|
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]
|
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]
|
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 timestamps_opt: Option[ReplyChannelRangeTlv.EncodedTimestamps] = tlvStream.get[ReplyChannelRangeTlv.EncodedTimestamps]
|
||||||
val checksums_opt: Option[ReplyChannelRangeTlv.EncodedChecksums] = tlvStream.get[ReplyChannelRangeTlv.EncodedChecksums]
|
val checksums_opt: Option[ReplyChannelRangeTlv.EncodedChecksums] = tlvStream.get[ReplyChannelRangeTlv.EncodedChecksums]
|
||||||
}
|
}
|
||||||
|
|
||||||
object ReplyChannelRange {
|
object ReplyChannelRange {
|
||||||
def apply(chainHash: ByteVector32,
|
def apply(chainHash: BlockHash,
|
||||||
firstBlock: BlockHeight,
|
firstBlock: BlockHeight,
|
||||||
numberOfBlocks: Long,
|
numberOfBlocks: Long,
|
||||||
syncComplete: Byte,
|
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
|
case class OnionMessage(blindingKey: PublicKey, onionRoutingPacket: OnionRoutingPacket, tlvStream: TlvStream[OnionMessageTlv] = TlvStream.empty) extends LightningMessage
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.wire.protocol
|
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.crypto.Sphinx.RouteBlinding.{BlindedNode, BlindedRoute}
|
||||||
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
||||||
import fr.acinq.eclair.wire.protocol.OfferTypes.{InvoiceRequestChain, InvoiceRequestPayerNote, InvoiceRequestQuantity, _}
|
import fr.acinq.eclair.wire.protocol.OfferTypes.{InvoiceRequestChain, InvoiceRequestPayerNote, InvoiceRequestQuantity, _}
|
||||||
|
@ -26,7 +26,7 @@ import scodec.{Attempt, Codec, Err}
|
||||||
import scodec.codecs._
|
import scodec.codecs._
|
||||||
|
|
||||||
object OfferCodecs {
|
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)
|
private val offerMetadata: Codec[OfferMetadata] = tlvField(bytes)
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ object OfferCodecs {
|
||||||
|
|
||||||
private val invoiceRequestMetadata: Codec[InvoiceRequestMetadata] = tlvField(bytes)
|
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)
|
private val invoiceRequestAmount: Codec[InvoiceRequestAmount] = tlvField(tmillisatoshi)
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,12 @@ package fr.acinq.eclair.wire.protocol
|
||||||
|
|
||||||
import fr.acinq.bitcoin.Bech32
|
import fr.acinq.bitcoin.Bech32
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, XonlyPublicKey}
|
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.crypto.Sphinx.RouteBlinding.{BlindedNode, BlindedRoute}
|
||||||
import fr.acinq.eclair.wire.protocol.CommonCodecs.varint
|
import fr.acinq.eclair.wire.protocol.CommonCodecs.varint
|
||||||
import fr.acinq.eclair.wire.protocol.OnionRoutingCodecs.{ForbiddenTlv, InvalidTlvPayload, MissingRequiredTlv}
|
import fr.acinq.eclair.wire.protocol.OnionRoutingCodecs.{ForbiddenTlv, InvalidTlvPayload, MissingRequiredTlv}
|
||||||
import fr.acinq.eclair.wire.protocol.TlvCodecs.genericTlv
|
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.Codec
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
import scodec.codecs.vector
|
import scodec.codecs.vector
|
||||||
|
@ -61,7 +61,7 @@ object OfferTypes {
|
||||||
/**
|
/**
|
||||||
* Chains for which the offer is valid. If empty, bitcoin mainnet is implied.
|
* 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.
|
* 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.
|
* 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.
|
* Amount that the sender is going to send.
|
||||||
|
@ -216,11 +216,11 @@ object OfferTypes {
|
||||||
case class Signature(signature: ByteVector64) extends InvoiceRequestTlv with InvoiceTlv
|
case class Signature(signature: ByteVector64) extends InvoiceRequestTlv with InvoiceTlv
|
||||||
|
|
||||||
def filterOfferFields(tlvs: TlvStream[InvoiceRequestTlv]): TlvStream[OfferTlv] =
|
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)))
|
TlvStream[OfferTlv](tlvs.records.collect { case tlv: OfferTlv => tlv }, tlvs.unknown.filter(_.tag < UInt64(80)))
|
||||||
|
|
||||||
def filterInvoiceRequestFields(tlvs: TlvStream[InvoiceTlv]): TlvStream[InvoiceRequestTlv] =
|
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)))
|
TlvStream[InvoiceRequestTlv](tlvs.records.collect { case tlv: InvoiceRequestTlv => tlv }, tlvs.unknown.filter(_.tag < UInt64(160)))
|
||||||
|
|
||||||
case class ErroneousField(tag: Long) extends InvoiceErrorTlv
|
case class ErroneousField(tag: Long) extends InvoiceErrorTlv
|
||||||
|
@ -230,7 +230,7 @@ object OfferTypes {
|
||||||
case class Error(message: String) extends InvoiceErrorTlv
|
case class Error(message: String) extends InvoiceErrorTlv
|
||||||
|
|
||||||
case class Offer(records: TlvStream[OfferTlv]) {
|
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 metadata: Option[ByteVector] = records.get[OfferMetadata].map(_.data)
|
||||||
val currency: Option[String] = records.get[OfferCurrency].map(_.iso4217)
|
val currency: Option[String] = records.get[OfferCurrency].map(_.iso4217)
|
||||||
val amount: Option[MilliSatoshi] = currency match {
|
val amount: Option[MilliSatoshi] = currency match {
|
||||||
|
@ -271,7 +271,7 @@ object OfferTypes {
|
||||||
description: String,
|
description: String,
|
||||||
nodeId: PublicKey,
|
nodeId: PublicKey,
|
||||||
features: Features[Bolt12Feature],
|
features: Features[Bolt12Feature],
|
||||||
chain: ByteVector32,
|
chain: BlockHash,
|
||||||
additionalTlvs: Set[OfferTlv] = Set.empty,
|
additionalTlvs: Set[OfferTlv] = Set.empty,
|
||||||
customTlvs: Set[GenericTlv] = Set.empty): Offer = {
|
customTlvs: Set[GenericTlv] = Set.empty): Offer = {
|
||||||
val tlvs: Set[OfferTlv] = Set(
|
val tlvs: Set[OfferTlv] = Set(
|
||||||
|
@ -310,7 +310,7 @@ object OfferTypes {
|
||||||
val offer: Offer = Offer.validate(filterOfferFields(records)).toOption.get
|
val offer: Offer = Offer.validate(filterOfferFields(records)).toOption.get
|
||||||
|
|
||||||
val metadata: ByteVector = records.get[InvoiceRequestMetadata].get.data
|
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 amount: Option[MilliSatoshi] = records.get[InvoiceRequestAmount].map(_.amount)
|
||||||
val features: Features[Bolt12Feature] = records.get[InvoiceRequestFeatures].map(_.features.bolt12Features()).getOrElse(Features.empty)
|
val features: Features[Bolt12Feature] = records.get[InvoiceRequestFeatures].map(_.features.bolt12Features()).getOrElse(Features.empty)
|
||||||
val quantity_opt: Option[Long] = records.get[InvoiceRequestQuantity].map(_.quantity)
|
val quantity_opt: Option[Long] = records.get[InvoiceRequestQuantity].map(_.quantity)
|
||||||
|
@ -367,7 +367,7 @@ object OfferTypes {
|
||||||
quantity: Long,
|
quantity: Long,
|
||||||
features: Features[Bolt12Feature],
|
features: Features[Bolt12Feature],
|
||||||
payerKey: PrivateKey,
|
payerKey: PrivateKey,
|
||||||
chain: ByteVector32,
|
chain: BlockHash,
|
||||||
additionalTlvs: Set[InvoiceRequestTlv] = Set.empty,
|
additionalTlvs: Set[InvoiceRequestTlv] = Set.empty,
|
||||||
customTlvs: Set[GenericTlv] = Set.empty): InvoiceRequest = {
|
customTlvs: Set[GenericTlv] = Set.empty): InvoiceRequest = {
|
||||||
require(offer.chains.contains(chain))
|
require(offer.chains.contains(chain))
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.wire.protocol
|
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.UInt64
|
||||||
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
||||||
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
|
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
|
||||||
|
@ -33,7 +33,7 @@ sealed trait InitTlv extends Tlv
|
||||||
object InitTlv {
|
object InitTlv {
|
||||||
|
|
||||||
/** The chains the node is interested in. */
|
/** 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.
|
* 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._
|
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)
|
private val remoteAddress: Codec[RemoteAddress] = tlvField(nodeaddress)
|
||||||
|
|
||||||
val initTlvCodec = tlvStream(discriminated[InitTlv].by(varint)
|
val initTlvCodec = tlvStream(discriminated[InitTlv].by(varint)
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
"waitingSince" : 400000,
|
"waitingSince" : 400000,
|
||||||
"lastSent" : {
|
"lastSent" : {
|
||||||
"temporaryChannelId" : "0000000000000000000000000000000000000000000000000000000000000000",
|
"temporaryChannelId" : "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"fundingTxHash" : "e917adc681383fe00f779c6144a1bd91135ba2c9862ad1bc5aa8a14d37bae3f4",
|
"fundingTxId" : "f4e3ba374da1a85abcd12a86c9a25b1391bda144619c770fe03f3881c6ad17e9",
|
||||||
"fundingOutputIndex" : 0,
|
"fundingOutputIndex" : 0,
|
||||||
"signature" : "bd55d8660f54a1be6e123e2ed9cd6669d90b830d99dc6f9addd9ae65c447ea6b657e2fe39841f66c1d6a2fc80ad59e6f3d9bfaf177f4e95a579f0683bf3e9790",
|
"signature" : "bd55d8660f54a1be6e123e2ed9cd6669d90b830d99dc6f9addd9ae65c447ea6b657e2fe39841f66c1d6a2fc80ad59e6f3d9bfaf177f4e95a579f0683bf3e9790",
|
||||||
"tlvStream" : { }
|
"tlvStream" : { }
|
||||||
|
|
|
@ -22,7 +22,7 @@ import akka.pattern.pipe
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.ApiTypes.{ChannelIdentifier, ChannelNotFound}
|
||||||
import fr.acinq.eclair.TestConstants._
|
import fr.acinq.eclair.TestConstants._
|
||||||
import fr.acinq.eclair.blockchain.DummyOnChainWallet
|
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_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)))
|
(ann_ec, makeUpdateShort(ShortChannelId(7L), e, c, feeBase = 2 msat, 0, minHtlc = 0 msat, maxHtlc = None, cltvDelta = CltvExpiryDelta(12)))
|
||||||
).map { case (ann, update) =>
|
).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)
|
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 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 channel2 = Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, RealShortChannelId(2), b, c, b, c, ByteVector64.Zeroes, ByteVector64.Zeroes, ByteVector64.Zeroes, ByteVector64.Zeroes)
|
||||||
val publicChannels = SortedMap(
|
val publicChannels = SortedMap(
|
||||||
channel1.shortChannelId -> PublicChannel(channel1, ByteVector32.Zeroes, 100_000 sat, None, None, None),
|
channel1.shortChannelId -> PublicChannel(channel1, TxId(ByteVector32.Zeroes), 100_000 sat, None, None, None),
|
||||||
channel2.shortChannelId -> PublicChannel(channel2, ByteVector32.Zeroes, 150_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 (channelId3, shortIds3) = (randomBytes32(), ShortIds(RealScidStatus.Unknown, Alias(13), None))
|
||||||
val (channelId4, shortIds4) = (randomBytes32(), ShortIds(RealScidStatus.Final(RealShortChannelId(4)), Alias(14), None))
|
val (channelId4, shortIds4) = (randomBytes32(), ShortIds(RealScidStatus.Final(RealShortChannelId(4)), Alias(14), None))
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair
|
package fr.acinq.eclair
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
|
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 fr.acinq.bitcoin.{Base58, Base58Check, Bech32}
|
||||||
import org.scalatest.funsuite.AnyFunSuite
|
import org.scalatest.funsuite.AnyFunSuite
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
@ -32,13 +32,15 @@ class PackageSpec extends AnyFunSuite {
|
||||||
implicit def byteVector322array(input: ByteVector32): Array[Byte] = input.toArray
|
implicit def byteVector322array(input: ByteVector32): Array[Byte] = input.toArray
|
||||||
|
|
||||||
test("compute long channel id") {
|
test("compute long channel id") {
|
||||||
val data = ((hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0, hex"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") ::
|
case class TestCase(fundingTxId: TxId, fundingOutputIndex: Int, expected: ByteVector32)
|
||||||
(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)))
|
|
||||||
|
|
||||||
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") {
|
test("decode base58 addresses") {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair
|
package fr.acinq.eclair
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
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._
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinJsonRPCAuthMethod.UserPassword
|
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinJsonRPCAuthMethod.UserPassword
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, BitcoinCoreClient}
|
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
|
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))
|
system.eventStream.publish(NewTransaction(tx))
|
||||||
Future.successful(tx.txid)
|
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))
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,17 +19,17 @@ package fr.acinq.eclair
|
||||||
import akka.actor.{ActorRef, ActorSystem}
|
import akka.actor.{ActorRef, ActorSystem}
|
||||||
import akka.event.{DiagnosticLoggingAdapter, EventStream}
|
import akka.event.{DiagnosticLoggingAdapter, EventStream}
|
||||||
import akka.testkit.{TestActor, TestProbe}
|
import akka.testkit.{TestActor, TestProbe}
|
||||||
|
import fr.acinq.bitcoin.scalacompat.TxId
|
||||||
import fr.acinq.eclair.channel.fsm.Channel
|
import fr.acinq.eclair.channel.fsm.Channel
|
||||||
import fr.acinq.eclair.io.Peer
|
import fr.acinq.eclair.io.Peer
|
||||||
import fr.acinq.eclair.wire.protocol.LightningMessage
|
import fr.acinq.eclair.wire.protocol.LightningMessage
|
||||||
import org.scalatest.concurrent.Eventually.eventually
|
import org.scalatest.concurrent.Eventually.eventually
|
||||||
import org.scalatest.concurrent.PatienceConfiguration
|
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
object TestUtils {
|
object TestUtils {
|
||||||
|
|
||||||
|
@ -110,4 +110,6 @@ object TestUtils {
|
||||||
|
|
||||||
def waitFor(duration: FiniteDuration): Unit = Thread.sleep(duration.toMillis)
|
def waitFor(duration: FiniteDuration): Unit = Thread.sleep(duration.toMillis)
|
||||||
|
|
||||||
|
def randomTxId(): TxId = TxId(randomBytes32())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ package fr.acinq.eclair.balance
|
||||||
|
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import akka.testkit.TestProbe
|
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.balance.CheckBalance.{ClosingBalance, MainAndHtlcBalance, OffChainBalance, PossiblyPublishedMainAndHtlcBalance, PossiblyPublishedMainBalance}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{apply => _, _}
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{apply => _, _}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
||||||
|
@ -250,12 +251,12 @@ class CheckBalanceSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
||||||
}
|
}
|
||||||
|
|
||||||
test("tx pruning") { () =>
|
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 knownTxids = Set(txids(1), txids(3), txids(4), txids(6), txids(9), txids(12), txids(13))
|
||||||
|
|
||||||
val bitcoinClient = new BitcoinCoreClient(null) {
|
val bitcoinClient = new BitcoinCoreClient(null) {
|
||||||
/** Get the number of confirmations of a given transaction. */
|
/** 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)
|
Future.successful(if (knownTxids.contains(txid)) Some(42) else None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,14 @@ package fr.acinq.eclair.blockchain
|
||||||
import fr.acinq.bitcoin.TxIn.SEQUENCE_FINAL
|
import fr.acinq.bitcoin.TxIn.SEQUENCE_FINAL
|
||||||
import fr.acinq.bitcoin.psbt.Psbt
|
import fr.acinq.bitcoin.psbt.Psbt
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.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.OnChainWallet.{FundTransactionResponse, MakeFundingTxResponse, OnChainBalance, ProcessPsbtResponse}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.SignTransactionResponse
|
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.SignTransactionResponse
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
|
import fr.acinq.eclair.randomKey
|
||||||
import fr.acinq.eclair.transactions.Transactions
|
import fr.acinq.eclair.transactions.Transactions
|
||||||
import fr.acinq.eclair.{randomBytes32, randomKey}
|
|
||||||
import scodec.bits._
|
import scodec.bits._
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||||
|
@ -38,8 +39,8 @@ class DummyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
|
||||||
|
|
||||||
import DummyOnChainWallet._
|
import DummyOnChainWallet._
|
||||||
|
|
||||||
val funded = collection.concurrent.TrieMap.empty[ByteVector32, Transaction]
|
val funded = collection.concurrent.TrieMap.empty[TxId, Transaction]
|
||||||
val published = collection.concurrent.TrieMap.empty[ByteVector32, Transaction]
|
val published = collection.concurrent.TrieMap.empty[TxId, Transaction]
|
||||||
var rolledback = Set.empty[Transaction]
|
var rolledback = Set.empty[Transaction]
|
||||||
|
|
||||||
override def onChainBalance()(implicit ec: ExecutionContext): Future[OnChainBalance] = Future.successful(OnChainBalance(1105 sat, 561 sat))
|
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 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)
|
published += (tx.txid -> tx)
|
||||||
Future.successful(tx.txid)
|
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 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] = {
|
override def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
rolledback = rolledback + tx
|
rolledback = rolledback + tx
|
||||||
|
@ -87,7 +88,7 @@ class NoOpOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
|
||||||
import DummyOnChainWallet._
|
import DummyOnChainWallet._
|
||||||
|
|
||||||
var rolledback = 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))
|
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 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 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 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] = {
|
override def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
rolledback = rolledback :+ tx
|
rolledback = rolledback :+ tx
|
||||||
|
@ -125,7 +126,7 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
|
||||||
// We create a new dummy input transaction for every funding request.
|
// We create a new dummy input transaction for every funding request.
|
||||||
var inputs = Seq.empty[Transaction]
|
var inputs = Seq.empty[Transaction]
|
||||||
var rolledback = 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))
|
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
|
val amountOut = tx.txOut.map(_.amount).sum
|
||||||
// We add a single input to reach the desired feerate.
|
// We add a single input to reach the desired feerate.
|
||||||
val inputAmount = amountOut + 100_000.sat
|
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
|
inputs = inputs :+ inputTx
|
||||||
val dummyWitness = Script.witnessPay2wpkh(pubkey, ByteVector.fill(73)(0))
|
val dummyWitness = Script.witnessPay2wpkh(pubkey, ByteVector.fill(73)(0))
|
||||||
val dummySignedTx = tx.copy(
|
val dummySignedTx = tx.copy(
|
||||||
|
@ -166,7 +167,7 @@ class SingleKeyOnChainWallet extends OnChainWallet with OnchainPubkeyCache {
|
||||||
Future.successful(SignTransactionResponse(signedTx, complete))
|
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
|
inputs = inputs :+ tx
|
||||||
Future.successful(tx.txid)
|
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] = {
|
override def signPsbt(psbt: Psbt, ourInputs: Seq[Int], ourOutputs: Seq[Int])(implicit ec: ExecutionContext): Future[ProcessPsbtResponse] = {
|
||||||
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
|
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)) {
|
val signedPsbt = tx.txIn.zipWithIndex.foldLeft(new Psbt(tx)) {
|
||||||
case (currentPsbt, (txIn, index)) => inputs.find(_.txid == txIn.outPoint.txid) match {
|
case (currentPsbt, (txIn, index)) => inputs.find(_.txid == txIn.outPoint.txid) match {
|
||||||
case Some(inputTx) =>
|
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 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 {
|
inputs.find(_.txid == txId) match {
|
||||||
case Some(tx) => Future.successful(tx)
|
case Some(tx) => Future.successful(tx)
|
||||||
case None => Future.failed(new RuntimeException(s"txid=$txId not found"))
|
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] = {
|
override def rollback(tx: Transaction)(implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
rolledback = rolledback :+ tx
|
rolledback = rolledback :+ tx
|
||||||
|
@ -225,7 +226,7 @@ object DummyOnChainWallet {
|
||||||
def makeDummyFundingTx(pubkeyScript: ByteVector, amount: Satoshi): MakeFundingTxResponse = {
|
def makeDummyFundingTx(pubkeyScript: ByteVector, amount: Satoshi): MakeFundingTxResponse = {
|
||||||
val fundingTx = Transaction(
|
val fundingTx = Transaction(
|
||||||
version = 2,
|
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,
|
txOut = TxOut(amount, pubkeyScript) :: Nil,
|
||||||
lockTime = 0
|
lockTime = 0
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,8 +22,9 @@ import akka.testkit.TestProbe
|
||||||
import fr.acinq.bitcoin
|
import fr.acinq.bitcoin
|
||||||
import fr.acinq.bitcoin.psbt.{Psbt, UpdateFailure}
|
import fr.acinq.bitcoin.psbt.{Psbt, UpdateFailure}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.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.OnChainWallet.{FundTransactionResponse, MakeFundingTxResponse, OnChainBalance, ProcessPsbtResponse}
|
||||||
import fr.acinq.eclair.blockchain.WatcherSpec.{createSpendManyP2WPKH, createSpendP2WPKH}
|
import fr.acinq.eclair.blockchain.WatcherSpec.{createSpendManyP2WPKH, createSpendP2WPKH}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.{BitcoinReq, SignTransactionResponse}
|
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)
|
walletExternalFunds.getReceiveAddress().pipeTo(sender.ref)
|
||||||
val walletAddress = sender.expectMsgType[String]
|
val walletAddress = sender.expectMsgType[String]
|
||||||
defaultWallet.sendToPubkeyScript(Script.write(addressToPublicKeyScript(Block.RegtestGenesisBlock.hash, walletAddress).toOption.get), amount, FeeratePerKw(FeeratePerByte(3.sat))).pipeTo(sender.ref)
|
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.
|
// 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 nonWalletWitness = ScriptWitness(Seq(nonWalletSig, nonWalletKey.publicKey.value))
|
||||||
val txWithSignedNonWalletInput = txWithNonWalletInput.updateWitness(0, nonWalletWitness)
|
val txWithSignedNonWalletInput = txWithNonWalletInput.updateWitness(0, nonWalletWitness)
|
||||||
val psbt = new Psbt(txWithSignedNonWalletInput)
|
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))
|
val Right(psbt1) = updated.flatMap(_.finalizeWitnessInput(0, nonWalletWitness))
|
||||||
bitcoinClient.signPsbt(psbt1, txWithSignedNonWalletInput.txIn.indices.tail, Nil).pipeTo(sender.ref)
|
bitcoinClient.signPsbt(psbt1, txWithSignedNonWalletInput.txIn.indices.tail, Nil).pipeTo(sender.ref)
|
||||||
val signTxResponse2 = sender.expectMsgType[ProcessPsbtResponse]
|
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 nonWalletWitness = ScriptWitness(Seq(nonWalletSig, nonWalletKey.publicKey.value))
|
||||||
val txWithSignedUnconfirmedInput = txWithUnconfirmedInput.updateWitness(0, nonWalletWitness)
|
val txWithSignedUnconfirmedInput = txWithUnconfirmedInput.updateWitness(0, nonWalletWitness)
|
||||||
val psbt = new Psbt(txWithSignedUnconfirmedInput)
|
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))
|
.flatMap(_.finalizeWitnessInput(0, nonWalletWitness))
|
||||||
bitcoinClient.signPsbt(psbt1, txWithSignedUnconfirmedInput.txIn.indices.tail, Nil).pipeTo(sender.ref)
|
bitcoinClient.signPsbt(psbt1, txWithSignedUnconfirmedInput.txIn.indices.tail, Nil).pipeTo(sender.ref)
|
||||||
assert(sender.expectMsgType[ProcessPsbtResponse].complete)
|
assert(sender.expectMsgType[ProcessPsbtResponse].complete)
|
||||||
|
@ -733,7 +734,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
|
||||||
val spendingTx = {
|
val spendingTx = {
|
||||||
val address = getNewAddress(sender)
|
val address = getNewAddress(sender)
|
||||||
val pos = if (changePos == 0) 1 else 0
|
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 JString(unsignedTxStr) = sender.expectMsgType[JValue]
|
||||||
val unsignedTx = Transaction.read(unsignedTxStr)
|
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)
|
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
|
signTxResponse.tx
|
||||||
}
|
}
|
||||||
|
|
||||||
def getMempoolTx(txid: ByteVector32): MempoolTx = {
|
def getMempoolTx(txid: TxId): MempoolTx = {
|
||||||
val probe = TestProbe()
|
val probe = TestProbe()
|
||||||
bitcoinClient.getMempoolTx(txid).pipeTo(probe.ref)
|
bitcoinClient.getMempoolTx(txid).pipeTo(probe.ref)
|
||||||
probe.expectMsgType[MempoolTx]
|
probe.expectMsgType[MempoolTx]
|
||||||
|
@ -1090,7 +1091,7 @@ class BitcoinCoreClientSpec extends TestKitBaseClass with BitcoindService with A
|
||||||
test("cannot bump transaction fees (unknown transaction)") {
|
test("cannot bump transaction fees (unknown transaction)") {
|
||||||
val sender = TestProbe()
|
val sender = TestProbe()
|
||||||
val bitcoinClient = makeBitcoinCoreClient()
|
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]
|
val failure = sender.expectMsgType[Failure]
|
||||||
assert(failure.cause.getMessage.contains("some transactions could not be found"))
|
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 pubKey = randomKey().publicKey
|
||||||
val legacyAddress = computeP2PkhAddress(pubKey, Block.RegtestGenesisBlock.hash)
|
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)
|
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)
|
bitcoinClient.getTransaction(txId).pipeTo(sender.ref)
|
||||||
val tx = sender.expectMsgType[Transaction]
|
val tx = sender.expectMsgType[Transaction]
|
||||||
// We have a change output.
|
// 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"))
|
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)
|
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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOp
|
||||||
import akka.actor.{ActorRef, Props, typed}
|
import akka.actor.{ActorRef, Props, typed}
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import akka.testkit.TestProbe
|
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.OnChainWallet.{FundTransactionResponse, MakeFundingTxResponse}
|
||||||
import fr.acinq.eclair.blockchain.WatcherSpec._
|
import fr.acinq.eclair.blockchain.WatcherSpec._
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService.SignTransactionResponse
|
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.bitcoind.zmq.ZMQActor
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.blockchain.{CurrentBlockHeight, NewTransaction}
|
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 grizzled.slf4j.Logging
|
||||||
import org.scalatest.BeforeAndAfterAll
|
import org.scalatest.BeforeAndAfterAll
|
||||||
import org.scalatest.funsuite.AnyFunSuiteLike
|
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") {
|
test("add/remove watches from/to utxo map") {
|
||||||
val m0 = Map.empty[OutPoint, Set[Watch[_ <: WatchTriggered]]]
|
val m0 = Map.empty[OutPoint, Set[Watch[_ <: WatchTriggered]]]
|
||||||
val txid = randomBytes32()
|
val txid = randomTxId()
|
||||||
val outputIndex = 42
|
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 w1 = WatchFundingSpent(TestProbe().ref, txid, outputIndex, hints = Set.empty)
|
||||||
val w2 = 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 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)
|
val w5 = WatchFundingConfirmed(TestProbe().ref, txid, 3)
|
||||||
|
|
||||||
// we test as if the collection was immutable
|
// 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)
|
val m3 = addWatchedUtxos(m2, w3)
|
||||||
assert(m3.keySet == Set(utxo) && m3(utxo).size == 3)
|
assert(m3.keySet == Set(utxo) && m3(utxo).size == 3)
|
||||||
val m4 = addWatchedUtxos(m3, w4)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
val m10 = removeWatchedUtxos(m9, w4)
|
||||||
assert(m10.isEmpty)
|
assert(m10.isEmpty)
|
||||||
}
|
}
|
||||||
|
@ -281,7 +282,7 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
|
||||||
watcher ! StopWatching(probe.ref)
|
watcher ! StopWatching(probe.ref)
|
||||||
|
|
||||||
// We should still find tx2 if the provided hint is wrong
|
// 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 }
|
probe.fishForMessage() { case m: WatchOutputSpentTriggered => m.spendingTx.txid == tx2.txid }
|
||||||
watcher ! StopWatching(probe.ref)
|
watcher ! StopWatching(probe.ref)
|
||||||
|
|
||||||
|
@ -359,11 +360,11 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
|
||||||
val actor1 = TestProbe()
|
val actor1 = TestProbe()
|
||||||
val actor2 = TestProbe()
|
val actor2 = TestProbe()
|
||||||
|
|
||||||
val txid = randomBytes32()
|
val txid = randomTxId()
|
||||||
watcher ! WatchFundingConfirmed(actor1.ref, txid, 2)
|
watcher ! WatchFundingConfirmed(actor1.ref, txid, 2)
|
||||||
watcher ! WatchFundingConfirmed(actor1.ref, txid, 3)
|
watcher ! WatchFundingConfirmed(actor1.ref, txid, 3)
|
||||||
watcher ! WatchFundingDeeplyBuried(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, 0, Set.empty)
|
||||||
watcher ! WatchOutputSpent(actor1.ref, txid, 1, Set.empty)
|
watcher ! WatchOutputSpent(actor1.ref, txid, 1, Set.empty)
|
||||||
watcher ! ListWatches(actor1.ref)
|
watcher ! ListWatches(actor1.ref)
|
||||||
|
@ -372,7 +373,7 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
|
||||||
|
|
||||||
watcher ! WatchFundingConfirmed(actor2.ref, txid, 2)
|
watcher ! WatchFundingConfirmed(actor2.ref, txid, 2)
|
||||||
watcher ! WatchFundingDeeplyBuried(actor2.ref, txid, 3)
|
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, 0, Set.empty)
|
||||||
watcher ! WatchOutputSpent(actor2.ref, txid, 1, Set.empty)
|
watcher ! WatchOutputSpent(actor2.ref, txid, 1, Set.empty)
|
||||||
watcher ! ListWatches(actor2.ref)
|
watcher ! ListWatches(actor2.ref)
|
||||||
|
|
|
@ -18,6 +18,7 @@ package fr.acinq.eclair.channel
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
||||||
import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, ByteVector64, DeterministicWallet, Satoshi, SatoshiLong, Transaction}
|
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._
|
||||||
import fr.acinq.eclair.blockchain.fee._
|
import fr.acinq.eclair.blockchain.fee._
|
||||||
import fr.acinq.eclair.channel.Helpers.Funding
|
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.funsuite.FixtureAnyFunSuiteLike
|
||||||
import org.scalatest.{Outcome, Tag}
|
import org.scalatest.{Outcome, Tag}
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.util.Random
|
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 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 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 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 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(
|
Commitments(
|
||||||
ChannelParams(randomBytes32(), ChannelConfig.standard, ChannelFeatures(), localParams, remoteParams, ChannelFlags(announceChannel = announceChannel)),
|
ChannelParams(randomBytes32(), ChannelConfig.standard, ChannelFeatures(), localParams, remoteParams, ChannelFlags(announceChannel = announceChannel)),
|
||||||
CommitmentChanges(LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 1, remoteNextHtlcId = 1),
|
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 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 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 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 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(
|
Commitments(
|
||||||
ChannelParams(randomBytes32(), ChannelConfig.standard, ChannelFeatures(), localParams, remoteParams, ChannelFlags(announceChannel = announceChannel)),
|
ChannelParams(randomBytes32(), ChannelConfig.standard, ChannelFeatures(), localParams, remoteParams, ChannelFlags(announceChannel = announceChannel)),
|
||||||
CommitmentChanges(LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 1, remoteNextHtlcId = 1),
|
CommitmentChanges(LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 1, remoteNextHtlcId = 1),
|
||||||
|
|
|
@ -228,7 +228,7 @@ class HelpersSpec extends TestKitBaseClass with AnyFunSuiteLike with ChannelStat
|
||||||
)
|
)
|
||||||
|
|
||||||
def toClosingTx(txOut: Seq[TxOut]): ClosingTx = {
|
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)))
|
assert(Closing.MutualClose.checkClosingDustAmounts(toClosingTx(allOutputsAboveDust)))
|
||||||
|
|
|
@ -23,7 +23,8 @@ import akka.testkit.TestProbe
|
||||||
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
|
import com.softwaremill.quicklens.{ModifyPimp, QuicklensAt}
|
||||||
import fr.acinq.bitcoin.psbt.Psbt
|
import fr.acinq.bitcoin.psbt.Psbt
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.OnChainWallet.{FundTransactionResponse, ProcessPsbtResponse}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient.{MempoolTx, Utxo}
|
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)
|
txid <- client.publishTransaction(signed.finalTx_opt.toOption.get)
|
||||||
} yield txid
|
} yield txid
|
||||||
f.pipeTo(probe.ref)
|
f.pipeTo(probe.ref)
|
||||||
probe.expectMsgType[ByteVector32]
|
probe.expectMsgType[TxId]
|
||||||
}
|
}
|
||||||
|
|
||||||
private def createInput(channelId: ByteVector32, serialId: UInt64, amount: Satoshi): TxAddInput = {
|
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)))
|
val fundingPubkeyScript: ByteVector = Script.write(Script.pay2wsh(Scripts.multiSig2of2(fundingParamsB.remoteFundingPubKey, fundingParamsA.remoteFundingPubKey)))
|
||||||
|
|
||||||
def dummySharedInputB(amount: Satoshi): SharedFundingInput = {
|
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 {
|
val fundingTxIndex = fundingParamsA.sharedInput_opt match {
|
||||||
case Some(input: Multisig2of2Input) => input.fundingTxIndex + 1
|
case Some(input: Multisig2of2Input) => input.fundingTxIndex + 1
|
||||||
case _ => 0
|
case _ => 0
|
||||||
|
@ -1034,7 +1035,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
|
||||||
val Right(signedTx) = probe.expectMsgType[ProcessPsbtResponse].finalTx_opt
|
val Right(signedTx) = probe.expectMsgType[ProcessPsbtResponse].finalTx_opt
|
||||||
assert(Transaction.write(signedTx).length >= 65_000)
|
assert(Transaction.write(signedTx).length >= 65_000)
|
||||||
minerWallet.publishTransaction(signedTx).pipeTo(probe.ref)
|
minerWallet.publishTransaction(signedTx).pipeTo(probe.ref)
|
||||||
probe.expectMsgType[ByteVector32]
|
probe.expectMsgType[TxId]
|
||||||
}
|
}
|
||||||
generateBlocks(1)
|
generateBlocks(1)
|
||||||
|
|
||||||
|
@ -1805,7 +1806,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
|
||||||
val probe = TestProbe()
|
val probe = TestProbe()
|
||||||
walletB.getP2wpkhPubkey().pipeTo(probe.ref)
|
walletB.getP2wpkhPubkey().pipeTo(probe.ref)
|
||||||
walletB.sendToPubkeyScript(Script.write(Script.pay2wpkh(probe.expectMsgType[PublicKey])), 75_000 sat, FeeratePerKw(FeeratePerByte(1.sat))).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)
|
alice ! Start(alice2bob.ref)
|
||||||
bob ! Start(bob2alice.ref)
|
bob ! Start(bob2alice.ref)
|
||||||
|
@ -1841,7 +1842,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
|
||||||
val probe = TestProbe()
|
val probe = TestProbe()
|
||||||
walletB.getReceiveAddress().pipeTo(probe.ref)
|
walletB.getReceiveAddress().pipeTo(probe.ref)
|
||||||
walletB.sendToAddress(probe.expectMsgType[String], 75_000 sat, 1).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)
|
alice ! Start(alice2bob.ref)
|
||||||
bob ! Start(bob2alice.ref)
|
bob ! Start(bob2alice.ref)
|
||||||
|
@ -2335,7 +2336,7 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
|
||||||
bob ! Start(probe.ref)
|
bob ! Start(probe.ref)
|
||||||
// Alice --- tx_add_input --> Bob
|
// Alice --- tx_add_input --> Bob
|
||||||
// The input doesn't include the previous transaction but is not the shared input.
|
// 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)
|
bob ! ReceiveMessage(nonSharedInput)
|
||||||
assert(probe.expectMsgType[RemoteFailure].cause == PreviousTxMissing(params.channelId, UInt64(0)))
|
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)
|
probe.expectMsg(txA1.txId)
|
||||||
|
|
||||||
// we modify remote's input in previous txs, it won't be double spent
|
// 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 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)
|
val bobRbf = fixtureParams.spawnTxBuilderRbfBob(bobParams.copy(targetFeerate = FeeratePerKw(10_000 sat)), commitmentB1, Seq(txB1, fakeTxB2), walletB)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import akka.actor.typed.ActorRef
|
||||||
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter}
|
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter}
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import akka.testkit.TestProbe
|
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.CurrentBlockHeight
|
||||||
import fr.acinq.eclair.blockchain.WatcherSpec.createSpendP2WPKH
|
import fr.acinq.eclair.blockchain.WatcherSpec.createSpendP2WPKH
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
||||||
|
@ -63,7 +63,7 @@ class FinalTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
|
||||||
probe.expectMsgType[Seq[Transaction]]
|
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))
|
awaitCond(getMempool(bitcoinClient, probe).exists(_.txid == txId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, actorRefAdapter
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
|
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.WatcherSpec.{createSpendManyP2WPKH, createSpendP2WPKH}
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
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.TxPublishContext
|
||||||
import fr.acinq.eclair.channel.publish.TxPublisher.TxRejectedReason._
|
import fr.acinq.eclair.channel.publish.TxPublisher.TxRejectedReason._
|
||||||
import fr.acinq.eclair.channel.{TransactionConfirmed, TransactionPublished}
|
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.BeforeAndAfterAll
|
||||||
import org.scalatest.funsuite.AnyFunSuiteLike
|
import org.scalatest.funsuite.AnyFunSuiteLike
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ class MempoolTxMonitorSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
|
||||||
probe.expectMsgType[Seq[Transaction]]
|
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))
|
awaitCond(getMempool(bitcoinClient, probe).exists(_.txid == txId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +166,7 @@ class MempoolTxMonitorSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val tx = createSpendP2WPKH(parentTx, priv, priv.publicKey, 5_000 sat, 0, 0)
|
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)
|
monitor ! Publish(probe.ref, txUnknownInput, txUnknownInput.txIn.head.outPoint, "test-tx", 10 sat)
|
||||||
probe.expectMsg(TxRejected(txUnknownInput.txid, InputGone))
|
probe.expectMsg(TxRejected(txUnknownInput.txid, InputGone))
|
||||||
}
|
}
|
||||||
|
@ -178,7 +179,7 @@ class MempoolTxMonitorSpec extends TestKitBaseClass with AnyFunSuiteLike with Bi
|
||||||
generateBlocks(1)
|
generateBlocks(1)
|
||||||
|
|
||||||
val tx = createSpendP2WPKH(parentTx, priv, priv.publicKey, 5_000 sat, 0, 0)
|
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)
|
monitor ! Publish(probe.ref, txUnknownInput, txUnknownInput.txIn.head.outPoint, "test-tx", 10 sat)
|
||||||
probe.expectMsg(TxRejected(txUnknownInput.txid, InputGone))
|
probe.expectMsg(TxRejected(txUnknownInput.txid, InputGone))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.channel.publish
|
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.blockchain.fee.{ConfirmationTarget, FeeratePerKw}
|
||||||
import fr.acinq.eclair.channel.Helpers.Funding
|
import fr.acinq.eclair.channel.Helpers.Funding
|
||||||
import fr.acinq.eclair.channel.publish.ReplaceableTxFunder.AdjustPreviousTxOutputResult.{AddWalletInputs, TxOutputAdjusted}
|
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) = {
|
private def createAnchorTx(): (CommitTx, ClaimLocalAnchorOutputTx) = {
|
||||||
val anchorScript = Scripts.anchor(PlaceHolderPubKey)
|
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(
|
val commitTx = Transaction(
|
||||||
2,
|
2,
|
||||||
Seq(TxIn(commitInput.outPoint, commitInput.redeemScript, 0, Scripts.witness2of2(PlaceHolderSig, PlaceHolderSig, PlaceHolderPubKey, PlaceHolderPubKey))),
|
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 htlcTimeoutScript = Scripts.htlcOffered(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, randomBytes32(), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
|
||||||
val commitTx = Transaction(
|
val commitTx = Transaction(
|
||||||
2,
|
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))),
|
Seq(TxOut(5000 sat, Script.pay2wsh(htlcSuccessScript)), TxOut(4000 sat, Script.pay2wsh(htlcTimeoutScript))),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
@ -86,15 +87,15 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
|
||||||
val htlcSuccessScript = Scripts.htlcReceived(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, paymentHash, CltvExpiry(0), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
|
val htlcSuccessScript = Scripts.htlcReceived(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, paymentHash, CltvExpiry(0), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
|
||||||
val htlcTimeoutScript = Scripts.htlcOffered(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, randomBytes32(), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
|
val htlcTimeoutScript = Scripts.htlcOffered(PlaceHolderPubKey, PlaceHolderPubKey, PlaceHolderPubKey, randomBytes32(), ZeroFeeHtlcTxAnchorOutputsCommitmentFormat)
|
||||||
val claimHtlcSuccess = ClaimHtlcSuccessWithWitnessData(ClaimHtlcSuccessTx(
|
val claimHtlcSuccess = ClaimHtlcSuccessWithWitnessData(ClaimHtlcSuccessTx(
|
||||||
InputInfo(OutPoint(ByteVector32.Zeroes, 3), TxOut(5000 sat, Script.pay2wsh(htlcSuccessScript)), htlcSuccessScript),
|
InputInfo(OutPoint(TxId(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),
|
Transaction(2, Seq(TxIn(OutPoint(TxId(ByteVector32.Zeroes), 3), ByteVector.empty, 0)), Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey))), 0),
|
||||||
paymentHash,
|
paymentHash,
|
||||||
5,
|
5,
|
||||||
ConfirmationTarget.Absolute(BlockHeight(0))
|
ConfirmationTarget.Absolute(BlockHeight(0))
|
||||||
), preimage)
|
), preimage)
|
||||||
val claimHtlcTimeout = ClaimHtlcTimeoutWithWitnessData(ClaimHtlcTimeoutTx(
|
val claimHtlcTimeout = ClaimHtlcTimeoutWithWitnessData(ClaimHtlcTimeoutTx(
|
||||||
InputInfo(OutPoint(ByteVector32.Zeroes, 7), TxOut(5000 sat, Script.pay2wsh(htlcTimeoutScript)), htlcTimeoutScript),
|
InputInfo(OutPoint(TxId(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),
|
Transaction(2, Seq(TxIn(OutPoint(TxId(ByteVector32.Zeroes), 7), ByteVector.empty, 0)), Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey))), 0),
|
||||||
7,
|
7,
|
||||||
ConfirmationTarget.Absolute(BlockHeight(0))
|
ConfirmationTarget.Absolute(BlockHeight(0))
|
||||||
))
|
))
|
||||||
|
@ -133,8 +134,8 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
|
||||||
txIn = Seq(
|
txIn = Seq(
|
||||||
initialAnchorTx.tx.txIn.head,
|
initialAnchorTx.tx.txIn.head,
|
||||||
// The previous funding attempt added two wallet inputs:
|
// The previous funding attempt added two wallet inputs:
|
||||||
TxIn(OutPoint(randomBytes32(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
|
TxIn(OutPoint(randomTxId(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
|
||||||
TxIn(OutPoint(randomBytes32(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
|
TxIn(OutPoint(randomTxId(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
|
||||||
),
|
),
|
||||||
// And a change output:
|
// And a change output:
|
||||||
txOut = Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey)))
|
txOut = Seq(TxOut(5000 sat, Script.pay2wpkh(PlaceHolderPubKey)))
|
||||||
|
@ -173,9 +174,9 @@ class ReplaceableTxFunderSpec extends TestKitBaseClass with AnyFunSuiteLike {
|
||||||
txIn = Seq(
|
txIn = Seq(
|
||||||
initialHtlcTx.txInfo.tx.txIn.head,
|
initialHtlcTx.txInfo.tx.txIn.head,
|
||||||
// The previous funding attempt added three wallet inputs:
|
// The previous funding attempt added three wallet inputs:
|
||||||
TxIn(OutPoint(randomBytes32(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
|
TxIn(OutPoint(randomTxId(), 3), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
|
||||||
TxIn(OutPoint(randomBytes32(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
|
TxIn(OutPoint(randomTxId(), 1), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig)),
|
||||||
TxIn(OutPoint(randomBytes32(), 5), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
|
TxIn(OutPoint(randomTxId(), 5), ByteVector.empty, 0, Script.witnessPay2wpkh(PlaceHolderPubKey, PlaceHolderSig))
|
||||||
),
|
),
|
||||||
txOut = Seq(
|
txOut = Seq(
|
||||||
initialHtlcTx.txInfo.tx.txOut.head,
|
initialHtlcTx.txInfo.tx.txOut.head,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import akka.pattern.pipe
|
||||||
import akka.testkit.{TestFSMRef, TestProbe}
|
import akka.testkit.{TestFSMRef, TestProbe}
|
||||||
import com.softwaremill.quicklens.ModifyPimp
|
import com.softwaremill.quicklens.ModifyPimp
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.NotificationsLogger.NotifyNodeOperator
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
import fr.acinq.eclair.blockchain.bitcoind.BitcoindService
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
|
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)
|
getMempool().exists(_.txid == txid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import akka.actor.typed.scaladsl.ActorContext
|
||||||
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter}
|
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, TypedActorRefOps, actorRefAdapter}
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import fr.acinq.bitcoin.scalacompat.{OutPoint, SatoshiLong, Transaction, TxIn, TxOut}
|
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.CurrentBlockHeight
|
||||||
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget}
|
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget}
|
||||||
import fr.acinq.eclair.channel.publish
|
import fr.acinq.eclair.channel.publish
|
||||||
|
@ -72,7 +73,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publish final tx") { f =>
|
test("publish final tx") { f =>
|
||||||
import 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)
|
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
|
||||||
txPublisher ! cmd
|
txPublisher ! cmd
|
||||||
val child = factory.expectMsgType[FinalTxPublisherSpawned].actor
|
val child = factory.expectMsgType[FinalTxPublisherSpawned].actor
|
||||||
|
@ -82,7 +83,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publish final tx duplicate") { f =>
|
test("publish final tx duplicate") { f =>
|
||||||
import 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 tx1 = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
|
||||||
val cmd1 = PublishFinalTx(tx1, input, "final-tx", 10 sat, None)
|
val cmd1 = PublishFinalTx(tx1, input, "final-tx", 10 sat, None)
|
||||||
txPublisher ! cmd1
|
txPublisher ! cmd1
|
||||||
|
@ -93,7 +94,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
factory.expectNoMessage(100 millis)
|
factory.expectNoMessage(100 millis)
|
||||||
|
|
||||||
// But a different tx spending the same main input is allowed:
|
// 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)
|
val cmd2 = PublishFinalTx(tx2, input, "another-final-tx", 0 sat, None)
|
||||||
txPublisher ! cmd2
|
txPublisher ! cmd2
|
||||||
factory.expectMsgType[FinalTxPublisherSpawned]
|
factory.expectMsgType[FinalTxPublisherSpawned]
|
||||||
|
@ -103,7 +104,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val confirmBefore = ConfirmationTarget.Absolute(nodeParams.currentBlockHeight + 12)
|
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)
|
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
|
txPublisher ! cmd
|
||||||
val child = factory.expectMsgType[ReplaceableTxPublisherSpawned].actor
|
val child = factory.expectMsgType[ReplaceableTxPublisherSpawned].actor
|
||||||
|
@ -115,7 +116,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val confirmBefore = nodeParams.currentBlockHeight + 12
|
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 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)
|
val cmd = PublishReplaceableTx(anchorTx, null)
|
||||||
txPublisher ! cmd
|
txPublisher ! cmd
|
||||||
|
@ -161,14 +162,14 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("stop publishing attempts when transaction confirms") { f =>
|
test("stop publishing attempts when transaction confirms") { f =>
|
||||||
import 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 tx1 = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
|
||||||
val cmd1 = PublishFinalTx(tx1, input, "final-tx-1", 5 sat, None)
|
val cmd1 = PublishFinalTx(tx1, input, "final-tx-1", 5 sat, None)
|
||||||
txPublisher ! cmd1
|
txPublisher ! cmd1
|
||||||
val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned].actor
|
val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned].actor
|
||||||
attempt1.expectMsgType[FinalTxPublisher.Publish]
|
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)
|
val cmd2 = PublishFinalTx(tx2, input, "final-tx-2", 15 sat, None)
|
||||||
txPublisher ! cmd2
|
txPublisher ! cmd2
|
||||||
val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned].actor
|
val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned].actor
|
||||||
|
@ -189,7 +190,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (wallet input gone)") { f =>
|
test("publishing attempt fails (wallet input gone)") { f =>
|
||||||
import 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 tx1 = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
|
||||||
val cmd1 = PublishFinalTx(tx1, input, "final-tx-1", 0 sat, None)
|
val cmd1 = PublishFinalTx(tx1, input, "final-tx-1", 0 sat, None)
|
||||||
txPublisher ! cmd1
|
txPublisher ! cmd1
|
||||||
|
@ -213,7 +214,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (main input gone)") { f =>
|
test("publishing attempt fails (main input gone)") { f =>
|
||||||
import 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 tx = Transaction(2, TxIn(input, Nil, 0) :: Nil, Nil, 0)
|
||||||
val cmd = PublishFinalTx(tx, input, "final-tx", 0 sat, None)
|
val cmd = PublishFinalTx(tx, input, "final-tx", 0 sat, None)
|
||||||
txPublisher ! cmd
|
txPublisher ! cmd
|
||||||
|
@ -234,7 +235,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val target = nodeParams.currentBlockHeight + 12
|
val target = nodeParams.currentBlockHeight + 12
|
||||||
val input = OutPoint(randomBytes32(), 7)
|
val input = OutPoint(randomTxId(), 7)
|
||||||
val paymentHash = randomBytes32()
|
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)
|
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
|
txPublisher ! cmd
|
||||||
|
@ -254,13 +255,13 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (transaction skipped)") { f =>
|
test("publishing attempt fails (transaction skipped)") { f =>
|
||||||
import 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)
|
val cmd1 = PublishFinalTx(tx1, tx1.txIn.head.outPoint, "final-tx-1", 0 sat, None)
|
||||||
txPublisher ! cmd1
|
txPublisher ! cmd1
|
||||||
val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned]
|
val attempt1 = factory.expectMsgType[FinalTxPublisherSpawned]
|
||||||
attempt1.actor.expectMsgType[FinalTxPublisher.Publish]
|
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)
|
val cmd2 = PublishFinalTx(tx2, tx2.txIn.head.outPoint, "final-tx-2", 5 sat, None)
|
||||||
txPublisher ! cmd2
|
txPublisher ! cmd2
|
||||||
val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned]
|
val attempt2 = factory.expectMsgType[FinalTxPublisherSpawned]
|
||||||
|
@ -281,7 +282,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (unconfirmed conflicting raw transaction)") { f =>
|
test("publishing attempt fails (unconfirmed conflicting raw transaction)") { f =>
|
||||||
import 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)
|
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
|
||||||
txPublisher ! cmd
|
txPublisher ! cmd
|
||||||
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
|
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
|
||||||
|
@ -298,7 +299,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (unconfirmed conflicting replaceable transaction)") { f =>
|
test("publishing attempt fails (unconfirmed conflicting replaceable transaction)") { f =>
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val input = OutPoint(randomBytes32(), 7)
|
val input = OutPoint(randomTxId(), 7)
|
||||||
val paymentHash = randomBytes32()
|
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)
|
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
|
txPublisher ! cmd
|
||||||
|
@ -318,7 +319,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (confirmed conflicting transaction)") { f =>
|
test("publishing attempt fails (confirmed conflicting transaction)") { f =>
|
||||||
import 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)
|
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
|
||||||
txPublisher ! cmd
|
txPublisher ! cmd
|
||||||
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
|
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
|
||||||
|
@ -335,7 +336,7 @@ class TxPublisherSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike {
|
||||||
test("publishing attempt fails (unknown failure)") { f =>
|
test("publishing attempt fails (unknown failure)") { f =>
|
||||||
import 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)
|
val cmd = PublishFinalTx(tx, tx.txIn.head.outPoint, "final-tx", 5 sat, None)
|
||||||
txPublisher ! cmd
|
txPublisher ! cmd
|
||||||
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
|
val attempt = factory.expectMsgType[FinalTxPublisherSpawned]
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.channel.states.a
|
||||||
|
|
||||||
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
|
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
|
||||||
import akka.testkit.{TestFSMRef, TestProbe}
|
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.TestConstants.{Alice, Bob}
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.channel.fsm.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 =>
|
test("recv OpenDualFundedChannel (invalid chain)", Tag(ChannelStateTestsTags.DualFunding), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||||
import f._
|
import f._
|
||||||
val open = alice2bob.expectMsgType[OpenDualFundedChannel]
|
val open = alice2bob.expectMsgType[OpenDualFundedChannel]
|
||||||
val chain = randomBytes32()
|
val chain = BlockHash(randomBytes32())
|
||||||
bob ! open.copy(chainHash = chain)
|
bob ! open.copy(chainHash = chain)
|
||||||
val error = bob2alice.expectMsgType[Error]
|
val error = bob2alice.expectMsgType[Error]
|
||||||
assert(error == Error(open.temporaryChannelId, InvalidChainHash(open.temporaryChannelId, Block.RegtestGenesisBlock.hash, chain).getMessage))
|
assert(error == Error(open.temporaryChannelId, InvalidChainHash(open.temporaryChannelId, Block.RegtestGenesisBlock.hash, chain).getMessage))
|
||||||
|
|
|
@ -18,7 +18,8 @@ package fr.acinq.eclair.channel.states.b
|
||||||
|
|
||||||
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
|
import akka.actor.typed.scaladsl.adapter.ClassicActorRefOps
|
||||||
import akka.testkit.{TestFSMRef, TestProbe}
|
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.SingleKeyOnChainWallet
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchPublished}
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchPublished}
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
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.channel.states.{ChannelStateTestsBase, ChannelStateTestsTags}
|
||||||
import fr.acinq.eclair.io.Peer.OpenChannelResponse
|
import fr.acinq.eclair.io.Peer.OpenChannelResponse
|
||||||
import fr.acinq.eclair.wire.protocol._
|
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.funsuite.FixtureAnyFunSuiteLike
|
||||||
import org.scalatest.{Outcome, Tag}
|
import org.scalatest.{Outcome, Tag}
|
||||||
|
|
||||||
|
@ -219,14 +220,14 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
|
||||||
|
|
||||||
val bobSigs = bob2alice.expectMsgType[TxSignatures]
|
val bobSigs = bob2alice.expectMsgType[TxSignatures]
|
||||||
bob2blockchain.expectMsgType[WatchFundingConfirmed]
|
bob2blockchain.expectMsgType[WatchFundingConfirmed]
|
||||||
bob2alice.forward(alice, bobSigs.copy(txHash = randomBytes32(), witnesses = Nil))
|
bob2alice.forward(alice, bobSigs.copy(txId = randomTxId(), witnesses = Nil))
|
||||||
alice2bob.expectMsgType[Error]
|
alice2bob.expectMsgType[Error]
|
||||||
awaitCond(wallet.rolledback.size == 1)
|
awaitCond(wallet.rolledback.size == 1)
|
||||||
aliceListener.expectMsgType[ChannelAborted]
|
aliceListener.expectMsgType[ChannelAborted]
|
||||||
awaitCond(alice.stateName == CLOSED)
|
awaitCond(alice.stateName == CLOSED)
|
||||||
|
|
||||||
// Bob has sent his signatures already, so he cannot close the channel yet.
|
// 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]
|
bob2alice.expectMsgType[Error]
|
||||||
bob2blockchain.expectNoMessage(100 millis)
|
bob2blockchain.expectNoMessage(100 millis)
|
||||||
assert(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED)
|
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)
|
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._
|
import f._
|
||||||
|
|
||||||
val listener = TestProbe()
|
val listener = TestProbe()
|
||||||
|
|
|
@ -653,10 +653,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||||
|
|
||||||
// splice 1 confirms
|
// splice 1 confirms
|
||||||
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
||||||
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx1.txid)
|
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx1.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
||||||
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx1.txid)
|
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx1.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2blockchain.expectWatchFundingSpent(fundingTx1.txid, Some(Set(fundingTx2.txid, commitAlice1.txid, commitBob1.txid)))
|
alice2blockchain.expectWatchFundingSpent(fundingTx1.txid, Some(Set(fundingTx2.txid, commitAlice1.txid, commitBob1.txid)))
|
||||||
bob2blockchain.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
|
// splice 2 confirms
|
||||||
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
|
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
|
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
|
alice2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
|
||||||
bob2blockchain.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.expectWatchFundingConfirmed(fundingTx1.txid)
|
||||||
bob2blockchain.expectNoMessage(100 millis)
|
bob2blockchain.expectNoMessage(100 millis)
|
||||||
|
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
|
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
|
|
||||||
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
|
awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1)
|
||||||
|
@ -709,10 +709,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||||
|
|
||||||
// splice 2 confirms
|
// splice 2 confirms
|
||||||
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
|
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
|
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
|
alice2blockchain.expectWatchFundingSpent(fundingTx2.txid, Some(Set(commitAlice2.txid, commitBob2.txid)))
|
||||||
bob2blockchain.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
|
// splice 1 confirms on alice, splice 2 confirms on bob
|
||||||
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
||||||
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx1.txid)
|
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx1.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
|
bob2alice.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2blockchain.expectWatchFundingSpent(fundingTx1.txid)
|
alice2blockchain.expectWatchFundingSpent(fundingTx1.txid)
|
||||||
bob2blockchain.expectWatchFundingSpent(fundingTx2.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
|
// splice 2 confirms on bob, splice 1 confirms on alice
|
||||||
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxid == fundingTx2.txid)
|
alice2bob.expectMsgTypeHaving[SpliceLocked](_.fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
||||||
bob2alice.expectNoMessage(100 millis)
|
bob2alice.expectNoMessage(100 millis)
|
||||||
|
@ -1236,7 +1236,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||||
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice)
|
awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice)
|
||||||
val spliceTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localFundingStatus.signedTx_opt.get
|
val spliceTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localFundingStatus.signedTx_opt.get
|
||||||
alice ! WatchPublishedTriggered(spliceTx)
|
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)
|
disconnect(f)
|
||||||
val (channelReestablishAlice, channelReestablishBob) = reconnect(f, interceptFundingDeeplyBuried = false)
|
val (channelReestablishAlice, channelReestablishBob) = reconnect(f, interceptFundingDeeplyBuried = false)
|
||||||
|
@ -1248,7 +1248,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||||
|
|
||||||
alice2bob.expectMsgType[TxSignatures]
|
alice2bob.expectMsgType[TxSignatures]
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == spliceTx.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == spliceTx.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
bob2alice.expectNoMessage(100 millis)
|
bob2alice.expectNoMessage(100 millis)
|
||||||
bob ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
|
bob ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx)
|
||||||
|
@ -1343,58 +1343,58 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||||
|
|
||||||
// splice 1 confirms on alice's side
|
// splice 1 confirms on alice's side
|
||||||
watchConfirmed1a.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
watchConfirmed1a.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
alice2blockchain.expectMsgType[WatchFundingSpent]
|
alice2blockchain.expectMsgType[WatchFundingSpent]
|
||||||
|
|
||||||
disconnect(f)
|
disconnect(f)
|
||||||
reconnect(f)
|
reconnect(f)
|
||||||
|
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
|
|
||||||
// splice 2 confirms on alice's side
|
// splice 2 confirms on alice's side
|
||||||
watchConfirmed2a.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
watchConfirmed2a.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
alice2blockchain.expectMsgType[WatchFundingSpent]
|
alice2blockchain.expectMsgType[WatchFundingSpent]
|
||||||
|
|
||||||
disconnect(f)
|
disconnect(f)
|
||||||
reconnect(f)
|
reconnect(f)
|
||||||
|
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
alice2bob.expectNoMessage(100 millis)
|
alice2bob.expectNoMessage(100 millis)
|
||||||
bob2alice.expectNoMessage(100 millis)
|
bob2alice.expectNoMessage(100 millis)
|
||||||
|
|
||||||
// splice 1 confirms on bob's side
|
// splice 1 confirms on bob's side
|
||||||
watchConfirmed1b.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
watchConfirmed1b.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx1)
|
||||||
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
|
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
bob2blockchain.expectMsgType[WatchFundingSpent]
|
bob2blockchain.expectMsgType[WatchFundingSpent]
|
||||||
|
|
||||||
disconnect(f)
|
disconnect(f)
|
||||||
reconnect(f)
|
reconnect(f)
|
||||||
|
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx1.txid)
|
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx1.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2bob.expectNoMessage(100 millis)
|
alice2bob.expectNoMessage(100 millis)
|
||||||
bob2alice.expectNoMessage(100 millis)
|
bob2alice.expectNoMessage(100 millis)
|
||||||
|
|
||||||
// splice 2 confirms on bob's side
|
// splice 2 confirms on bob's side
|
||||||
watchConfirmed2b.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
watchConfirmed2b.replyTo ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx2)
|
||||||
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
bob2blockchain.expectMsgType[WatchFundingSpent]
|
bob2blockchain.expectMsgType[WatchFundingSpent]
|
||||||
|
|
||||||
// NB: we disconnect *before* transmitting the splice_confirmed to alice
|
// NB: we disconnect *before* transmitting the splice_confirmed to alice
|
||||||
disconnect(f)
|
disconnect(f)
|
||||||
reconnect(f)
|
reconnect(f)
|
||||||
|
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
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
|
// this time alice received the splice_confirmed for funding tx 2
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2bob.expectNoMessage(100 millis)
|
alice2bob.expectNoMessage(100 millis)
|
||||||
|
@ -1403,9 +1403,9 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
||||||
disconnect(f)
|
disconnect(f)
|
||||||
reconnect(f)
|
reconnect(f)
|
||||||
|
|
||||||
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
alice2bob.forward(bob)
|
alice2bob.forward(bob)
|
||||||
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxid == fundingTx2.txid)
|
assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx2.txid)
|
||||||
bob2alice.forward(alice)
|
bob2alice.forward(alice)
|
||||||
alice2bob.expectNoMessage(100 millis)
|
alice2bob.expectNoMessage(100 millis)
|
||||||
bob2alice.expectNoMessage(100 millis)
|
bob2alice.expectNoMessage(100 millis)
|
||||||
|
|
|
@ -3476,7 +3476,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
||||||
alice2blockchain.expectMsgType[WatchOutputSpent], // htlc 3
|
alice2blockchain.expectMsgType[WatchOutputSpent], // htlc 3
|
||||||
alice2blockchain.expectMsgType[WatchOutputSpent], // htlc 4
|
alice2blockchain.expectMsgType[WatchOutputSpent], // htlc 4
|
||||||
alice2blockchain.expectMsgType[WatchOutputSpent], // local anchor
|
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
|
val localCommitPublished = alice.stateData.asInstanceOf[DATA_CLOSING].localCommitPublished.get
|
||||||
assert(watchedOutputs == localCommitPublished.htlcTxs.keySet + localAnchor.txInfo.input.outPoint)
|
assert(watchedOutputs == localCommitPublished.htlcTxs.keySet + localAnchor.txInfo.input.outPoint)
|
||||||
alice2blockchain.expectNoMessage(1 second)
|
alice2blockchain.expectNoMessage(1 second)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
|
||||||
import fr.acinq.bitcoin.ScriptFlags
|
import fr.acinq.bitcoin.ScriptFlags
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
|
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
|
||||||
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, SatoshiLong, Script, Transaction, TxIn, TxOut}
|
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.bitcoind.ZmqWatcher._
|
||||||
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget, FeeratePerKw, FeeratesPerKw}
|
import fr.acinq.eclair.blockchain.fee.{ConfirmationPriority, ConfirmationTarget, FeeratePerKw, FeeratesPerKw}
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
|
@ -380,14 +381,14 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
||||||
channelUpdateListener.expectMsgType[LocalChannelDown]
|
channelUpdateListener.expectMsgType[LocalChannelDown]
|
||||||
|
|
||||||
// scenario 1: bob claims the htlc output from the commit tx using its preimage
|
// 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)
|
alice ! WatchOutputSpentTriggered(claimHtlcSuccessFromCommitTx)
|
||||||
val fulfill1 = alice2relayer.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]]
|
val fulfill1 = alice2relayer.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]]
|
||||||
assert(fulfill1.htlc == htlca1)
|
assert(fulfill1.htlc == htlca1)
|
||||||
assert(fulfill1.result.paymentPreimage == ra1)
|
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)
|
// 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)
|
alice ! WatchOutputSpentTriggered(claimHtlcSuccessTx)
|
||||||
val fulfill2 = alice2relayer.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]]
|
val fulfill2 = alice2relayer.expectMsgType[RES_ADD_SETTLED[Origin, HtlcResult.OnChainFulfill]]
|
||||||
assert(fulfill2.htlc == htlca1)
|
assert(fulfill2.htlc == htlca1)
|
||||||
|
@ -1584,7 +1585,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
||||||
alice2blockchain.expectNoMessage(1 second)
|
alice2blockchain.expectNoMessage(1 second)
|
||||||
|
|
||||||
// bob RBFs his htlc-success with a different transaction
|
// 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)
|
assert(bobHtlcSuccessTx2.txid !== bobHtlcSuccessTx1.tx.txid)
|
||||||
alice ! WatchOutputSpentTriggered(bobHtlcSuccessTx2)
|
alice ! WatchOutputSpentTriggered(bobHtlcSuccessTx2)
|
||||||
awaitCond(alice.stateData.asInstanceOf[DATA_CLOSING].revokedCommitPublished.head.claimHtlcDelayedPenaltyTxs.size == 3)
|
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(
|
val bobHtlcTx = Transaction(
|
||||||
2,
|
2,
|
||||||
Seq(
|
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(0).tx.txIn.head,
|
||||||
bobHtlcTxs(1).tx.txIn.head,
|
bobHtlcTxs(1).tx.txIn.head,
|
||||||
bobHtlcTxs(2).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],
|
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)
|
assert(watchedOutpoints == spentOutpoints)
|
||||||
alice2blockchain.expectNoMessage(1 second)
|
alice2blockchain.expectNoMessage(1 second)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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=")
|
Base64.getDecoder.decode("cHNidP8BAHECAAAAAfZo4nGIyTg77MFmEBkQH1Au3Jl8vzB2WWQGGz/MbyssAAAAAAD9////ArAHPgUAAAAAFgAU6j9yVvLg66Zu3GM/xHbmXT0yvyiAlpgAAAAAABYAFODscQh3N7lmDYyV5yrHpGL2Zd4JAAAAAAABAH0CAAAAAaNdmqUNlziIjSaif3JUcvJWdyF0U5bYq13NMe+LbaBZAAAAAAD9////AjSp1gUAAAAAFgAUjfFMfBg8ulo/874n3+0ode7ka0BAQg8AAAAAACIAIPUn/XU17DfnvDkj8gn2twG3jtr2Z7sthy9K2MPTdYkaAAAAAAEBHzSp1gUAAAAAFgAUjfFMfBg8ulo/874n3+0ode7ka0AiBgM+PDdyxsVisa66SyBxiUvhEam8lEP64yujvVsEcGaqIxgPCfOBVAAAgAEAAIAAAACAAQAAAAMAAAAAIgIDWmAhb/sCV9+HjwFpPuy2TyEBi/Y11wrEHZUihe3N80EYDwnzgVQAAIABAACAAAAAgAEAAAAFAAAAAAA=")
|
||||||
).getRight
|
).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()
|
val tx = psbt1.extract()
|
||||||
assert(tx.isRight)
|
assert(tx.isRight)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.db
|
package fr.acinq.eclair.db
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.TestDatabases.{TestPgDatabases, TestSqliteDatabases, migrationCheck}
|
||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
|
import fr.acinq.eclair.channel.Helpers.Closing.MutualClose
|
||||||
|
@ -865,7 +865,7 @@ class AuditDbSpec extends AnyFunSuite {
|
||||||
val channelId = randomBytes32()
|
val channelId = randomBytes32()
|
||||||
val scid = ShortChannelId(123)
|
val scid = ShortChannelId(123)
|
||||||
val remoteNodeId = randomKey().publicKey
|
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))
|
dbs.audit.addChannelUpdate(ChannelUpdateParametersChanged(null, channelId, remoteNodeId, u))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,11 @@
|
||||||
package fr.acinq.eclair.db
|
package fr.acinq.eclair.db
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.FeatureSupport.Optional
|
||||||
import fr.acinq.eclair.Features.VariableLengthOnion
|
import fr.acinq.eclair.Features.VariableLengthOnion
|
||||||
import fr.acinq.eclair.TestDatabases._
|
import fr.acinq.eclair.TestDatabases._
|
||||||
|
import fr.acinq.eclair.TestUtils.randomTxId
|
||||||
import fr.acinq.eclair.db.jdbc.JdbcUtils.using
|
import fr.acinq.eclair.db.jdbc.JdbcUtils.using
|
||||||
import fr.acinq.eclair.db.pg.PgNetworkDb
|
import fr.acinq.eclair.db.pg.PgNetworkDb
|
||||||
import fr.acinq.eclair.db.sqlite.SqliteNetworkDb
|
import fr.acinq.eclair.db.sqlite.SqliteNetworkDb
|
||||||
|
@ -86,7 +87,7 @@ class NetworkDbSpec extends AnyFunSuite {
|
||||||
val db = dbs.network
|
val db = dbs.network
|
||||||
val sig = ByteVector64.Zeroes
|
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 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))
|
db.addChannel(c, txid, Satoshi(42))
|
||||||
assert(db.listChannels() == SortedMap(c.shortChannelId -> PublicChannel(c, txid, Satoshi(42), None, None, None)))
|
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_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 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_1 = randomTxId()
|
||||||
val txid_2 = randomBytes32()
|
val txid_2 = randomTxId()
|
||||||
val txid_3 = randomBytes32()
|
val txid_3 = randomTxId()
|
||||||
val capacity = 10000 sat
|
val capacity = 10000 sat
|
||||||
|
|
||||||
assert(db.listChannels().toSet == Set.empty)
|
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))
|
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_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, 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, 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)
|
||||||
db.updateChannel(channel_update_1) // duplicate is ignored
|
db.updateChannel(channel_update_1) // duplicate is ignored
|
||||||
|
@ -214,9 +215,9 @@ class NetworkDbSpec extends AnyFunSuite {
|
||||||
val capacity = 10000 sat
|
val capacity = 10000 sat
|
||||||
|
|
||||||
val channels = shortChannelIds.map(id => Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, id, pub, pub, pub, pub, sig, sig, sig, sig))
|
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 updates = shortChannelIds.map(id => template.copy(shortChannelId = id))
|
||||||
val txid = randomBytes32()
|
val txid = randomTxId()
|
||||||
channels.foreach(ca => db.addChannel(ca, txid, capacity))
|
channels.foreach(ca => db.addChannel(ca, txid, capacity))
|
||||||
updates.foreach(u => db.updateChannel(u))
|
updates.foreach(u => db.updateChannel(u))
|
||||||
assert(db.listChannels().keySet == channels.map(_.shortChannelId).toSet)
|
assert(db.listChannels().keySet == channels.map(_.shortChannelId).toSet)
|
||||||
|
@ -391,7 +392,7 @@ object NetworkDbSpec {
|
||||||
data: Array[Byte])
|
data: Array[Byte])
|
||||||
|
|
||||||
case class ChannelTestCase(shortChannelId: ShortChannelId,
|
case class ChannelTestCase(shortChannelId: ShortChannelId,
|
||||||
txid: ByteVector32,
|
txid: TxId,
|
||||||
channel: ChannelAnnouncement,
|
channel: ChannelAnnouncement,
|
||||||
channel_data: Array[Byte],
|
channel_data: Array[Byte],
|
||||||
capacity: Satoshi,
|
capacity: Satoshi,
|
||||||
|
@ -425,7 +426,7 @@ object NetworkDbSpec {
|
||||||
val channel_update_2_data = channel_update_2_opt.map(channelUpdateCodec.encode(_).require.toByteArray)
|
val channel_update_2_data = channel_update_2_opt.map(channelUpdateCodec.encode(_).require.toByteArray)
|
||||||
ChannelTestCase(
|
ChannelTestCase(
|
||||||
shortChannelId = channel.shortChannelId,
|
shortChannelId = channel.shortChannelId,
|
||||||
txid = randomBytes32(),
|
txid = randomTxId(),
|
||||||
channel = channel,
|
channel = channel,
|
||||||
channel_data = channel_data,
|
channel_data = channel_data,
|
||||||
capacity = Random.nextInt(100_000).sat,
|
capacity = Random.nextInt(100_000).sat,
|
||||||
|
|
|
@ -793,7 +793,7 @@ class PaymentsDbSpec extends AnyFunSuite {
|
||||||
object PaymentsDbSpec {
|
object PaymentsDbSpec {
|
||||||
val (alicePriv, bobPriv, carolPriv, davePriv) = (randomKey(), randomKey(), randomKey(), randomKey())
|
val (alicePriv, bobPriv, carolPriv, davePriv) = (randomKey(), randomKey(), randomKey(), randomKey())
|
||||||
val (alice, bob, carol, dave) = (alicePriv.publicKey, bobPriv.publicKey, carolPriv.publicKey, davePriv.publicKey)
|
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 hop_bc = NodeHop(bob, carol, CltvExpiryDelta(14), 1 msat)
|
||||||
val (preimage1, preimage2, preimage3, preimage4) = (randomBytes32(), randomBytes32(), randomBytes32(), randomBytes32())
|
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))
|
val (paymentHash1, paymentHash2, paymentHash3, paymentHash4) = (Crypto.sha256(preimage1), Crypto.sha256(preimage2), Crypto.sha256(preimage3), Crypto.sha256(preimage4))
|
||||||
|
|
|
@ -23,7 +23,7 @@ import akka.testkit.TestProbe
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import fr.acinq.bitcoin.ScriptFlags
|
import fr.acinq.bitcoin.ScriptFlags
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.BitcoindService.BitcoinReq
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
import fr.acinq.eclair.blockchain.bitcoind.rpc.BitcoinCoreClient
|
||||||
import fr.acinq.eclair.channel._
|
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. */
|
/** 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({
|
awaitCond({
|
||||||
bitcoinClient.getMempool().pipeTo(sender.ref)
|
bitcoinClient.getMempool().pipeTo(sender.ref)
|
||||||
val inMempool = sender.expectMsgType[Seq[Transaction]].exists(_.txid == txid)
|
val inMempool = sender.expectMsgType[Seq[Transaction]].exists(_.txid == txid)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import akka.testkit.{TestActor, TestProbe}
|
||||||
import com.softwaremill.quicklens.ModifyPimp
|
import com.softwaremill.quicklens.ModifyPimp
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.ShortChannelId.txIndex
|
||||||
import fr.acinq.eclair.blockchain.DummyOnChainWallet
|
import fr.acinq.eclair.blockchain.DummyOnChainWallet
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
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
|
* 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.
|
* transactions in a independent and stateless fashion, since there is no actual blockchain in those tests.
|
||||||
*/
|
*/
|
||||||
def deterministicShortId(txId: ByteVector32): RealShortChannelId = {
|
def deterministicShortId(txId: TxId): RealShortChannelId = {
|
||||||
val blockHeight = txId.take(3).toInt(signed = false)
|
val blockHeight = txId.value.take(3).toInt(signed = false)
|
||||||
val txIndex = txId.takeRight(2).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
|
val outputIndex = 0 // funding txs created by the dummy wallet used in tests only have one output
|
||||||
RealShortChannelId(BlockHeight(blockHeight), txIndex, outputIndex)
|
RealShortChannelId(BlockHeight(blockHeight), txIndex, outputIndex)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import akka.actor.testkit.typed.scaladsl.{ScalaTestWithActorTestKit, TestProbe}
|
||||||
import akka.actor.typed.eventstream.EventStream.Publish
|
import akka.actor.typed.eventstream.EventStream.Publish
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.channel._
|
||||||
import fr.acinq.eclair.io.PendingChannelsRateLimiter.filterPendingChannels
|
import fr.acinq.eclair.io.PendingChannelsRateLimiter.filterPendingChannels
|
||||||
import fr.acinq.eclair.router.Router
|
import fr.acinq.eclair.router.Router
|
||||||
|
@ -74,11 +74,11 @@ class PendingChannelsRateLimiterSpec extends ScalaTestWithActorTestKit(ConfigFac
|
||||||
val tx = Transaction.read("010000000110f01d4a4228ef959681feb1465c2010d0135be88fd598135b2e09d5413bf6f1000000006a473044022074658623424cebdac8290488b76f893cfb17765b7a3805e773e6770b7b17200102202892cfa9dda662d5eac394ba36fcfd1ea6c0b8bb3230ab96220731967bbdb90101210372d437866d9e4ead3d362b01b615d24cc0d5152c740d51e3c55fb53f6d335d82ffffffff01408b0700000000001976a914678db9a7caa2aca887af1177eda6f3d0f702df0d88ac00000000")
|
val tx = Transaction.read("010000000110f01d4a4228ef959681feb1465c2010d0135be88fd598135b2e09d5413bf6f1000000006a473044022074658623424cebdac8290488b76f893cfb17765b7a3805e773e6770b7b17200102202892cfa9dda662d5eac394ba36fcfd1ea6c0b8bb3230ab96220731967bbdb90101210372d437866d9e4ead3d362b01b615d24cc0d5152c740d51e3c55fb53f6d335d82ffffffff01408b0700000000001976a914678db9a7caa2aca887af1177eda6f3d0f702df0d88ac00000000")
|
||||||
val closingTx = ClosingTx(InputInfo(tx.txIn.head.outPoint, TxOut(10_000 sat, Nil), Nil), tx, None)
|
val closingTx = ClosingTx(InputInfo(tx.txIn.head.outPoint, TxOut(10_000 sat, Nil), Nil), tx, None)
|
||||||
val channelsOnWhitelistAtLimit: Seq[PersistentChannelData] = Seq(
|
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)),
|
DATA_WAIT_FOR_CHANNEL_READY(commitments(peerOnWhitelistAtLimit, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
|
||||||
)
|
)
|
||||||
val channelsAtLimit1 = Seq(
|
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)),
|
DATA_WAIT_FOR_CHANNEL_READY(commitments(peerAtLimit1, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
|
||||||
)
|
)
|
||||||
val channelsAtLimit2 = Seq(
|
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)),
|
DATA_WAIT_FOR_DUAL_FUNDING_READY(commitments(peerAtLimit2, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
|
||||||
)
|
)
|
||||||
val channelsBelowLimit1 = Seq(
|
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(
|
val channelsBelowLimit2 = Seq(
|
||||||
DATA_WAIT_FOR_DUAL_FUNDING_READY(commitments(peerBelowLimit2, channelIdBelowLimit2), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None)),
|
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),
|
DATA_NORMAL(commitments(privatePeer2, randomBytes32()), ShortIds(RealScidStatus.Unknown, ShortChannelId.generateLocalAlias(), None), None, null, None, None, None, SpliceStatus.NoSplice),
|
||||||
)
|
)
|
||||||
val initiatorChannels = Seq(
|
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_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_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)),
|
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_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_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_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))
|
val limiter = testKit.spawn(PendingChannelsRateLimiter(nodeParams, router.ref, channels))
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ package fr.acinq.eclair.json
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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._
|
||||||
import fr.acinq.eclair.balance.CheckBalance
|
import fr.acinq.eclair.balance.CheckBalance
|
||||||
import fr.acinq.eclair.balance.CheckBalance.{ClosingBalance, GlobalBalance, MainAndHtlcBalance, PossiblyPublishedMainAndHtlcBalance, PossiblyPublishedMainBalance}
|
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 {
|
class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Matchers {
|
||||||
|
|
||||||
test("deserialize Map[OutPoint, ByteVector]") {
|
test("deserialize Map[OutPoint, ByteVector]") {
|
||||||
val output1 = OutPoint(ByteVector32(hex"11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
|
val output1 = OutPoint(TxHash.fromValidHex("11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
|
||||||
val output2 = OutPoint(ByteVector32(hex"3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
|
val output2 = OutPoint(TxHash.fromValidHex("3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
|
||||||
val map = Map(
|
val map = Map(
|
||||||
output1 -> hex"dead",
|
output1 -> hex"dead",
|
||||||
output2 -> hex"beef"
|
output2 -> hex"beef"
|
||||||
|
@ -63,8 +63,8 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||||
}
|
}
|
||||||
|
|
||||||
test("deserialize Map[OutPoint, Transaction]") {
|
test("deserialize Map[OutPoint, Transaction]") {
|
||||||
val output1 = OutPoint(ByteVector32(hex"11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
|
val output1 = OutPoint(TxHash.fromValidHex("11418a2d282a40461966e4f578e1fdf633ad15c1b7fb3e771d14361127233be1"), 0)
|
||||||
val output2 = OutPoint(ByteVector32(hex"3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
|
val output2 = OutPoint(TxHash.fromValidHex("3d62bd4f71dc63798418e59efbc7532380c900b5e79db3a5521374b161dd0e33"), 1)
|
||||||
val map = Map(
|
val map = Map(
|
||||||
output1 -> Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800"),
|
output1 -> Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800"),
|
||||||
output2 -> Transaction.read("0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a362184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d875069184ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8ad1afdcf652f900000000")
|
output2 -> Transaction.read("0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a362184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d875069184ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8ad1afdcf652f900000000")
|
||||||
|
@ -121,9 +121,9 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||||
val dummyBytes32 = ByteVector32(hex"0202020202020202020202020202020202020202020202020202020202020202")
|
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 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 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 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(
|
val channelInfo = RES_GET_CHANNEL_INFO(
|
||||||
PublicKey(hex"0270685ca81a8e4d4d01beec5781f4cc924684072ae52c507f8ebe9daf0caaab7b"),
|
PublicKey(hex"0270685ca81a8e4d4d01beec5781f4cc924684072ae52c507f8ebe9daf0caaab7b"),
|
||||||
ByteVector32(hex"345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"),
|
ByteVector32(hex"345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"),
|
||||||
|
@ -276,7 +276,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||||
|
|
||||||
test("InputInfo serialization") {
|
test("InputInfo serialization") {
|
||||||
val inputInfo = InputInfo(
|
val inputInfo = InputInfo(
|
||||||
outPoint = OutPoint(ByteVector32(hex"345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"), 42),
|
outPoint = OutPoint(TxHash.fromValidHex("345b2b05ec046ffe0c14d3b61838c79980713ad1cf8ae7a45c172ce90c9c0b9f"), 42),
|
||||||
txOut = TxOut(456651 sat, hex"3c7a66997c681a3de1bae56438abeee4fc50a16554725a430ade1dc8db6bdd76704d45c6151c4051d710cf487e63"),
|
txOut = TxOut(456651 sat, hex"3c7a66997c681a3de1bae56438abeee4fc50a16554725a430ade1dc8db6bdd76704d45c6151c4051d710cf487e63"),
|
||||||
redeemScript = hex"00dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773"
|
redeemScript = hex"00dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773"
|
||||||
)
|
)
|
||||||
|
@ -331,11 +331,11 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||||
htlcs = Btc(0.05)
|
htlcs = Btc(0.05)
|
||||||
), closing = ClosingBalance(
|
), closing = ClosingBalance(
|
||||||
localCloseBalance = PossiblyPublishedMainAndHtlcBalance(
|
localCloseBalance = PossiblyPublishedMainAndHtlcBalance(
|
||||||
toLocal = Map(ByteVector32(hex"4d176ad844c363bed59edf81962b008faa6194c3b3757ffcd26ba60f95716db2") -> Btc(0.1)),
|
toLocal = Map(TxId.fromValidHex("4d176ad844c363bed59edf81962b008faa6194c3b3757ffcd26ba60f95716db2") -> Btc(0.1)),
|
||||||
htlcs = Map(ByteVector32(hex"94b70cec5a98d67d17c6e3de5c7697f8a6cab4f698df91e633ce35efa3574d71") -> Btc(0.03), ByteVector32(hex"a844edd41ce8503864f3c95d89d850b177a09d7d35e950a7d27c14abb63adb13") -> Btc(0.06)),
|
htlcs = Map(TxId.fromValidHex("94b70cec5a98d67d17c6e3de5c7697f8a6cab4f698df91e633ce35efa3574d71") -> Btc(0.03), TxId.fromValidHex("a844edd41ce8503864f3c95d89d850b177a09d7d35e950a7d27c14abb63adb13") -> Btc(0.06)),
|
||||||
htlcsUnpublished = Btc(0.01)),
|
htlcsUnpublished = Btc(0.01)),
|
||||||
mutualCloseBalance = PossiblyPublishedMainBalance(
|
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}}"""
|
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") {
|
test("TransactionWithInputInfo serializer") {
|
||||||
// the input info is ignored when serializing to JSON
|
// 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 htlcSuccessTx = Transaction.read("0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800")
|
||||||
val htlcSuccessTxInfo = HtlcSuccessTx(dummyInputInfo, htlcSuccessTx, ByteVector32.One, 3, ConfirmationTarget.Absolute(BlockHeight(1105)))
|
val htlcSuccessTxInfo = HtlcSuccessTx(dummyInputInfo, htlcSuccessTx, ByteVector32.One, 3, ConfirmationTarget.Absolute(BlockHeight(1105)))
|
||||||
val htlcSuccessJson =
|
val htlcSuccessJson =
|
||||||
s"""{
|
s"""{
|
||||||
| "txid": "${htlcSuccessTx.txid.toHex}",
|
| "txid": "${htlcSuccessTx.txid.value.toHex}",
|
||||||
| "tx": "0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800",
|
| "tx": "0200000001c8a8934fb38a44b969528252bc37be66ee166c7897c57384d1e561449e110c93010000006b483045022100dc6c50f445ed53d2fb41067fdcb25686fe79492d90e6e5db43235726ace247210220773d35228af0800c257970bee9cf75175d75217de09a8ecd83521befd040c4ca012102082b751372fe7e3b012534afe0bb8d1f2f09c724b1a10a813ce704e5b9c217ccfdffffff0247ba2300000000001976a914f97a7641228e6b17d4b0b08252ae75bd62a95fe788ace3de24000000000017a914a9fefd4b9a9282a1d7a17d2f14ac7d1eb88141d287f7d50800",
|
||||||
| "paymentHash": "0100000000000000000000000000000000000000000000000000000000000000",
|
| "paymentHash": "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
| "htlcId": 3,
|
| "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 claimHtlcTimeoutTxInfo = ClaimHtlcTimeoutTx(dummyInputInfo, claimHtlcTimeoutTx, 2, ConfirmationTarget.Absolute(BlockHeight(144)))
|
||||||
val claimHtlcTimeoutJson =
|
val claimHtlcTimeoutJson =
|
||||||
s"""{
|
s"""{
|
||||||
| "txid": "${claimHtlcTimeoutTx.txid.toHex}",
|
| "txid": "${claimHtlcTimeoutTx.txid.value.toHex}",
|
||||||
| "tx": "010000000110f01d4a4228ef959681feb1465c2010d0135be88fd598135b2e09d5413bf6f1000000006a473044022074658623424cebdac8290488b76f893cfb17765b7a3805e773e6770b7b17200102202892cfa9dda662d5eac394ba36fcfd1ea6c0b8bb3230ab96220731967bbdb90101210372d437866d9e4ead3d362b01b615d24cc0d5152c740d51e3c55fb53f6d335d82ffffffff01408b0700000000001976a914678db9a7caa2aca887af1177eda6f3d0f702df0d88ac00000000",
|
| "tx": "010000000110f01d4a4228ef959681feb1465c2010d0135be88fd598135b2e09d5413bf6f1000000006a473044022074658623424cebdac8290488b76f893cfb17765b7a3805e773e6770b7b17200102202892cfa9dda662d5eac394ba36fcfd1ea6c0b8bb3230ab96220731967bbdb90101210372d437866d9e4ead3d362b01b615d24cc0d5152c740d51e3c55fb53f6d335d82ffffffff01408b0700000000001976a914678db9a7caa2aca887af1177eda6f3d0f702df0d88ac00000000",
|
||||||
| "htlcId": 2,
|
| "htlcId": 2,
|
||||||
| "confirmBeforeBlock": 144
|
| "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 closingTxWithLocalOutput = ClosingTx(dummyInputInfo, closingTx, Some(OutputInfo(1, Satoshi(15000), hex"deadbeef")))
|
||||||
val closingTxWithLocalOutputJson =
|
val closingTxWithLocalOutputJson =
|
||||||
s"""{
|
s"""{
|
||||||
| "txid": "${closingTx.txid.toHex}",
|
| "txid": "${closingTx.txid.value.toHex}",
|
||||||
| "tx": "0100000001be43e9788523ed4de0b24a007a90009bc25e667ddac0e9ee83049be03e220138000000006b483045022100f74dd6ad3e6a00201d266a0ed860a6379c6e68b473970423f3fc8a15caa1ea0f022065b4852c9da230d9e036df743cb743601ca5229e1cb610efdd99769513f2a2260121020636de7755830fb4a3f136e97ecc6c58941611957ba0364f01beae164b945b2fffffffff0150f80c000000000017a9146809053148799a10480eada3d56d15edf4a648c88700000000",
|
| "tx": "0100000001be43e9788523ed4de0b24a007a90009bc25e667ddac0e9ee83049be03e220138000000006b483045022100f74dd6ad3e6a00201d266a0ed860a6379c6e68b473970423f3fc8a15caa1ea0f022065b4852c9da230d9e036df743cb743601ca5229e1cb610efdd99769513f2a2260121020636de7755830fb4a3f136e97ecc6c58941611957ba0364f01beae164b945b2fffffffff0150f80c000000000017a9146809053148799a10480eada3d56d15edf4a648c88700000000",
|
||||||
| "toLocalOutput": {
|
| "toLocalOutput": {
|
||||||
| "index": 1,
|
| "index": 1,
|
||||||
|
@ -398,7 +398,7 @@ class JsonSerializersSpec extends TestKitBaseClass with AnyFunSuiteLike with Mat
|
||||||
val closingTxWithoutLocalOutput = ClosingTx(dummyInputInfo, closingTx, None)
|
val closingTxWithoutLocalOutput = ClosingTx(dummyInputInfo, closingTx, None)
|
||||||
val closingTxWithoutLocalOutputJson =
|
val closingTxWithoutLocalOutputJson =
|
||||||
s"""{
|
s"""{
|
||||||
| "txid": "${closingTx.txid.toHex}",
|
| "txid": "${closingTx.txid.value.toHex}",
|
||||||
| "tx": "0100000001be43e9788523ed4de0b24a007a90009bc25e667ddac0e9ee83049be03e220138000000006b483045022100f74dd6ad3e6a00201d266a0ed860a6379c6e68b473970423f3fc8a15caa1ea0f022065b4852c9da230d9e036df743cb743601ca5229e1cb610efdd99769513f2a2260121020636de7755830fb4a3f136e97ecc6c58941611957ba0364f01beae164b945b2fffffffff0150f80c000000000017a9146809053148799a10480eada3d56d15edf4a648c88700000000"
|
| "tx": "0100000001be43e9788523ed4de0b24a007a90009bc25e667ddac0e9ee83049be03e220138000000006b483045022100f74dd6ad3e6a00201d266a0ed860a6379c6e68b473970423f3fc8a15caa1ea0f022065b4852c9da230d9e036df743cb743601ca5229e1cb610efdd99769513f2a2260121020636de7755830fb4a3f136e97ecc6c58941611957ba0364f01beae164b945b2fffffffff0150f80c000000000017a9146809053148799a10480eada3d56d15edf4a648c88700000000"
|
||||||
|}
|
|}
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package fr.acinq.eclair.payment
|
package fr.acinq.eclair.payment
|
||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.FeatureSupport.{Mandatory, Optional}
|
||||||
import fr.acinq.eclair.Features.{PaymentMetadata, PaymentSecret, _}
|
import fr.acinq.eclair.Features.{PaymentMetadata, PaymentSecret, _}
|
||||||
import fr.acinq.eclair.payment.Bolt11Invoice._
|
import fr.acinq.eclair.payment.Bolt11Invoice._
|
||||||
|
@ -43,7 +43,7 @@ class Bolt11InvoiceSpec extends AnyFunSuite {
|
||||||
assert(nodeId == PublicKey(hex"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad"))
|
assert(nodeId == PublicKey(hex"03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad"))
|
||||||
|
|
||||||
// Copy of Bolt11Invoice.apply that doesn't strip unknown features
|
// Copy of Bolt11Invoice.apply that doesn't strip unknown features
|
||||||
def createInvoiceUnsafe(chainHash: ByteVector32,
|
def createInvoiceUnsafe(chainHash: BlockHash,
|
||||||
amount: Option[MilliSatoshi],
|
amount: Option[MilliSatoshi],
|
||||||
paymentHash: ByteVector32,
|
paymentHash: ByteVector32,
|
||||||
privateKey: PrivateKey,
|
privateKey: PrivateKey,
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.payment
|
||||||
|
|
||||||
import fr.acinq.bitcoin.Bech32
|
import fr.acinq.bitcoin.Bech32
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.FeatureSupport.{Mandatory, Optional}
|
||||||
import fr.acinq.eclair.Features.{BasicMultiPartPayment, VariableLengthOnion}
|
import fr.acinq.eclair.Features.{BasicMultiPartPayment, VariableLengthOnion}
|
||||||
import fr.acinq.eclair.crypto.Sphinx
|
import fr.acinq.eclair.crypto.Sphinx
|
||||||
|
@ -55,7 +55,7 @@ class Bolt12InvoiceSpec extends AnyFunSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("check invoice signature") {
|
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 offer = Offer(Some(10000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
|
||||||
val request = InvoiceRequest(offer, 11000 msat, 1, Features.empty, payerKey, 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)))
|
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") {
|
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 offer = Offer(Some(10000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
|
||||||
val basicRequest = InvoiceRequest(offer, 11000 msat, 1, Features.empty, payerKey, 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"))))
|
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") {
|
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 offer = Offer(Some(10000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
|
||||||
val request = InvoiceRequest(offer, 11000 msat, 1, Features.empty, payerKey, 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)))
|
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") {
|
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 offer = Offer(Some(15000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
|
||||||
val request = InvoiceRequest(offer, 15000 msat, 1, Features.empty, payerKey, 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
|
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") {
|
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 offer = Offer(Some(5000 msat), "test offer", nodeKey.publicKey, Features.empty, chain)
|
||||||
val request = InvoiceRequest(offer, 5000 msat, 1, Features.empty, payerKey, 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)))
|
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.paymentHash == paymentHash)
|
||||||
assert(codedDecoded.relativeExpiry == relativeExpiry.seconds)
|
assert(codedDecoded.relativeExpiry == relativeExpiry.seconds)
|
||||||
assert(codedDecoded.fallbacks.contains(fallbacks))
|
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") {
|
test("minimal tip") {
|
||||||
|
|
|
@ -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.bitcoin.scalacompat.{Block, ByteVector32, Crypto, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, TxOut}
|
||||||
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
|
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
|
||||||
import fr.acinq.eclair.Features._
|
import fr.acinq.eclair.Features._
|
||||||
|
import fr.acinq.eclair.TestUtils.randomTxId
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.channel.fsm.Channel
|
import fr.acinq.eclair.channel.fsm.Channel
|
||||||
import fr.acinq.eclair.crypto.{ShaChain, Sphinx}
|
import fr.acinq.eclair.crypto.{ShaChain, Sphinx}
|
||||||
|
@ -704,7 +705,7 @@ object PaymentPacketSpec {
|
||||||
val channelReserve = testCapacity * 0.01
|
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 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 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 localCommit = LocalCommit(0, null, CommitTxAndRemoteSig(Transactions.CommitTx(commitInput, null), null), Nil)
|
||||||
val remoteCommit = RemoteCommit(0, null, null, randomKey().publicKey)
|
val remoteCommit = RemoteCommit(0, null, null, randomKey().publicKey)
|
||||||
val localChanges = LocalChanges(Nil, Nil, Nil)
|
val localChanges = LocalChanges(Nil, Nil, Nil)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package fr.acinq.eclair.router
|
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.RealShortChannelId
|
||||||
import fr.acinq.eclair.router.Router.{ChannelMeta, PublicChannel}
|
import fr.acinq.eclair.router.Router.{ChannelMeta, PublicChannel}
|
||||||
import fr.acinq.eclair.router.Sync._
|
import fr.acinq.eclair.router.Sync._
|
||||||
|
@ -103,8 +103,8 @@ class ChannelRangeQueriesSpec extends AnyFunSuite {
|
||||||
val ef = RouteCalculationSpec.makeChannel(167514L, e, f)
|
val ef = RouteCalculationSpec.makeChannel(167514L, e, f)
|
||||||
|
|
||||||
val channels = SortedMap(
|
val channels = SortedMap(
|
||||||
ab.shortChannelId -> PublicChannel(ab, ByteVector32.Zeroes, 0 sat, Some(uab1), Some(uab2), Some(ChannelMeta(1000 msat, 400 msat))),
|
ab.shortChannelId -> PublicChannel(ab, TxId(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)
|
cd.shortChannelId -> PublicChannel(cd, TxId(ByteVector32.Zeroes), 0 sat, Some(ucd1), None, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert(getChannelDigestInfo(channels)(ab.shortChannelId) == (Timestamps(now, now), Checksums(3352963162L, 2581904122L)))
|
assert(getChannelDigestInfo(channels)(ab.shortChannelId) == (Timestamps(now, now), Checksums(3352963162L, 2581904122L)))
|
||||||
|
|
|
@ -18,7 +18,7 @@ package fr.acinq.eclair.router
|
||||||
|
|
||||||
import com.softwaremill.quicklens.ModifyPimp
|
import com.softwaremill.quicklens.ModifyPimp
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
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.payment.relay.Relayer.RelayFees
|
||||||
import fr.acinq.eclair.router.Announcements.makeNodeAnnouncement
|
import fr.acinq.eclair.router.Announcements.makeNodeAnnouncement
|
||||||
import fr.acinq.eclair.router.BaseRouterSpec.channelHopFromUpdate
|
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 publicChannels = channels.map { case (shortChannelId, announcement) =>
|
||||||
val HopRelayParams.FromAnnouncement(update) = edges.find(_.desc.shortChannelId == shortChannelId).get.params
|
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 (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)
|
(shortChannelId, pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -903,26 +903,26 @@ class RouteCalculationSpec extends AnyFunSuite with ParallelTestExecution {
|
||||||
val updates = SortedMap(
|
val updates = SortedMap(
|
||||||
RealShortChannelId(BlockHeight(565643), 1216, 0) -> PublicChannel(
|
RealShortChannelId(BlockHeight(565643), 1216, 0) -> PublicChannel(
|
||||||
ann = makeChannel(ShortChannelId.fromCoordinates("565643x1216x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")),
|
ann = makeChannel(ShortChannelId.fromCoordinates("565643x1216x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")),
|
||||||
fundingTxid = ByteVector32.Zeroes,
|
fundingTxId = TxId(ByteVector32.Zeroes),
|
||||||
capacity = DEFAULT_CAPACITY,
|
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_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, 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_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
|
meta_opt = None
|
||||||
),
|
),
|
||||||
RealShortChannelId(BlockHeight(542280), 2156, 0) -> PublicChannel(
|
RealShortChannelId(BlockHeight(542280), 2156, 0) -> PublicChannel(
|
||||||
ann = makeChannel(ShortChannelId.fromCoordinates("542280x2156x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")),
|
ann = makeChannel(ShortChannelId.fromCoordinates("542280x2156x0").success.value.toLong, PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")),
|
||||||
fundingTxid = ByteVector32.Zeroes,
|
fundingTxId = TxId(ByteVector32.Zeroes),
|
||||||
capacity = DEFAULT_CAPACITY,
|
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_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, 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_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
|
meta_opt = None
|
||||||
),
|
),
|
||||||
RealShortChannelId(BlockHeight(565779), 2711, 0) -> PublicChannel(
|
RealShortChannelId(BlockHeight(565779), 2711, 0) -> PublicChannel(
|
||||||
ann = makeChannel(ShortChannelId.fromCoordinates("565779x2711x0").success.value.toLong, PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")),
|
ann = makeChannel(ShortChannelId.fromCoordinates("565779x2711x0").success.value.toLong, PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")),
|
||||||
fundingTxid = ByteVector32.Zeroes,
|
fundingTxId = TxId(ByteVector32.Zeroes),
|
||||||
capacity = DEFAULT_CAPACITY,
|
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_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, 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_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
|
meta_opt = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,7 +20,7 @@ import akka.actor.typed.scaladsl.adapter.actorRefAdapter
|
||||||
import akka.actor.{Actor, Props}
|
import akka.actor.{Actor, Props}
|
||||||
import akka.testkit.{TestFSMRef, TestProbe}
|
import akka.testkit.{TestFSMRef, TestProbe}
|
||||||
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey}
|
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.TestConstants.{Alice, Bob}
|
||||||
import fr.acinq.eclair.RealShortChannelId
|
import fr.acinq.eclair.RealShortChannelId
|
||||||
import fr.acinq.eclair._
|
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 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_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 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)
|
(publicChannel, nodeAnnouncement_1, nodeAnnouncement_2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.acinq.eclair.testutils
|
package fr.acinq.eclair.testutils
|
||||||
|
|
||||||
import akka.testkit.TestProbe
|
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.MilliSatoshi
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchFundingSpent, WatchPublished, WatchTxConfirmed}
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{WatchFundingConfirmed, WatchFundingSpent, WatchPublished, WatchTxConfirmed}
|
||||||
import fr.acinq.eclair.channel.AvailableBalanceChanged
|
import fr.acinq.eclair.channel.AvailableBalanceChanged
|
||||||
|
@ -22,19 +22,19 @@ case class PimpTestProbe(probe: TestProbe) extends Assertions {
|
||||||
msg
|
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 => {
|
expectMsgTypeHaving[WatchFundingSpent](w => {
|
||||||
assert(w.txId == txid, "txid")
|
assert(w.txId == txid, "txid")
|
||||||
hints_opt.foreach(hints => assert(hints == w.hints))
|
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"))
|
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"))
|
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"))
|
expectMsgTypeHaving[WatchPublished](w => assert(w.txId == txid, "txid"))
|
||||||
|
|
||||||
def expectAvailableBalanceChanged(balance: MilliSatoshi, capacity: Satoshi): AvailableBalanceChanged =
|
def expectAvailableBalanceChanged(balance: MilliSatoshi, capacity: Satoshi): AvailableBalanceChanged =
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue