Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make signedTx a function #2784

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ object LocalFundingStatus {
override val localSigs_opt: Option[TxSignatures] = None
}
case class DualFundedUnconfirmedFundingTx(sharedTx: SignedSharedTransaction, createdAt: BlockHeight, fundingParams: InteractiveTxParams) extends UnconfirmedFundingTx with NotLocked {
override val signedTx_opt: Option[Transaction] = sharedTx.signedTx_opt
override val signedTx_opt: Option[Transaction] = sharedTx.signedTx_opt(fundingParams)
override val localSigs_opt: Option[TxSignatures] = Some(sharedTx.localSigs)
}
case class ZeroconfPublishedFundingTx(tx: Transaction, localSigs_opt: Option[TxSignatures]) extends UnconfirmedFundingTx with Locked {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
val dfu1 = dfu.copy(sharedTx = fundingTx)
d.commitments.updateLocalFundingStatus(msg.txId, dfu1) match {
case Right((commitments1, _)) =>
log.info("publishing funding tx for channelId={} fundingTxId={}", d.channelId, fundingTx.signedTx.txid)
log.info("publishing funding tx for channelId={} fundingTxId={}", d.channelId, fundingTx.txId)
Metrics.recordSplice(dfu.fundingParams, fundingTx.tx)
stay() using d.copy(commitments = commitments1) storing() calling publishFundingTx(dfu1)
case Left(_) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ trait ChannelOpenDualFunded extends DualFundingHandlers with ErrorHandlers {
// so we cannot close the channel yet.
stay() sending Error(d.channelId, InvalidFundingSignature(d.channelId, Some(unsignedFundingTx.txid)).getMessage)
case Right(fundingTx) =>
log.info("publishing funding tx for channelId={} fundingTxId={}", d.channelId, fundingTx.signedTx.txid)
log.info("publishing funding tx for channelId={} fundingTxId={}", d.channelId, fundingTx.txId)
val dfu1 = d.latestFundingTx.copy(sharedTx = fundingTx)
val d1 = d.modify(_.commitments.active.at(0).localFundingStatus).setTo(dfu1)
stay() using d1 storing() calling publishFundingTx(dfu1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ trait DualFundingHandlers extends CommonFundingHandlers {
this: Channel =>

def publishFundingTx(dualFundedTx: DualFundedUnconfirmedFundingTx): Unit = {
dualFundedTx.sharedTx match {
case _: PartiallySignedSharedTransaction =>
dualFundedTx.signedTx_opt match {
case None =>
log.info("we haven't received remote funding signatures yet: we cannot publish the funding transaction but our peer should publish it")
case fundingTx: FullySignedSharedTransaction =>
case Some(signedTx) =>
// Note that we don't use wallet.commit because we don't want to rollback on failure, since our peer may be able
// to publish and we may be able to RBF.
wallet.publishTransaction(fundingTx.signedTx).onComplete {
case Success(_) => context.system.eventStream.publish(TransactionPublished(dualFundedTx.fundingParams.channelId, remoteNodeId, fundingTx.signedTx, fundingTx.tx.localFees.truncateToSatoshi, "funding"))
wallet.publishTransaction(signedTx).onComplete {
case Success(_) => context.system.eventStream.publish(TransactionPublished(dualFundedTx.fundingParams.channelId, remoteNodeId, signedTx, dualFundedTx.sharedTx.tx.localFees.truncateToSatoshi, "funding"))
case Failure(t) => log.warning("error while publishing funding tx: {}", t.getMessage) // tx may be published by our peer, we can't fail-fast
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,15 @@ object InteractiveTxBuilder {
def txId: TxId
def tx: SharedTransaction
def localSigs: TxSignatures
def signedTx_opt: Option[Transaction]
def signedTx_opt(fundingParams: InteractiveTxParams): Option[Transaction]
}
case class PartiallySignedSharedTransaction(tx: SharedTransaction, localSigs: TxSignatures) extends SignedSharedTransaction {
override val txId: TxId = tx.buildUnsignedTx().txid
override val signedTx_opt: Option[Transaction] = None
override def signedTx_opt(fundingParams: InteractiveTxParams): Option[Transaction] = None
}
case class FullySignedSharedTransaction(tx: SharedTransaction, localSigs: TxSignatures, remoteSigs: TxSignatures, sharedSigs_opt: Option[ScriptWitness]) extends SignedSharedTransaction {
val signedTx: Transaction = {
override val txId: TxId = localSigs.txId
override def signedTx_opt(fundingParams: InteractiveTxParams): Option[Transaction] = {
import tx._
val sharedTxIn = sharedInput_opt.map(i => (i.serialId, TxIn(i.outPoint, ByteVector.empty, i.sequence, sharedSigs_opt.getOrElse(ScriptWitness.empty)))).toSeq
val localTxIn = localInputs.sortBy(_.serialId).zip(localSigs.witnesses).map { case (i, w) => (i.serialId, TxIn(i.outPoint, ByteVector.empty, i.sequence, w)) }
Expand All @@ -328,11 +329,8 @@ object InteractiveTxBuilder {
val localTxOut = localOutputs.map(o => (o.serialId, TxOut(o.amount, o.pubkeyScript)))
val remoteTxOut = remoteOutputs.map(o => (o.serialId, TxOut(o.amount, o.pubkeyScript)))
val outputs = (Seq(sharedTxOut) ++ localTxOut ++ remoteTxOut).sortBy(_._1).map(_._2)
Transaction(2, inputs, outputs, lockTime)
Some(Transaction(2, inputs, outputs, lockTime))
}
override val txId: TxId = signedTx.txid
override val signedTx_opt: Option[Transaction] = Some(signedTx)
val feerate: FeeratePerKw = Transactions.fee2rate(tx.fees, signedTx.weight())
}
// @formatter:on

Expand Down Expand Up @@ -917,26 +915,33 @@ object InteractiveTxSigningSession {
}
case None => None
}
val txWithSigs = FullySignedSharedTransaction(partiallySignedTx.tx, partiallySignedTx.localSigs, remoteSigs, sharedSigs_opt)
if (remoteSigs.txId != txWithSigs.signedTx.txid) {
log.info("invalid tx_signatures: txId mismatch (expected={}, got={})", txWithSigs.signedTx.txid, remoteSigs.txId)
if (remoteSigs.txId != partiallySignedTx.txId) {
log.info("invalid tx_signatures: txId mismatch (expected={}, got={})", partiallySignedTx.txId, remoteSigs.txId)
return Left(InvalidFundingSignature(fundingParams.channelId, Some(partiallySignedTx.txId)))
}
// We allow a 5% error margin since witness size prediction could be inaccurate.
if (fundingParams.localContribution != 0.sat && txWithSigs.feerate < fundingParams.targetFeerate * 0.95) {
return Left(InvalidFundingFeerate(fundingParams.channelId, fundingParams.targetFeerate, txWithSigs.feerate))
}
val previousOutputs = {
val sharedOutput = fundingParams.sharedInput_opt.map(sharedInput => sharedInput.info.outPoint -> sharedInput.info.txOut).toMap
val localOutputs = txWithSigs.tx.localInputs.map(i => i.outPoint -> i.previousTx.txOut(i.previousTxOutput.toInt)).toMap
val remoteOutputs = txWithSigs.tx.remoteInputs.map(i => i.outPoint -> i.txOut).toMap
sharedOutput ++ localOutputs ++ remoteOutputs
}
Try(Transaction.correctlySpends(txWithSigs.signedTx, previousOutputs, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) match {
case Failure(f) =>
log.info("invalid tx_signatures: {}", f.getMessage)
val txWithSigs = FullySignedSharedTransaction(partiallySignedTx.tx, partiallySignedTx.localSigs, remoteSigs, sharedSigs_opt)
txWithSigs.signedTx_opt(fundingParams) match {
case Some(signedTx) =>
val feerate = Transactions.fee2rate(txWithSigs.tx.fees, signedTx.weight())
// We allow a 5% error margin since witness size prediction could be inaccurate.
if (fundingParams.localContribution != 0.sat && feerate < fundingParams.targetFeerate * 0.95) {
return Left(InvalidFundingFeerate(fundingParams.channelId, fundingParams.targetFeerate, feerate))
}
val previousOutputs = {
val sharedOutput = fundingParams.sharedInput_opt.map(sharedInput => sharedInput.info.outPoint -> sharedInput.info.txOut).toMap
val localOutputs = txWithSigs.tx.localInputs.map(i => i.outPoint -> i.previousTx.txOut(i.previousTxOutput.toInt)).toMap
val remoteOutputs = txWithSigs.tx.remoteInputs.map(i => i.outPoint -> i.txOut).toMap
sharedOutput ++ localOutputs ++ remoteOutputs
}
Comment on lines +930 to +935
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move this to SharedTransaction right away to minimize diff in feature branch?

Try(Transaction.correctlySpends(signedTx, previousOutputs, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)) match {
case Failure(f) =>
log.info("invalid tx_signatures: {}", f.getMessage)
Left(InvalidFundingSignature(fundingParams.channelId, Some(partiallySignedTx.txId)))
case Success(_) => Right(txWithSigs)
}
case None =>
log.info("invalid tx_signatures: couldn't create signed transaction")
Left(InvalidFundingSignature(fundingParams.channelId, Some(partiallySignedTx.txId)))
case Success(_) => Right(txWithSigs)
}
}

Expand Down
Loading