mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-23 06:35:11 +01:00
upgrade CSV handling
This commit is contained in:
parent
b530a15cf7
commit
b3f5ce4e38
4 changed files with 82 additions and 41 deletions
|
@ -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 ::
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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") {
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -45,7 +45,7 @@
|
|||
<scala.version.short>2.11</scala.version.short>
|
||||
<akka.version>2.4.1</akka.version>
|
||||
<spray.version>1.3.3</spray.version>
|
||||
<bitcoinlib.version>0.9.4</bitcoinlib.version>
|
||||
<bitcoinlib.version>0.9.5</bitcoinlib.version>
|
||||
<acinqtools.version>1.2</acinqtools.version>
|
||||
</properties>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue