From b3f5ce4e38123d86a421a793d126774cb30c45d6 Mon Sep 17 00:00:00 2001 From: sstone Date: Wed, 24 Feb 2016 17:32:47 +0100 Subject: [PATCH] upgrade CSV handling --- .../fr/acinq/eclair/channel/Scripts.scala | 14 ++-- .../acinq/eclair/ClaimReceivedHtlcSpec.scala | 34 +++++---- .../fr/acinq/eclair/ClaimSentHtlcSpec.scala | 73 ++++++++++++++----- pom.xml | 2 +- 4 files changed, 82 insertions(+), 41 deletions(-) diff --git a/eclair-demo/src/main/scala/fr/acinq/eclair/channel/Scripts.scala b/eclair-demo/src/main/scala/fr/acinq/eclair/channel/Scripts.scala index 98a4efe1d..cb3c09e21 100644 --- a/eclair-demo/src/main/scala/fr/acinq/eclair/channel/Scripts.scala +++ b/eclair-demo/src/main/scala/fr/acinq/eclair/channel/Scripts.scala @@ -59,19 +59,19 @@ object Scripts { def anchorPubkeyScript(pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = Script.write(pay2sh(multiSig2of2(pubkey1, pubkey2))) - def redeemSecretOrDelay(delayedKey: BinaryData, lockTime: Long, keyIfSecretKnown: BinaryData, hashOfSecret: BinaryData): Seq[ScriptElt] = { + def redeemSecretOrDelay(delayedKey: BinaryData, reltimeout: Long, keyIfSecretKnown: BinaryData, hashOfSecret: BinaryData): Seq[ScriptElt] = { // @formatter:off OP_HASH160 :: OP_PUSHDATA(ripemd160(hashOfSecret)) :: OP_EQUAL :: OP_IF :: OP_PUSHDATA(keyIfSecretKnown) :: OP_ELSE :: - OP_PUSHDATA(Script.encodeNumber(lockTime)) :: OP_CHECKSEQUENCEVERIFY :: OP_DROP :: OP_PUSHDATA(delayedKey) :: + OP_PUSHDATA(Script.encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_DROP :: OP_PUSHDATA(delayedKey) :: OP_ENDIF :: OP_CHECKSIG :: Nil // @formatter:on } - def scriptPubKeyHtlcSend(ourkey: BinaryData, theirkey: BinaryData, htlc_abstimeout: Long, locktime: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = { + def scriptPubKeyHtlcSend(ourkey: BinaryData, theirkey: BinaryData, abstimeout: Long, reltimeout: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = { // @formatter:off OP_HASH160 :: OP_DUP :: OP_PUSHDATA(ripemd160(rhash)) :: OP_EQUAL :: @@ -79,22 +79,22 @@ object Scripts { OP_IF :: OP_PUSHDATA(theirkey) :: OP_ELSE :: - OP_PUSHDATA(Script.encodeNumber(htlc_abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_PUSHDATA(Script.encodeNumber(locktime)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) :: + OP_PUSHDATA(Script.encodeNumber(abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_PUSHDATA(Script.encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) :: OP_ENDIF :: OP_CHECKSIG :: Nil // @formatter:on } - def scriptPubKeyHtlcReceive(ourkey: BinaryData, theirkey: BinaryData, htlc_abstimeout: Long, locktime: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = { + def scriptPubKeyHtlcReceive(ourkey: BinaryData, theirkey: BinaryData, abstimeout: Long, reltimeout: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = { // @formatter:off OP_HASH160 :: OP_DUP :: OP_PUSHDATA(ripemd160(rhash)) :: OP_EQUAL :: OP_IF :: - OP_PUSHDATA(Script.encodeNumber(locktime)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) :: + OP_PUSHDATA(Script.encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) :: OP_ELSE :: OP_PUSHDATA(ripemd160(commit_revoke)) :: OP_EQUAL :: OP_NOTIF :: - OP_PUSHDATA(Script.encodeNumber(htlc_abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_DROP :: + OP_PUSHDATA(Script.encodeNumber(abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_DROP :: OP_ENDIF :: OP_PUSHDATA(theirkey) :: OP_ENDIF :: diff --git a/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimReceivedHtlcSpec.scala b/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimReceivedHtlcSpec.scala index e30b25ed9..3de36cebe 100644 --- a/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimReceivedHtlcSpec.scala +++ b/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimReceivedHtlcSpec.scala @@ -33,7 +33,9 @@ class ClaimReceivedHtlcSpec extends FunSuite { val revokeCommitHash = Crypto.sha256(revokeCommit) } - val htlcScript = scriptPubKeyHtlcReceive(Alice.finalPubKey, Bob.finalPubKey, 1000, 2000, Bob.Rhash, Bob.revokeCommitHash) + val abstimeout = 3000 + val reltimeout = 2000 + val htlcScript = scriptPubKeyHtlcReceive(Alice.finalPubKey, Bob.finalPubKey, abstimeout, reltimeout, Bob.Rhash, Bob.revokeCommitHash) // this tx sends money to our HTLC val tx = Transaction( @@ -44,24 +46,24 @@ class ClaimReceivedHtlcSpec extends FunSuite { // this tx tries to spend the previous tx val tx1 = Transaction( - version = 1, + version = 2, txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, 0xffffffff) :: Nil, txOut = TxOut(10, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil, lockTime = 0) test("Alice can spend this HTLC after a delay if she knows the payment hash") { - val tx1 = Transaction( - version = 1, - txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, 0xffffffff - 2000) :: Nil, + val tx2 = Transaction( + version = 2, + txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, reltimeout + 1) :: Nil, txOut = TxOut(10, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil, - lockTime = 2000) + lockTime = abstimeout + 1) - val sig = Transaction.signInput(tx1, 0, Script.write(htlcScript), SIGHASH_ALL, Alice.finalKey) + val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, Alice.finalKey) val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Bob.R) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil - val tx2 = tx1.updateSigScript(0, Script.write(sigScript)) + val tx3 = tx2.updateSigScript(0, Script.write(sigScript)) val runner = new Script.Runner( - new Script.Context(tx2, 0), + new Script.Context(tx3, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY ) val result = runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) @@ -69,18 +71,18 @@ class ClaimReceivedHtlcSpec extends FunSuite { } test("Blob can spend this HTLC after a delay") { - val tx1 = Transaction( - version = 1, - txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, 0xffffffff - 2000) :: Nil, + val tx2 = Transaction( + version = 2, + txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, reltimeout + 1) :: Nil, txOut = TxOut(10, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil, - lockTime = 2000) + lockTime = abstimeout + 1) - val sig = Transaction.signInput(tx1, 0, Script.write(htlcScript), SIGHASH_ALL, Bob.finalKey) + val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, Bob.finalKey) val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Array.emptyByteArray) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil - val tx2 = tx1.updateSigScript(0, Script.write(sigScript)) + val tx3 = tx2.updateSigScript(0, Script.write(sigScript)) - val runner = new Script.Runner(new Script.Context(tx2, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) + val runner = new Script.Runner(new Script.Context(tx3, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) val result = runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) assert(result) } diff --git a/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimSentHtlcSpec.scala b/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimSentHtlcSpec.scala index 2f54f6ac1..e55a9c68b 100644 --- a/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimSentHtlcSpec.scala +++ b/eclair-demo/src/test/scala/fr/acinq/eclair/ClaimSentHtlcSpec.scala @@ -8,6 +8,7 @@ import org.scalatest.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) class ClaimSentHtlcSpec extends FunSuite { + object Alice { val (_, commitKey) = Base58Check.decode("cVuzKWCszfvjkoJyUasvsrRdECriz8hSd1BDinRNzytwnXmX7m1g") val (_, finalKey) = Base58Check.decode("cRUfvpbRtMSqCFD1ADdvgPn5HfRLYuHCFYAr2noWnaRDNger2AoA") @@ -31,7 +32,9 @@ class ClaimSentHtlcSpec extends FunSuite { val revokeCommitHash = Crypto.sha256(revokeCommit) } - val htlcScript = scriptPubKeyHtlcSend(Alice.finalPubKey, Bob.finalPubKey, 1000, 2000, Alice.revokeCommitHash, Alice.Rhash) + val abstimeout = 3000 + val reltimeout = 2000 + val htlcScript = scriptPubKeyHtlcSend(Alice.finalPubKey, Bob.finalPubKey, abstimeout, reltimeout, Alice.revokeCommitHash, Alice.Rhash) // this tx sends money to our HTLC val tx = Transaction( @@ -48,29 +51,65 @@ class ClaimSentHtlcSpec extends FunSuite { lockTime = 0) test("Alice can spend this HTLC after a delay") { - val tx1 = Transaction( - version = 1, - txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = 0xffffffff - 2000) :: Nil, + val tx2 = Transaction( + version = 2, + txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = reltimeout + 1) :: Nil, txOut = TxOut(10, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil, - lockTime = 2000) + lockTime = abstimeout + 1) - val sig = Transaction.signInput(tx1, 0, Script.write(htlcScript), SIGHASH_ALL, Alice.finalKey) - val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Array.emptyByteArray) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil - val tx2 = tx1.updateSigScript(0, Script.write(sigScript)) + val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, Alice.finalKey) + val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Array.emptyByteArray) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil + val tx3 = tx2.updateSigScript(0, Script.write(sigScript)) - val runner = new Script.Runner(new Script.Context(tx2, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) - val result = runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) - assert(result) + val runner = new Script.Runner(new Script.Context(tx3, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) + val result = runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) + assert(result) + } + + test("Alice cannot spend this HTLC before its absolute timeout") { + val tx2 = Transaction( + version = 2, + txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = reltimeout + 1) :: Nil, + txOut = TxOut(10, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil, + lockTime = abstimeout -1) + + val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, Alice.finalKey) + val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Array.emptyByteArray) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil + val tx3 = tx2.updateSigScript(0, Script.write(sigScript)) + + val runner = new Script.Runner(new Script.Context(tx3, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) + val e = intercept[RuntimeException] { + runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) + } + assert(e.getMessage === "unsatisfied CLTV lock time") + } + + test("Alice cannot spend this HTLC before its relative timeout") { + val tx2 = Transaction( + version = 2, + txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = reltimeout - 1) :: Nil, + txOut = TxOut(10, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil, + lockTime = abstimeout +1) + + val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, Alice.finalKey) + val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Array.emptyByteArray) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil + val tx3 = tx2.updateSigScript(0, Script.write(sigScript)) + + val runner = new Script.Runner(new Script.Context(tx3, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) + val e = intercept[RuntimeException] { + runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) + } + assert(e.getMessage === "unsatisfied CSV lock time") } test("Blob can spend this HTLC if he knows the payment hash") { - val sig = Transaction.signInput(tx1, 0, Script.write(htlcScript), SIGHASH_ALL, Bob.finalKey) - val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Alice.R) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil - val tx2 = tx1.updateSigScript(0, Script.write(sigScript)) + val sig = Transaction.signInput(tx1, 0, Script.write(htlcScript), SIGHASH_ALL, Bob.finalKey) + val sigScript = OP_PUSHDATA(sig) :: OP_PUSHDATA(Alice.R) :: OP_PUSHDATA(Script.write(htlcScript)) :: Nil + val tx2 = tx1.updateSigScript(0, Script.write(sigScript)) - val runner = new Script.Runner(new Script.Context(tx2, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) - val result = runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) - assert(result) + val runner = new Script.Runner(new Script.Context(tx2, 0), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS | ScriptFlags.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | ScriptFlags.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) + val result = runner.verifyScripts(Script.write(sigScript), Script.write(pay2sh(htlcScript))) + assert(result) } test("Blob can spend this HTLC if he knows the revocation hash") { diff --git a/pom.xml b/pom.xml index 4f51e8728..0faa81356 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 2.11 2.4.1 1.3.3 - 0.9.4 + 0.9.5 1.2