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

moved OldScripts into Scripts

This commit is contained in:
pm47 2016-12-13 14:23:11 +01:00
parent b05f52b412
commit 1e97d4e0a6
17 changed files with 262 additions and 268 deletions

View file

@ -3,7 +3,7 @@ package fr.acinq.eclair.blockchain
import fr.acinq.bitcoin._
import fr.acinq.eclair.blockchain.rpc.{BitcoinJsonRPCClient, JsonRPCError}
import fr.acinq.eclair.{channel, transactions}
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
import org.bouncycastle.util.encoders.Hex
import org.json4s.JsonAST._
@ -93,12 +93,12 @@ class ExtendedBitcoinClient(val client: BitcoinJsonRPCClient) {
publishTransaction(tx2Hex(tx))
def makeAnchorTx(ourCommitPub: BinaryData, theirCommitPub: BinaryData, amount: Satoshi)(implicit ec: ExecutionContext): Future[(Transaction, Int)] = {
val anchorOutputScript = transactions.OldScripts.anchorPubkeyScript(ourCommitPub, theirCommitPub)
val anchorOutputScript = transactions.Common.anchorPubkeyScript(ourCommitPub, theirCommitPub)
val tx = Transaction(version = 2, txIn = Seq.empty[TxIn], txOut = TxOut(amount, anchorOutputScript) :: Nil, lockTime = 0)
val future = for {
FundTransactionResponse(tx1, changepos, fee) <- fundTransaction(tx)
SignTransactionResponse(anchorTx, true) <- signTransaction(tx1)
Some(pos) = OldScripts.findPublicKeyScriptIndex(anchorTx, anchorOutputScript)
Some(pos) = Common.findPublicKeyScriptIndex(anchorTx, anchorOutputScript)
} yield (anchorTx, pos)
future
@ -106,20 +106,20 @@ class ExtendedBitcoinClient(val client: BitcoinJsonRPCClient) {
def makeAnchorTx(fundingPriv: BinaryData, ourCommitPub: BinaryData, theirCommitPub: BinaryData, amount: Btc)(implicit ec: ExecutionContext): Future[(Transaction, Int)] = {
val pub = Crypto.publicKeyFromPrivateKey(fundingPriv)
val script = Script.write(OldScripts.pay2sh(OldScripts.pay2wpkh(pub)))
val script = Script.write(Common.pay2sh(Common.pay2wpkh(pub)))
val address = Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, script)
val future = for {
id <- sendFromAccount("", address, amount.amount.toDouble)
tx <- getTransaction(id)
Some(pos) = OldScripts.findPublicKeyScriptIndex(tx, script)
Some(pos) = Common.findPublicKeyScriptIndex(tx, script)
output = tx.txOut(pos)
anchorOutputScript = transactions.OldScripts.anchorPubkeyScript(ourCommitPub, theirCommitPub)
anchorOutputScript = transactions.Common.anchorPubkeyScript(ourCommitPub, theirCommitPub)
tx1 = Transaction(version = 2, txIn = TxIn(OutPoint(tx, pos), Nil, 0xffffffffL) :: Nil, txOut = TxOut(amount, anchorOutputScript) :: Nil, lockTime = 0)
pubKeyScript = Script.write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
sig = Transaction.signInput(tx1, 0, pubKeyScript, SIGHASH_ALL, output.amount, 1, fundingPriv)
witness = ScriptWitness(Seq(sig, pub))
tx2 = tx1.updateWitness(0, witness)
Some(pos1) = OldScripts.findPublicKeyScriptIndex(tx2, anchorOutputScript)
Some(pos1) = Common.findPublicKeyScriptIndex(tx2, anchorOutputScript)
} yield (tx2, pos1)
future

View file

@ -5,7 +5,7 @@ import akka.pattern.pipe
import fr.acinq.bitcoin._
import fr.acinq.eclair.blockchain.peer.{BlockchainEvent, CurrentBlockCount, NewBlock, NewTransaction}
import fr.acinq.eclair.channel.BITCOIN_FUNDING_SPENT
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
import scala.collection.SortedMap
import scala.concurrent.ExecutionContext
@ -70,8 +70,8 @@ class PeerWatcher(client: ExtendedBitcoinClient, blockCount: Long)(implicit ec:
}
case PublishAsap(tx) =>
val cltvTimeout = OldScripts.cltvTimeout(tx)
val csvTimeout = currentBlockCount + OldScripts.csvTimeout(tx)
val cltvTimeout = Common.cltvTimeout(tx)
val csvTimeout = currentBlockCount + Common.csvTimeout(tx)
val timeout = Math.max(cltvTimeout, csvTimeout)
val block2tx1 = block2tx.updated(timeout, tx +: block2tx.getOrElse(timeout, Seq.empty[Transaction]))
context.become(watching(watches, block2tx1, currentBlockCount))

View file

@ -442,7 +442,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
sender ! "closing already in progress"
stay
} else {
val defaultScriptPubkey: BinaryData = Script.write(OldScripts.pay2wpkh(d.params.localParams.finalPrivKey.point))
val defaultScriptPubkey: BinaryData = Script.write(Common.pay2wpkh(d.params.localParams.finalPrivKey.point))
val ourScriptPubKey = ourScriptPubKey_opt.getOrElse(defaultScriptPubkey)
val ourShutdown = Shutdown(d.channelId, ourScriptPubKey)
them ! ourShutdown
@ -451,7 +451,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(theirShutdown@Shutdown(_, theirScriptPubKey), d@DATA_NORMAL(channelId, params, commitments, ourShutdownOpt, downstreams)) =>
val ourShutdown: Shutdown = ourShutdownOpt.getOrElse {
val defaultScriptPubkey: BinaryData = Script.write(OldScripts.pay2wpkh(params.localParams.finalPrivKey.point))
val defaultScriptPubkey: BinaryData = Script.write(Common.pay2wpkh(params.localParams.finalPrivKey.point))
val c = Shutdown(channelId, defaultScriptPubkey)
them ! c
c

View file

@ -2,7 +2,7 @@ package fr.acinq.eclair.channel
import fr.acinq.bitcoin.{OutPoint, _}
import fr.acinq.eclair.crypto.Generators
import fr.acinq.eclair.transactions.OldScripts._
import fr.acinq.eclair.transactions.Common._
import fr.acinq.eclair.transactions._
import fr.acinq.eclair.wire.UpdateFulfillHtlc
@ -47,7 +47,7 @@ object Helpers {
val remoteTx = CommitmentSpec.makeRemoteTxTemplate(params.localParams, params.remoteParams, commitmentInput :: Nil, remoteFirstPerCommitmentPoint, remoteSpec).makeTx
val localFundingPubkey = params.localParams.fundingPrivkey.point
val fundingTxOutput = TxOut(Satoshi(params.fundingSatoshis), publicKeyScript = OldScripts.anchorPubkeyScript(localFundingPubkey, params.remoteParams.fundingPubkey))
val fundingTxOutput = TxOut(Satoshi(params.fundingSatoshis), publicKeyScript = Common.anchorPubkeyScript(localFundingPubkey, params.remoteParams.fundingPubkey))
(localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput)
}
@ -155,8 +155,8 @@ object Helpers {
val index = tx.txOut.indexOf(htlcTemplate.txOut)
val tx1 = Transaction(version = 2,
txIn = TxIn(OutPoint(tx, index), BinaryData.empty, sequence = OldScripts.toSelfDelay2csv(htlcTemplate.delay)) :: Nil,
txOut = TxOut(htlcTemplate.amount, OldScripts.pay2pkh(Crypto.publicKeyFromPrivateKey(privateKey))) :: Nil,
txIn = TxIn(OutPoint(tx, index), BinaryData.empty, sequence = Common.toSelfDelay2csv(htlcTemplate.delay)) :: Nil,
txOut = TxOut(htlcTemplate.amount, Common.pay2pkh(Crypto.publicKeyFromPrivateKey(privateKey))) :: Nil,
lockTime = ??? /*Scripts.locktime2long_cltv(htlcTemplate.htlc.add.expiry)*/)
val sig = Transaction.signInput(tx1, 0, htlcTemplate.redeemScript, SIGHASH_ALL, htlcTemplate.amount, 1, privateKey)
@ -198,8 +198,8 @@ object Helpers {
val index = tx.txOut.indexOf(htlcTemplate.txOut)
val tx1 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, index), Array.emptyByteArray, sequence = OldScripts.toSelfDelay2csv(htlcTemplate.delay)) :: Nil,
txOut = TxOut(htlcTemplate.amount, OldScripts.pay2pkh(Crypto.publicKeyFromPrivateKey(privateKey))) :: Nil,
txIn = TxIn(OutPoint(tx, index), Array.emptyByteArray, sequence = Common.toSelfDelay2csv(htlcTemplate.delay)) :: Nil,
txOut = TxOut(htlcTemplate.amount, Common.pay2pkh(Crypto.publicKeyFromPrivateKey(privateKey))) :: Nil,
lockTime = ??? /*Scripts.locktime2long_cltv(htlcTemplate.htlc.add.expiry)*/)
val sig = Transaction.signInput(tx1, 0, htlcTemplate.redeemScript, SIGHASH_ALL, htlcTemplate.amount, 1, privateKey)

View file

@ -5,7 +5,7 @@ import akka.util.Timeout
import fr.acinq.bitcoin.{BinaryData, Crypto, DeterministicWallet, Satoshi, Script}
import fr.acinq.eclair.Globals
import fr.acinq.eclair.io.AuthHandler
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
import scala.concurrent.duration._

View file

@ -1,230 +0,0 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin.Crypto._
import fr.acinq.bitcoin._
import fr.acinq.eclair._
/**
* Created by PM on 21/01/2016.
*/
object OldScripts {
def toSelfDelay2csv(in: Int): Long = ???
/*in match {
case locktime(Blocks(blocks)) => blocks
case locktime(Seconds(seconds)) => TxIn.SEQUENCE_LOCKTIME_TYPE_FLAG | (seconds >> TxIn.SEQUENCE_LOCKTIME_GRANULARITY)
}*/
def expiry2cltv(in: Long): Long = ???
/*in match {
case locktime(Blocks(blocks)) => blocks
case locktime(Seconds(seconds)) => seconds
}*/
def isLess(a: Seq[Byte], b: Seq[Byte]): Boolean = memcmp(a.dropWhile(_ == 0).toList, b.dropWhile(_ == 0).toList) < 0
def lessThan(output1: TxOut, output2: TxOut): Boolean = (output1, output2) match {
case (TxOut(amount1, script1), TxOut(amount2, script2)) if amount1 == amount2 => memcmp(script1.toList, script2.toList) < 0
case (TxOut(amount1, _), TxOut(amount2, _)) => amount1.toLong < amount2.toLong
}
def permuteOutputs(tx: Transaction): Transaction = tx.copy(txOut = tx.txOut.sortWith(lessThan))
def multiSig2of2(pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = if (isLess(pubkey1, pubkey2))
Script.createMultiSigMofN(2, Seq(pubkey1, pubkey2))
else
Script.createMultiSigMofN(2, Seq(pubkey2, pubkey1))
def sigScript2of2(sig1: BinaryData, sig2: BinaryData, pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = if (isLess(pubkey1, pubkey2))
Script.write(OP_0 :: OP_PUSHDATA(sig1) :: OP_PUSHDATA(sig2) :: OP_PUSHDATA(multiSig2of2(pubkey1, pubkey2)) :: Nil)
else
Script.write(OP_0 :: OP_PUSHDATA(sig2) :: OP_PUSHDATA(sig1) :: OP_PUSHDATA(multiSig2of2(pubkey1, pubkey2)) :: Nil)
/**
*
* @param sig1
* @param sig2
* @param pubkey1
* @param pubkey2
* @return a script witness that matches the msig 2-of-2 pubkey script for pubkey1 and pubkey2
*/
def witness2of2(sig1: BinaryData, sig2: BinaryData, pubkey1: BinaryData, pubkey2: BinaryData): ScriptWitness = {
if (isLess(pubkey1, pubkey2))
ScriptWitness(Seq(BinaryData.empty, sig1, sig2, multiSig2of2(pubkey1, pubkey2)))
else
ScriptWitness(Seq(BinaryData.empty, sig2, sig1, multiSig2of2(pubkey1, pubkey2)))
}
def pay2pkh(pubKey: BinaryData): Seq[ScriptElt] = OP_DUP :: OP_HASH160 :: OP_PUSHDATA(hash160(pubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil
def pay2sh(script: Seq[ScriptElt]): Seq[ScriptElt] = pay2sh(Script.write(script))
def pay2sh(script: BinaryData): Seq[ScriptElt] = OP_HASH160 :: OP_PUSHDATA(hash160(script)) :: OP_EQUAL :: Nil
def pay2wsh(script: Seq[ScriptElt]): Seq[ScriptElt] = pay2wsh(Script.write(script))
def pay2wsh(script: BinaryData): Seq[ScriptElt] = OP_0 :: OP_PUSHDATA(sha256(script)) :: Nil
def pay2wpkh(pubKey: BinaryData): Seq[ScriptElt] = OP_0 :: OP_PUSHDATA(hash160(pubKey)) :: Nil
/**
*
* @param pubkey1 public key for A
* @param pubkey2 public key for B
* @param amount funding tx amount
* @param previousTx tx that will fund the funding tx; it * must * be a P2PWPK embedded in a standard P2SH tx: the p2sh
* script is just the P2WPK script for the public key that matches our "key" parameter
* @param outputIndex index of the output in the funding tx
* @param key private key that can redeem the funding tx
* @return a signed funding tx
*/
def makeAnchorTx(pubkey1: BinaryData, pubkey2: BinaryData, amount: Long, previousTx: Transaction, outputIndex: Int, key: BinaryData): (Transaction, Int) = {
val tx = Transaction(version = 2,
txIn = TxIn(outPoint = OutPoint(previousTx, outputIndex), signatureScript = Array.emptyByteArray, sequence = 0xffffffffL) :: Nil,
txOut = TxOut(Satoshi(amount), publicKeyScript = pay2wsh(multiSig2of2(pubkey1, pubkey2))) :: Nil,
lockTime = 0)
val pub: BinaryData = Crypto.publicKeyFromPrivateKey(key)
val pkh = OP_0 :: OP_PUSHDATA(Crypto.hash160(pub)) :: Nil
val p2sh: BinaryData = Script.write(pay2sh(pkh))
require(p2sh == previousTx.txOut(outputIndex).publicKeyScript)
val pubKeyScript = Script.write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
val hash = Transaction.hashForSigning(tx, 0, pubKeyScript, SIGHASH_ALL, tx.txOut(0).amount, signatureVersion = 1)
val sig = Crypto.encodeSignature(Crypto.sign(hash, key.take(32))) :+ SIGHASH_ALL.toByte
val witness = ScriptWitness(Seq(sig, pub))
val script = Script.write(OP_0 :: OP_PUSHDATA(Crypto.hash160(pub)) :: Nil)
val signedTx = tx.updateSigScript(0, OP_PUSHDATA(script) :: Nil).updateWitness(0, witness)
// we don't permute outputs because by convention the multisig output has index = 0
(signedTx, 0)
}
def anchorPubkeyScript(pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = Script.write(pay2wsh(multiSig2of2(pubkey1, pubkey2)))
def encodeNumber(n: Long): BinaryData = {
// TODO: added for compatibility with lightningd => check (it's either a bug in lighningd or bitcoin-lib)
if (n < 0xff) Protocol.writeUInt8(n.toInt) else Script.encodeNumber(n)
}
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(encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_DROP :: OP_PUSHDATA(delayedKey) ::
OP_ENDIF ::
OP_CHECKSIG :: Nil
// @formatter:on
}
def scriptPubKeyHtlcSend(ourkey: BinaryData, theirkey: BinaryData, abstimeout: Long, reltimeout: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = {
// values lesser than 16 should be encoded using OP_0..OP_16 instead of OP_PUSHDATA
assert(abstimeout > 16, s"abstimeout=$abstimeout must be greater than 16")
// @formatter:off
OP_SIZE :: OP_PUSHDATA(encodeNumber(32)) :: OP_EQUALVERIFY ::
OP_HASH160 :: OP_DUP ::
OP_PUSHDATA(ripemd160(rhash)) :: OP_EQUAL ::
OP_SWAP :: OP_PUSHDATA(ripemd160(commit_revoke)) :: OP_EQUAL :: OP_ADD ::
OP_IF ::
OP_PUSHDATA(theirkey) ::
OP_ELSE ::
OP_PUSHDATA(encodeNumber(abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_PUSHDATA(encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) ::
OP_ENDIF ::
OP_CHECKSIG :: Nil
// @formatter:on
}
def scriptPubKeyHtlcReceive(ourkey: BinaryData, theirkey: BinaryData, abstimeout: Long, reltimeout: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = {
// values lesser than 16 should be encoded using OP_0..OP_16 instead of OP_PUSHDATA
assert(abstimeout > 16, s"abstimeout=$abstimeout must be greater than 16")
// @formatter:off
OP_SIZE :: OP_PUSHDATA(encodeNumber(32)) :: OP_EQUALVERIFY ::
OP_HASH160 :: OP_DUP ::
OP_PUSHDATA(ripemd160(rhash)) :: OP_EQUAL ::
OP_IF ::
OP_PUSHDATA(encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) ::
OP_ELSE ::
OP_PUSHDATA(ripemd160(commit_revoke)) :: OP_EQUAL ::
OP_NOTIF ::
OP_PUSHDATA(encodeNumber(abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_DROP ::
OP_ENDIF ::
OP_PUSHDATA(theirkey) ::
OP_ENDIF ::
OP_CHECKSIG :: Nil
// @formatter:on
}
def applyFees(amount_us: Satoshi, amount_them: Satoshi, fee: Satoshi) = {
val (amount_us1: Satoshi, amount_them1: Satoshi) = (amount_us, amount_them) match {
case (Satoshi(us), Satoshi(them)) if us >= fee.toLong / 2 && them >= fee.toLong / 2 => (Satoshi(us - fee.toLong / 2), Satoshi(them - fee.toLong / 2))
case (Satoshi(us), Satoshi(them)) if us < fee.toLong / 2 => (Satoshi(0L), Satoshi(Math.max(0L, them - fee.toLong + us)))
case (Satoshi(us), Satoshi(them)) if them < fee.toLong / 2 => (Satoshi(Math.max(us - fee.toLong + them, 0L)), Satoshi(0L))
}
(amount_us1, amount_them1)
}
/**
* Create a "final" channel transaction that will be published when the channel is closed
*
* @param inputs inputs to include in the tx. In most cases, there's only one input that points to the output of
* the funding tx
* @param ourPubkeyScript our public key script
* @param theirPubkeyScript their public key script
* @param amount_us pay to us
* @param amount_them pay to them
* @return an unsigned "final" tx
*/
def makeFinalTx(inputs: Seq[TxIn], ourPubkeyScript: BinaryData, theirPubkeyScript: BinaryData, amount_us: Satoshi, amount_them: Satoshi, fee: Satoshi): Transaction = {
val (amount_us1: Satoshi, amount_them1: Satoshi) = applyFees(amount_us, amount_them, fee)
permuteOutputs(Transaction(
version = 2,
txIn = inputs,
txOut = Seq(
TxOut(amount = amount_us1, publicKeyScript = ourPubkeyScript),
TxOut(amount = amount_them1, publicKeyScript = theirPubkeyScript)
),
lockTime = 0))
}
//def isFunder(o: open_channel): Boolean = o.anch == open_channel.anchor_offer.WILL_CREATE_FUNDING
def findPublicKeyScriptIndex(tx: Transaction, publicKeyScript: BinaryData): Option[Int] =
tx.txOut.zipWithIndex.find {
case (TxOut(_, script), _) => script == publicKeyScript
} map (_._2)
/**
*
* @param tx
* @return the block height before which this tx cannot be published
*/
def cltvTimeout(tx: Transaction): Long = {
require(tx.lockTime <= LockTimeThreshold)
tx.lockTime
}
/**
*
* @param tx
* @return the number of confirmations of the tx parent before which it can be published
*/
def csvTimeout(tx: Transaction): Long = {
def sequenceToBlockHeight(sequence: Long): Long = {
if ((sequence & TxIn.SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) 0
else {
require((sequence & TxIn.SEQUENCE_LOCKTIME_TYPE_FLAG) == 0, "CSV timeout must use block heights, not block times")
sequence & TxIn.SEQUENCE_LOCKTIME_MASK
}
}
if (tx.version < 2) 0
else tx.txIn.map(_.sequence).map(sequenceToBlockHeight).max
}
}

View file

@ -1,10 +1,234 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin.{BinaryData, OP_2, OP_CHECKLOCKTIMEVERIFY, OP_CHECKMULTISIG, OP_CHECKSEQUENCEVERIFY, OP_CHECKSIG, OP_DROP, OP_ELSE, OP_ENDIF, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_IF, OP_NOTIF, OP_PUSHDATA, OP_SIZE, OP_SWAP, Script}
import fr.acinq.bitcoin.Crypto.{hash160, ripemd160, sha256}
import fr.acinq.bitcoin.{BinaryData, Crypto, LockTimeThreshold, OP_0, OP_2, OP_2DROP, OP_ADD, OP_CHECKLOCKTIMEVERIFY, OP_CHECKMULTISIG, OP_CHECKSEQUENCEVERIFY, OP_CHECKSIG, OP_DROP, OP_DUP, OP_ELSE, OP_ENDIF, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_IF, OP_NOTIF, OP_PUSHDATA, OP_SIZE, OP_SWAP, OutPoint, Protocol, SIGHASH_ALL, Satoshi, Script, ScriptElt, ScriptWitness, Transaction, TxIn, TxOut}
import fr.acinq.eclair.memcmp
/**
* Created by PM on 02/12/2016.
*/
object Common {
def toSelfDelay2csv(in: Int): Long = ???
/*in match {
case locktime(Blocks(blocks)) => blocks
case locktime(Seconds(seconds)) => TxIn.SEQUENCE_LOCKTIME_TYPE_FLAG | (seconds >> TxIn.SEQUENCE_LOCKTIME_GRANULARITY)
}*/
def expiry2cltv(in: Long): Long = ???
/*in match {
case locktime(Blocks(blocks)) => blocks
case locktime(Seconds(seconds)) => seconds
}*/
def isLess(a: Seq[Byte], b: Seq[Byte]): Boolean = memcmp(a.dropWhile(_ == 0).toList, b.dropWhile(_ == 0).toList) < 0
def lessThan(output1: TxOut, output2: TxOut): Boolean = (output1, output2) match {
case (TxOut(amount1, script1), TxOut(amount2, script2)) if amount1 == amount2 => memcmp(script1.toList, script2.toList) < 0
case (TxOut(amount1, _), TxOut(amount2, _)) => amount1.toLong < amount2.toLong
}
def permuteOutputs(tx: Transaction): Transaction = tx.copy(txOut = tx.txOut.sortWith(lessThan))
def multiSig2of2(pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = if (isLess(pubkey1, pubkey2))
Script.createMultiSigMofN(2, Seq(pubkey1, pubkey2))
else
Script.createMultiSigMofN(2, Seq(pubkey2, pubkey1))
def sigScript2of2(sig1: BinaryData, sig2: BinaryData, pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = if (isLess(pubkey1, pubkey2))
Script.write(OP_0 :: OP_PUSHDATA(sig1) :: OP_PUSHDATA(sig2) :: OP_PUSHDATA(multiSig2of2(pubkey1, pubkey2)) :: Nil)
else
Script.write(OP_0 :: OP_PUSHDATA(sig2) :: OP_PUSHDATA(sig1) :: OP_PUSHDATA(multiSig2of2(pubkey1, pubkey2)) :: Nil)
/**
*
* @param sig1
* @param sig2
* @param pubkey1
* @param pubkey2
* @return a script witness that matches the msig 2-of-2 pubkey script for pubkey1 and pubkey2
*/
def witness2of2(sig1: BinaryData, sig2: BinaryData, pubkey1: BinaryData, pubkey2: BinaryData): ScriptWitness = {
if (isLess(pubkey1, pubkey2))
ScriptWitness(Seq(BinaryData.empty, sig1, sig2, multiSig2of2(pubkey1, pubkey2)))
else
ScriptWitness(Seq(BinaryData.empty, sig2, sig1, multiSig2of2(pubkey1, pubkey2)))
}
def pay2pkh(pubKey: BinaryData): Seq[ScriptElt] = OP_DUP :: OP_HASH160 :: OP_PUSHDATA(hash160(pubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil
def pay2sh(script: Seq[ScriptElt]): Seq[ScriptElt] = pay2sh(Script.write(script))
def pay2sh(script: BinaryData): Seq[ScriptElt] = OP_HASH160 :: OP_PUSHDATA(hash160(script)) :: OP_EQUAL :: Nil
def pay2wsh(script: Seq[ScriptElt]): Seq[ScriptElt] = pay2wsh(Script.write(script))
def pay2wsh(script: BinaryData): Seq[ScriptElt] = OP_0 :: OP_PUSHDATA(sha256(script)) :: Nil
def pay2wpkh(pubKey: BinaryData): Seq[ScriptElt] = OP_0 :: OP_PUSHDATA(hash160(pubKey)) :: Nil
/**
*
* @param pubkey1 public key for A
* @param pubkey2 public key for B
* @param amount funding tx amount
* @param previousTx tx that will fund the funding tx; it * must * be a P2PWPK embedded in a standard P2SH tx: the p2sh
* script is just the P2WPK script for the public key that matches our "key" parameter
* @param outputIndex index of the output in the funding tx
* @param key private key that can redeem the funding tx
* @return a signed funding tx
*/
def makeAnchorTx(pubkey1: BinaryData, pubkey2: BinaryData, amount: Long, previousTx: Transaction, outputIndex: Int, key: BinaryData): (Transaction, Int) = {
val tx = Transaction(version = 2,
txIn = TxIn(outPoint = OutPoint(previousTx, outputIndex), signatureScript = Array.emptyByteArray, sequence = 0xffffffffL) :: Nil,
txOut = TxOut(Satoshi(amount), publicKeyScript = pay2wsh(multiSig2of2(pubkey1, pubkey2))) :: Nil,
lockTime = 0)
val pub: BinaryData = Crypto.publicKeyFromPrivateKey(key)
val pkh = OP_0 :: OP_PUSHDATA(Crypto.hash160(pub)) :: Nil
val p2sh: BinaryData = Script.write(pay2sh(pkh))
require(p2sh == previousTx.txOut(outputIndex).publicKeyScript)
val pubKeyScript = Script.write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
val hash = Transaction.hashForSigning(tx, 0, pubKeyScript, SIGHASH_ALL, tx.txOut(0).amount, signatureVersion = 1)
val sig = Crypto.encodeSignature(Crypto.sign(hash, key.take(32))) :+ SIGHASH_ALL.toByte
val witness = ScriptWitness(Seq(sig, pub))
val script = Script.write(OP_0 :: OP_PUSHDATA(Crypto.hash160(pub)) :: Nil)
val signedTx = tx.updateSigScript(0, OP_PUSHDATA(script) :: Nil).updateWitness(0, witness)
// we don't permute outputs because by convention the multisig output has index = 0
(signedTx, 0)
}
def anchorPubkeyScript(pubkey1: BinaryData, pubkey2: BinaryData): BinaryData = Script.write(pay2wsh(multiSig2of2(pubkey1, pubkey2)))
def encodeNumber(n: Long): BinaryData = {
// TODO: added for compatibility with lightningd => check (it's either a bug in lighningd or bitcoin-lib)
if (n < 0xff) Protocol.writeUInt8(n.toInt) else Script.encodeNumber(n)
}
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(encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_DROP :: OP_PUSHDATA(delayedKey) ::
OP_ENDIF ::
OP_CHECKSIG :: Nil
// @formatter:on
}
def scriptPubKeyHtlcSend(ourkey: BinaryData, theirkey: BinaryData, abstimeout: Long, reltimeout: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = {
// values lesser than 16 should be encoded using OP_0..OP_16 instead of OP_PUSHDATA
assert(abstimeout > 16, s"abstimeout=$abstimeout must be greater than 16")
// @formatter:off
OP_SIZE :: OP_PUSHDATA(encodeNumber(32)) :: OP_EQUALVERIFY ::
OP_HASH160 :: OP_DUP ::
OP_PUSHDATA(ripemd160(rhash)) :: OP_EQUAL ::
OP_SWAP :: OP_PUSHDATA(ripemd160(commit_revoke)) :: OP_EQUAL :: OP_ADD ::
OP_IF ::
OP_PUSHDATA(theirkey) ::
OP_ELSE ::
OP_PUSHDATA(encodeNumber(abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_PUSHDATA(encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) ::
OP_ENDIF ::
OP_CHECKSIG :: Nil
// @formatter:on
}
def scriptPubKeyHtlcReceive(ourkey: BinaryData, theirkey: BinaryData, abstimeout: Long, reltimeout: Long, rhash: BinaryData, commit_revoke: BinaryData): Seq[ScriptElt] = {
// values lesser than 16 should be encoded using OP_0..OP_16 instead of OP_PUSHDATA
assert(abstimeout > 16, s"abstimeout=$abstimeout must be greater than 16")
// @formatter:off
OP_SIZE :: OP_PUSHDATA(encodeNumber(32)) :: OP_EQUALVERIFY ::
OP_HASH160 :: OP_DUP ::
OP_PUSHDATA(ripemd160(rhash)) :: OP_EQUAL ::
OP_IF ::
OP_PUSHDATA(encodeNumber(reltimeout)) :: OP_CHECKSEQUENCEVERIFY :: OP_2DROP :: OP_PUSHDATA(ourkey) ::
OP_ELSE ::
OP_PUSHDATA(ripemd160(commit_revoke)) :: OP_EQUAL ::
OP_NOTIF ::
OP_PUSHDATA(encodeNumber(abstimeout)) :: OP_CHECKLOCKTIMEVERIFY :: OP_DROP ::
OP_ENDIF ::
OP_PUSHDATA(theirkey) ::
OP_ENDIF ::
OP_CHECKSIG :: Nil
// @formatter:on
}
def applyFees(amount_us: Satoshi, amount_them: Satoshi, fee: Satoshi) = {
val (amount_us1: Satoshi, amount_them1: Satoshi) = (amount_us, amount_them) match {
case (Satoshi(us), Satoshi(them)) if us >= fee.toLong / 2 && them >= fee.toLong / 2 => (Satoshi(us - fee.toLong / 2), Satoshi(them - fee.toLong / 2))
case (Satoshi(us), Satoshi(them)) if us < fee.toLong / 2 => (Satoshi(0L), Satoshi(Math.max(0L, them - fee.toLong + us)))
case (Satoshi(us), Satoshi(them)) if them < fee.toLong / 2 => (Satoshi(Math.max(us - fee.toLong + them, 0L)), Satoshi(0L))
}
(amount_us1, amount_them1)
}
/**
* Create a "final" channel transaction that will be published when the channel is closed
*
* @param inputs inputs to include in the tx. In most cases, there's only one input that points to the output of
* the funding tx
* @param ourPubkeyScript our public key script
* @param theirPubkeyScript their public key script
* @param amount_us pay to us
* @param amount_them pay to them
* @return an unsigned "final" tx
*/
def makeFinalTx(inputs: Seq[TxIn], ourPubkeyScript: BinaryData, theirPubkeyScript: BinaryData, amount_us: Satoshi, amount_them: Satoshi, fee: Satoshi): Transaction = {
val (amount_us1: Satoshi, amount_them1: Satoshi) = applyFees(amount_us, amount_them, fee)
permuteOutputs(Transaction(
version = 2,
txIn = inputs,
txOut = Seq(
TxOut(amount = amount_us1, publicKeyScript = ourPubkeyScript),
TxOut(amount = amount_them1, publicKeyScript = theirPubkeyScript)
),
lockTime = 0))
}
//def isFunder(o: open_channel): Boolean = o.anch == open_channel.anchor_offer.WILL_CREATE_FUNDING
def findPublicKeyScriptIndex(tx: Transaction, publicKeyScript: BinaryData): Option[Int] =
tx.txOut.zipWithIndex.find {
case (TxOut(_, script), _) => script == publicKeyScript
} map (_._2)
/**
*
* @param tx
* @return the block height before which this tx cannot be published
*/
def cltvTimeout(tx: Transaction): Long = {
require(tx.lockTime <= LockTimeThreshold)
tx.lockTime
}
/**
*
* @param tx
* @return the number of confirmations of the tx parent before which it can be published
*/
def csvTimeout(tx: Transaction): Long = {
def sequenceToBlockHeight(sequence: Long): Long = {
if ((sequence & TxIn.SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) 0
else {
require((sequence & TxIn.SEQUENCE_LOCKTIME_TYPE_FLAG) == 0, "CSV timeout must use block heights, not block times")
sequence & TxIn.SEQUENCE_LOCKTIME_MASK
}
}
if (tx.version < 2) 0
else tx.txIn.map(_.sequence).map(sequenceToBlockHeight).max
}
}
object OutputScripts {
def toLocal(revocationPubKey: BinaryData, toSelfDelay: Int, localDelayedKey: BinaryData) = {

View file

@ -14,11 +14,11 @@ object Signature {
def sign(localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Satoshi, tx: Transaction): BinaryData = {
// this is because by convention in bitcoin-core-speak 32B keys are 'uncompressed' and 33B keys (ending by 0x01) are 'compressed'
val localCompressedFundingPrivkey: Seq[Byte] = localParams.fundingPrivkey.data.toSeq :+ 1.toByte
Transaction.signInput(tx, 0, OldScripts.multiSig2of2(localParams.fundingPrivkey.point, remoteParams.fundingPubkey), SIGHASH_ALL, fundingSatoshis, 1, localCompressedFundingPrivkey)
Transaction.signInput(tx, 0, Common.multiSig2of2(localParams.fundingPrivkey.point, remoteParams.fundingPubkey), SIGHASH_ALL, fundingSatoshis, 1, localCompressedFundingPrivkey)
}
def addSigs(tx: Transaction, localFundingPubkey: Point, remoteFundingPubkey: Point, localSig: BinaryData, remoteSig: BinaryData): Transaction = {
val witness = OldScripts.witness2of2(localSig, remoteSig, localFundingPubkey, remoteFundingPubkey)
val witness = Common.witness2of2(localSig, remoteSig, localFundingPubkey, remoteFundingPubkey)
tx.updateWitness(0, witness)
}

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin.{BinaryData, Crypto, OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptElt, Transaction, TxIn, TxOut}
import fr.acinq.eclair.transactions.OldScripts._
import fr.acinq.eclair.transactions.Common._
/**
* Created by PM on 06/12/2016.
@ -101,8 +101,8 @@ case class HTLCTemplate(htlc: Htlc, ourKey: BinaryData, theirKey: BinaryData, de
override def amount = Satoshi(htlc.add.amountMsat / 1000)
override def redeemScript = htlc.direction match {
case IN => Script.write(OldScripts.scriptPubKeyHtlcReceive(ourKey, theirKey, expiry2cltv(htlc.add.expiry), toSelfDelay2csv(delay), htlc.add.paymentHash, revocationHash))
case OUT => Script.write(OldScripts.scriptPubKeyHtlcSend(ourKey, theirKey, expiry2cltv(htlc.add.expiry), toSelfDelay2csv(delay), htlc.add.paymentHash, revocationHash))
case IN => Script.write(Common.scriptPubKeyHtlcReceive(ourKey, theirKey, expiry2cltv(htlc.add.expiry), toSelfDelay2csv(delay), htlc.add.paymentHash, revocationHash))
case OUT => Script.write(Common.scriptPubKeyHtlcSend(ourKey, theirKey, expiry2cltv(htlc.add.expiry), toSelfDelay2csv(delay), htlc.add.paymentHash, revocationHash))
}
override def txOut = TxOut(amount, pay2wsh(redeemScript))

View file

@ -1,7 +1,7 @@
package fr.acinq.protos
import fr.acinq.bitcoin._
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
object Bolt3 {
// TODO: sort tx according to BIP69 (lexicographical ordering)
@ -12,7 +12,7 @@ object Bolt3 {
def weight(tx: Transaction) = 3 * baseSize(tx) + totalSize(tx)
def fundingScript(pubKey1: BinaryData, pubKey2: BinaryData) = OldScripts.multiSig2of2(pubKey1, pubKey2)
def fundingScript(pubKey1: BinaryData, pubKey2: BinaryData) = Common.multiSig2of2(pubKey1, pubKey2)
def toLocal(revocationPubKey: BinaryData, toSelfDelay: Long, localDelayedKey: BinaryData) = {
// @formatter:off

View file

@ -5,7 +5,7 @@ import fr.acinq.bitcoin.{BinaryData, Satoshi, Transaction, TxIn, TxOut}
import fr.acinq.eclair.blockchain.ExtendedBitcoinClient
import fr.acinq.eclair.blockchain.peer.{NewBlock, NewTransaction}
import fr.acinq.eclair.blockchain.rpc.BitcoinJsonRPCClient
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
@ -24,7 +24,7 @@ class TestBitcoinClient()(implicit system: ActorSystem) extends ExtendedBitcoinC
override def makeAnchorTx(ourCommitPub: BinaryData, theirCommitPub: BinaryData, amount: Satoshi)(implicit ec: ExecutionContext): Future[(Transaction, Int)] = {
val anchorTx = Transaction(version = 1,
txIn = Seq.empty[TxIn],
txOut = TxOut(amount, OldScripts.anchorPubkeyScript(ourCommitPub, theirCommitPub)) :: Nil,
txOut = TxOut(amount, Common.anchorPubkeyScript(ourCommitPub, theirCommitPub)) :: Nil,
lockTime = 0
)
Future.successful((anchorTx, 0))

View file

@ -8,7 +8,7 @@ import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.channel.states.{StateSpecBaseClass, StateTestsHelperMethods}
import fr.acinq.eclair.channel.{BITCOIN_FUNDING_DEPTHOK, Data, State, _}
import fr.acinq.eclair.transactions.{IN, OldScripts}
import fr.acinq.eclair.transactions.{IN, Common}
import fr.acinq.eclair.wire.{AcceptChannel, ClosingSigned, CommitSig, Error, FundingCreated, FundingLocked, FundingSigned, OpenChannel, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFulfillHtlc}
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
@ -697,7 +697,7 @@ class NormalStateSpec extends StateSpecBaseClass with StateTestsHelperMethods {
// the punishment tx consumes all output but ours (which already goes to our final key)
assert(punishTx.txIn.size == 5)
// TODO: when changefee is implemented we should set fee = 0 and check against 304 000
assert(punishTx.txOut == Seq(TxOut(Satoshi(301670), Script.write(OldScripts.pay2wpkh(Alice.channelParams.finalPrivKey.point)))))
assert(punishTx.txOut == Seq(TxOut(Satoshi(301670), Script.write(Common.pay2wpkh(Alice.channelParams.finalPrivKey.point)))))
}
}

View file

@ -8,7 +8,7 @@ import fr.acinq.eclair.TestConstants.{Alice, Bob}
import fr.acinq.eclair.blockchain._
import fr.acinq.eclair.channel.states.{StateSpecBaseClass, StateTestsHelperMethods}
import fr.acinq.eclair.channel.{BITCOIN_FUNDING_DEPTHOK, Data, State, _}
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
import fr.acinq.eclair.wire.{AcceptChannel, CommitSig, Error, FundingCreated, FundingLocked, FundingSigned, OpenChannel, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFulfillHtlc}
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
@ -424,7 +424,7 @@ class ShutdownStateSpec extends StateSpecBaseClass with StateTestsHelperMethods
assert(revokedTx.txOut.size == 3)
// the punishment tx consumes all output but ours (which already goes to our final key)
assert(punishTx.txIn.size == 2)
assert(punishTx.txOut == Seq(TxOut(Satoshi(500000), Script.write(OldScripts.pay2wpkh(Alice.channelParams.finalPrivKey.point)))))
assert(punishTx.txOut == Seq(TxOut(Satoshi(500000), Script.write(Common.pay2wpkh(Alice.channelParams.finalPrivKey.point)))))
}
}

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin._
import fr.acinq.eclair.transactions.OldScripts._
import fr.acinq.eclair.transactions.Common._
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin._
import fr.acinq.eclair.transactions.OldScripts._
import fr.acinq.eclair.transactions.Common._
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin._
import fr.acinq.eclair.transactions.OldScripts._
import fr.acinq.eclair.transactions.Common._
import org.junit.runner.RunWith
import org.scalatest.FlatSpec
import org.scalatest.junit.JUnitRunner

View file

@ -7,7 +7,7 @@ import fr.acinq.eclair.blockchain.ExtendedBitcoinClient
import fr.acinq.eclair.blockchain.rpc.BitcoinJsonRPCClient
import fr.acinq.eclair.crypto.Generators
import fr.acinq.eclair.crypto.Generators.Scalar
import fr.acinq.eclair.transactions.OldScripts
import fr.acinq.eclair.transactions.Common
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner
@ -87,7 +87,7 @@ class Bolt3Spec extends FunSuite {
println(s"local sig size: ${localSig.length}")
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, fundingTx.txOut(fundingPos).amount, SigVersion.SIGVERSION_WITNESS_V0, remotePrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = if (OldScripts.isLess(localPubKey, remotePubKey))
val witness = if (Common.isLess(localPubKey, remotePubKey))
ScriptWitness(BinaryData.empty :: localSig :: remoteSig :: redeemScript :: Nil)
else
ScriptWitness(BinaryData.empty :: remoteSig :: localSig :: redeemScript :: Nil)