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

now checking final scriptpubkey validity

This commit is contained in:
pm47 2017-01-20 19:03:35 +01:00
parent 2badd3d7c2
commit ef586fe4ad
3 changed files with 40 additions and 6 deletions

View file

@ -447,22 +447,25 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) if d.localShutdown.isDefined =>
handleCommandError(sender, new RuntimeException("closing already in progress"))
stay
case Event(c@CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) if Commitments.localHasChanges(d.commitments) =>
// TODO: simplistic behavior, we could maybe sign+close
handleCommandError(sender, new RuntimeException("cannot close when there are pending changes"))
stay
case Event(CMD_CLOSE(ourScriptPubKey_opt), d: DATA_NORMAL) =>
val localShutdown = Shutdown(d.channelId, Script.write(d.params.localParams.defaultFinalScriptPubKey))
handleCommandSuccess(sender, localShutdown, d.copy(localShutdown = Some(localShutdown)))
ourScriptPubKey_opt.getOrElse(Script.write(d.params.localParams.defaultFinalScriptPubKey)) match {
case finalScriptPubKey if Closing.isValidFinalScriptPubkey(finalScriptPubKey) =>
val localShutdown = Shutdown(d.channelId, finalScriptPubKey)
handleCommandSuccess(sender, localShutdown, d.copy(localShutdown = Some(localShutdown)))
case _ => handleCommandError(sender, new RuntimeException("invalid final script"))
}
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) if commitments.remoteChanges.proposed.size > 0 =>
handleLocalError(new RuntimeException("it is illegal to send a shutdown while having unsigned changes"), d)
case Event(remoteShutdown@Shutdown(_, remoteScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) =>
Try(ourShutdownOpt.map(s => (s, commitments)).getOrElse {
require(Closing.isValidFinalScriptPubkey(remoteScriptPubKey), "invalid final script")
// first if we have pending changes, we need to commit them
val commitments2 = if (Commitments.localHasChanges(commitments)) {
val (commitments1, commit) = Commitments.sendCommit(d.commitments)

View file

@ -9,7 +9,7 @@ import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire.{ClosingSigned, UpdateAddHtlc, UpdateFulfillHtlc}
import scala.util.Try
import scala.util.{Success, Try}
/**
* Created by PM on 20/05/2016.
@ -55,6 +55,16 @@ object Helpers {
object Closing {
def isValidFinalScriptPubkey(scriptPubKey: BinaryData): Boolean = {
Try(Script.parse(scriptPubKey)) match {
case Success(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(pubkeyHash, _) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) if pubkeyHash.size == 20 => true
case Success(OP_HASH160 :: OP_PUSHDATA(scriptHash, _) :: OP_EQUAL :: Nil) if scriptHash.size == 20 => true
case Success(OP_0 :: OP_PUSHDATA(pubkeyHash, _) :: Nil) if pubkeyHash.size == 20 => true
case Success(OP_0 :: OP_PUSHDATA(scriptHash, _) :: Nil) if scriptHash.size == 32 => true
case _ => false
}
}
def makeFirstClosingTx(params: ChannelParams, commitments: Commitments, localScriptPubkey: BinaryData, remoteScriptPubkey: BinaryData): ClosingSigned = {
import commitments._
val closingFee = {
@ -69,6 +79,8 @@ object Helpers {
def makeClosingTx(params: ChannelParams, commitments: Commitments, localScriptPubkey: BinaryData, remoteScriptPubkey: BinaryData, closingFee: Satoshi): (ClosingTx, ClosingSigned) = {
import commitments._
require(isValidFinalScriptPubkey(localScriptPubkey), "invalid localScriptPubkey")
require(isValidFinalScriptPubkey(remoteScriptPubkey), "invalid remoteScriptPubkey")
// TODO: check that
val dustLimitSatoshis = Satoshi(Math.max(localParams.dustLimitSatoshis, remoteParams.dustLimitSatoshis))
val closingTx = Transactions.makeClosingTx(commitInput, localScriptPubkey, remoteScriptPubkey, localParams.isFunder, dustLimitSatoshis, closingFee, localCommit.spec)

View file

@ -648,6 +648,14 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
}
}
test("recv CMD_CLOSE (with invalid final script)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, CMD_CLOSE(Some(BinaryData("00112233445566778899"))))
sender.expectMsg("invalid final script")
}
}
test("recv CMD_CLOSE (with signed sent htlcs)") { case (alice, bob, alice2bob, bob2alice, _, _) =>
within(30 seconds) {
val sender = TestProbe()
@ -693,7 +701,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
test("recv Shutdown (no pending htlcs)") { case (alice, _, alice2bob, _, alice2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()
sender.send(alice, Shutdown(0, "00" * 25))
sender.send(alice, Shutdown(0, Script.write(Bob.channelParams.defaultFinalScriptPubKey)))
alice2bob.expectMsgType[Shutdown]
alice2bob.expectMsgType[ClosingSigned]
awaitCond(alice.stateName == NEGOTIATING)
@ -727,6 +735,17 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
}
}
test("recv Shutdown (with invalid final script)") { case (alice, bob, alice2bob, bob2alice, _, bob2blockchain) =>
within(30 seconds) {
val sender = TestProbe()
sender.send(bob, Shutdown(0, BinaryData("00112233445566778899")))
bob2alice.expectMsgType[Error]
bob2blockchain.expectMsgType[PublishAsap]
bob2blockchain.expectMsgType[WatchConfirmed]
awaitCond(bob.stateName == CLOSING)
}
}
test("recv Shutdown (with signed htlcs)") { case (alice, bob, alice2bob, bob2alice, alice2blockchain, _) =>
within(30 seconds) {
val sender = TestProbe()