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

updated bitcoin-lib to 0.9.8

This commit is contained in:
pm47 2016-12-19 16:15:50 +01:00
parent 4fb26c6c89
commit e1ae6d2a2e
24 changed files with 110 additions and 579 deletions

View file

@ -79,7 +79,7 @@
<dependency>
<groupId>fr.acinq</groupId>
<artifactId>bitcoin-lib_${scala.version.short}</artifactId>
<version>0.9.8-SNAPSHOT</version>
<version>${bitcoinlib.version}</version>
</dependency>
<!-- SERIALIZATION -->
<dependency>

View file

@ -16,9 +16,9 @@ object Globals {
val seed: BinaryData = config.getString("eclair.node.seed")
val master = DeterministicWallet.generate(seed)
val extendedPrivateKey = DeterministicWallet.derivePrivateKey(master, DeterministicWallet.hardened(46) :: DeterministicWallet.hardened(0) :: Nil)
val privateKey = extendedPrivateKey.secretkey
val privateKey = extendedPrivateKey.privateKey
val extendedPublicKey = DeterministicWallet.publicKey(extendedPrivateKey)
val publicKey = extendedPublicKey.publickey
val publicKey = extendedPublicKey.publicKey
val id = publicKey.toString()
}

View file

@ -1,6 +1,7 @@
package fr.acinq.eclair.blockchain
import fr.acinq.bitcoin._
import fr.acinq.bitcoin.Script._
import fr.acinq.eclair.blockchain.rpc.{BitcoinJsonRPCClient, JsonRPCError}
import fr.acinq.eclair.transactions
import fr.acinq.eclair.transactions.Scripts
@ -93,7 +94,7 @@ class ExtendedBitcoinClient(val client: BitcoinJsonRPCClient) {
publishTransaction(tx2Hex(tx))
def makeAnchorTx(ourCommitPub: BinaryData, theirCommitPub: BinaryData, amount: Satoshi)(implicit ec: ExecutionContext): Future[(Transaction, Int)] = {
val anchorOutputScript = Script.write(Scripts.pay2wsh(Scripts.multiSig2of2(ourCommitPub, theirCommitPub)))
val anchorOutputScript = write(pay2wsh(Scripts.multiSig2of2(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)
@ -106,16 +107,16 @@ 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(Scripts.pay2sh(Scripts.pay2wpkh(pub)))
val script = write(pay2sh(pay2wpkh(pub)))
val address = Base58Check.encode(Base58.Prefix.ScriptAddressTestnet, script)
val future = for {
id <- sendFromAccount("", address, amount.amount.toDouble)
tx <- getTransaction(id)
Some(pos) = Scripts.findPublicKeyScriptIndex(tx, script)
output = tx.txOut(pos)
anchorOutputScript = Script.write(Scripts.pay2wsh(Scripts.multiSig2of2(ourCommitPub, theirCommitPub)))
anchorOutputScript = write(pay2wsh(Scripts.multiSig2of2(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)
pubKeyScript = write(OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub.toBin)) :: 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)

View file

@ -79,10 +79,10 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
feeratePerKw = localParams.feeratePerKw,
toSelfDelay = localParams.toSelfDelay,
maxAcceptedHtlcs = localParams.maxAcceptedHtlcs,
fundingPubkey = localParams.fundingPrivkey.point,
revocationBasepoint = localParams.revocationSecret.point,
paymentBasepoint = localParams.paymentSecret.point,
delayedPaymentBasepoint = localParams.delayedPaymentKey.point,
fundingPubkey = localParams.fundingPrivkey.toPoint,
revocationBasepoint = localParams.revocationSecret.toPoint,
paymentBasepoint = localParams.paymentSecret.toPoint,
delayedPaymentBasepoint = localParams.delayedPaymentKey.toPoint,
firstPerCommitmentPoint = firstPerCommitmentPoint)
goto(WAIT_FOR_ACCEPT_CHANNEL) using DATA_WAIT_FOR_ACCEPT_CHANNEL(temporaryChannelId, localParams, fundingSatoshis = fundingSatoshis, pushMsat = pushMsat, autoSignInterval = autoSignInterval)
@ -105,10 +105,10 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
htlcMinimumMsat = localParams.htlcMinimumMsat,
toSelfDelay = localParams.toSelfDelay,
maxAcceptedHtlcs = localParams.maxAcceptedHtlcs,
fundingPubkey = localParams.fundingPrivkey.point,
revocationBasepoint = localParams.revocationSecret.point,
paymentBasepoint = localParams.paymentSecret.point,
delayedPaymentBasepoint = localParams.delayedPaymentKey.point,
fundingPubkey = localParams.fundingPrivkey.toPoint,
revocationBasepoint = localParams.revocationSecret.toPoint,
paymentBasepoint = localParams.paymentSecret.toPoint,
delayedPaymentBasepoint = localParams.delayedPaymentKey.toPoint,
firstPerCommitmentPoint = firstPerCommitmentPoint)
val remoteParams = RemoteParams(
dustLimitSatoshis = open.dustLimitSatoshis,
@ -162,7 +162,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
fundingSatoshis = fundingSatoshis,
minimumDepth = accept.minimumDepth,
autoSignInterval = autoSignInterval)
val localFundingPubkey = params.localParams.fundingPrivkey.point
val localFundingPubkey = params.localParams.fundingPrivkey.toPoint
blockchain ! MakeFundingTx(localFundingPubkey, remoteParams.fundingPubkey, Satoshi(params.fundingSatoshis))
goto(WAIT_FOR_FUNDING_CREATED_INTERNAL) using DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, params, pushMsat, accept.firstPerCommitmentPoint)
@ -204,7 +204,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
// check remote signature validity
val localSigOfLocalTx = Transactions.sign(localCommitTx, localParams.fundingPrivkey)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivkey.point, params.remoteParams.fundingPubkey, localSigOfLocalTx, remoteSig)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivkey.toPoint, params.remoteParams.fundingPubkey, localSigOfLocalTx, remoteSig)
Transactions.checkSig(signedLocalCommitTx) match {
case Failure(cause) =>
log.error(cause, "their FundingCreated message contains an invalid signature")
@ -245,7 +245,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
case Event(FundingSigned(_, remoteSig), DATA_WAIT_FOR_FUNDING_SIGNED(temporaryChannelId, params, fundingTx, localSpec, localCommitTx, remoteCommit)) =>
// we make sure that their sig checks out and that our first commit tx is spendable
val localSigOfLocalTx = Transactions.sign(localCommitTx, localParams.fundingPrivkey)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivkey.point, params.remoteParams.fundingPubkey, localSigOfLocalTx, remoteSig)
val signedLocalCommitTx = Transactions.addSigs(localCommitTx, params.localParams.fundingPrivkey.toPoint, params.remoteParams.fundingPubkey, localSigOfLocalTx, remoteSig)
Transactions.checkSig(signedLocalCommitTx) match {
case Failure(cause) =>
log.error(cause, "their FundingSigned message contains an invalid signature")
@ -443,7 +443,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, paymentHandler: Acto
sender ! "closing already in progress"
stay
} else {
val defaultScriptPubkey: BinaryData = Script.write(Scripts.pay2wpkh(d.params.localParams.finalPrivKey.point))
val defaultScriptPubkey: BinaryData = Script.write(Script.pay2wpkh(d.params.localParams.finalPrivKey.toPoint))
val ourScriptPubKey = ourScriptPubKey_opt.getOrElse(defaultScriptPubkey)
val ourShutdown = Shutdown(d.channelId, ourScriptPubKey)
them ! ourShutdown
@ -452,7 +452,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(Scripts.pay2wpkh(params.localParams.finalPrivKey.point))
val defaultScriptPubkey: BinaryData = Script.write(Script.pay2wpkh(params.localParams.finalPrivKey.toPoint))
val c = Shutdown(channelId, defaultScriptPubkey)
them ! c
c

View file

@ -1,7 +1,7 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.Crypto.{Point, Scalar}
import fr.acinq.bitcoin.{BinaryData, Transaction}
import fr.acinq.eclair.crypto.Generators.{Point, Scalar}
import fr.acinq.eclair.transactions.CommitmentSpec
import fr.acinq.eclair.transactions.Transactions.CommitTx
import fr.acinq.eclair.wire.{ClosingSigned, FundingLocked, Shutdown}

View file

@ -1,6 +1,7 @@
package fr.acinq.eclair.channel
import fr.acinq.bitcoin.{OutPoint, _}
import fr.acinq.bitcoin.Script._
import fr.acinq.eclair.crypto.Generators
import fr.acinq.eclair.transactions.Scripts._
import fr.acinq.eclair.transactions.Transactions.{CommitTx, InputInfo}
@ -19,7 +20,7 @@ object Helpers {
def makeFundingInputInfo(fundingTxId: BinaryData, fundingTxOutputIndex: Int, fundingSatoshis: Satoshi, fundingPubkey1: BinaryData, fundingPubkey2: BinaryData): InputInfo = {
val fundingScript = multiSig2of2(fundingPubkey1, fundingPubkey2)
val fundingTxOut = TxOut(fundingSatoshis, pay2wsh(fundingScript))
InputInfo(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, fundingScript)
InputInfo(OutPoint(fundingTxId, fundingTxOutputIndex), fundingTxOut, write(fundingScript))
}
/**
@ -41,9 +42,9 @@ object Helpers {
val localSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.localParams.feeratePerKw, to_local_msat = toLocalMsat, to_remote_msat = toRemoteMsat)
val remoteSpec = CommitmentSpec(Set.empty[Htlc], feeRate = params.remoteParams.feeratePerKw, to_local_msat = toRemoteMsat, to_remote_msat = toLocalMsat)
val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, Satoshi(params.fundingSatoshis), params.localParams.fundingPrivkey.point, params.remoteParams.fundingPubkey)
val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, Satoshi(params.fundingSatoshis), params.localParams.fundingPrivkey.toPoint, params.remoteParams.fundingPubkey)
val localPerCommitmentPoint = Generators.perCommitPoint(params.localParams.shaSeed, 0)
val localTxTemplate = CommitmentSpec.makeLocalTxs(params.localParams, params.remoteParams, commitmentInput, localPerCommitmentPoint.data, localSpec)
val localTxTemplate = CommitmentSpec.makeLocalTxs(params.localParams, params.remoteParams, commitmentInput, localPerCommitmentPoint, localSpec)
val (remoteTxTemplate, _, _) = CommitmentSpec.makeRemoteTxs(params.localParams, params.remoteParams, commitmentInput, remoteFirstPerCommitmentPoint, remoteSpec)
(localSpec, localTxTemplate, remoteSpec, remoteTxTemplate)

View file

@ -37,7 +37,7 @@ class Register(blockchain: ActorRef, paymentHandler: ActorRef) extends Actor wit
def main(counter: Long): Receive = {
case CreateChannel(connection, amount_opt) =>
def generateKey(index: Long): BinaryData = DeterministicWallet.derivePrivateKey(Globals.Node.extendedPrivateKey, index :: counter :: Nil).secretkey
def generateKey(index: Long): BinaryData = DeterministicWallet.derivePrivateKey(Globals.Node.extendedPrivateKey, index :: counter :: Nil).privateKey
val localParams = LocalParams(
dustLimitSatoshis = 542,
maxHtlcValueInFlightMsat = Long.MaxValue,

View file

@ -1,9 +1,7 @@
package fr.acinq.eclair.crypto
import java.math.BigInteger
import fr.acinq.bitcoin.Crypto.{Point, Scalar}
import fr.acinq.bitcoin.{BinaryData, Crypto}
import org.bouncycastle.math.ec.ECPoint
/**
* Created by PM on 07/12/2016.
@ -15,49 +13,19 @@ object Generators {
case length if length < 32 => Array.fill(32 - length)(0.toByte) ++ data
}
case class Scalar(data: BinaryData) {
require(data.length == 32)
def perCommitSecret(seed: BinaryData, index: Int): Scalar = Scalar(ShaChain.shaChainFromSeed(seed, index) :+ 1.toByte)
def point = Point(Crypto.publicKeyFromPrivateKey(data :+ 1.toByte))
def bigInteger: BigInteger = new BigInteger(1, data)
def add(scalar: Scalar): Scalar = Scalar(bigInteger.add(scalar.bigInteger))
def multiply(scalar: Scalar): Scalar = Scalar(bigInteger.multiply(scalar.bigInteger).mod(Crypto.curve.getN))
}
object Scalar {
def apply(value: BigInteger): Scalar = new Scalar(fixSize(value.toByteArray.dropWhile(_ == 0)))
}
case class Point(data: BinaryData) {
require(data.length == 33)
def ecPoint: ECPoint = Crypto.curve.getCurve.decodePoint(data)
def add(point: Point): Point = Point(ecPoint.add(point.ecPoint))
def multiply(scalar: Scalar): Point = Point(ecPoint.multiply(scalar.bigInteger))
}
object Point {
def apply(ecPoint: ECPoint): Point = new Point(ecPoint.getEncoded(true))
}
def perCommitSecret(seed: BinaryData, index: Int): Scalar = ShaChain.shaChainFromSeed(seed, index)
def perCommitPoint(seed: BinaryData, index: Int): Point = perCommitSecret(seed, index).point
def perCommitPoint(seed: BinaryData, index: Int): Point = perCommitSecret(seed, index).toPoint
def derivePrivKey(secret: Scalar, perCommitPoint: Point): Scalar = {
// secretkey = basepoint-secret + SHA256(per-commitment-point || basepoint)
secret.add(Scalar(Crypto.sha256(perCommitPoint.data ++ secret.point.data)))
secret.add(Scalar(Crypto.sha256(perCommitPoint.data ++ secret.toPoint.data)))
}
def derivePubKey(basePoint: Point, perCommitPoint: Point): Point = {
//pubkey = basepoint + SHA256(per-commitment-point || basepoint)*G
val a = Scalar(Crypto.sha256(perCommitPoint.data ++ basePoint.data))
Point(basePoint.ecPoint.add(Crypto.curve.getG.multiply(a.bigInteger)))
basePoint.add(a.toPoint)
}
def revocationPubKey(basePoint: Point, perCommitPoint: Point): Point = {
@ -67,8 +35,8 @@ object Generators {
}
def revocationPrivKey(secret: Scalar, perCommitSecret: Scalar): Scalar = {
val a = Scalar(Crypto.sha256(secret.point.data ++ perCommitSecret.point.data))
val b = Scalar(Crypto.sha256(perCommitSecret.point.data ++ secret.point.data))
val a = Scalar(Crypto.sha256(secret.toPoint.data ++ perCommitSecret.toPoint.data))
val b = Scalar(Crypto.sha256(perCommitSecret.toPoint.data ++ secret.toPoint.data))
secret.multiply(a).add(perCommitSecret.multiply(b))
}

View file

@ -1,5 +1,6 @@
package fr.acinq.eclair.crypto
import fr.acinq.bitcoin.Crypto.PrivateKey
import fr.acinq.bitcoin.{BinaryData, Crypto}
import fr.acinq.eclair.crypto.LightningCrypto._
@ -90,12 +91,4 @@ object Onion extends App {
//lazy val random = SecureRandom.getInstanceStrong
lazy val random = new Random()
def generatePrivateKey(): BinaryData = {
val key = new Array[Byte](32)
do {
random.nextBytes(key)
} while (Crypto.publicKeyFromPrivateKey(key :+ 0x01.toByte)(0) != 0x02)
key
}
}

View file

@ -101,7 +101,7 @@ class AuthHandler(them: ActorRef, blockchain: ActorRef, paymentHandler: ActorRef
* session_sig is the signature of the SHA256 of SHA256 of the receivers node_id, using the secret key corresponding to the sender's node_id.
*/
val sig: BinaryData = Crypto.encodeSignature(Crypto.sign(Crypto.hash256(their_session_key), Globals.Node.privateKey))
val our_auth = pkt(Auth(lightning.authenticate(Globals.Node.publicKey, bin2signature(sig))))
val our_auth = pkt(Auth(lightning.authenticate(Globals.Node.publicKey.toBin, bin2signature(sig))))
val encryptor = Encryptor(sending_key, 0)
val decryptor = Decryptor(receiving_key, 0)

View file

@ -5,7 +5,6 @@ import java.math.BigInteger
import com.google.protobuf.ByteString
import fr.acinq.bitcoin._
import fr.acinq.eclair.crypto.Generators.{Point, Scalar}
import lightning.{bitcoin_pubkey, signature}
import scala.annotation.tailrec
@ -18,14 +17,6 @@ package object eclair {
implicit def pubkey2bin(in: bitcoin_pubkey): BinaryData = in.key.toByteArray
implicit def point2bin(in: Point): BinaryData = in.data
implicit def bin2point(in: BinaryData): Point = Point(in)
implicit def scalar2bin(in: Scalar): BinaryData = in.data
implicit def bin2scalar(in: BinaryData): Scalar = Scalar(in)
private def fixSize(in: Array[Byte]): Array[Byte] = in.size match {
case 32 => in
case s if s < 32 => Array.fill(32 - s)(0: Byte) ++ in

View file

@ -1,10 +1,9 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin.BinaryData
import fr.acinq.eclair._
import fr.acinq.bitcoin.Crypto.Point
import fr.acinq.eclair.channel.{LocalParams, RemoteParams}
import fr.acinq.eclair.crypto.Generators
import fr.acinq.eclair.crypto.Generators.Point
import fr.acinq.eclair.crypto.LightningCrypto.sha256
import fr.acinq.eclair.transactions.Transactions.{CommitTx, HtlcSuccessTx, HtlcTimeoutTx, InputInfo}
import fr.acinq.eclair.wire.{UpdateAddHtlc, UpdateFailHtlc, UpdateFulfillHtlc, UpdateMessage}
@ -80,14 +79,14 @@ object CommitmentSpec {
}
def makeLocalTxs(localParams: LocalParams, remoteParams: RemoteParams, commitmentInput: InputInfo, localPerCommitmentPoint: Point, spec: CommitmentSpec): CommitTx = {
val localPubkey = Generators.derivePubKey(localParams.delayedPaymentKey.point, localPerCommitmentPoint)
val localPubkey = Generators.derivePubKey(localParams.delayedPaymentKey.toPoint, localPerCommitmentPoint)
val remotePubkey = Generators.derivePubKey(remoteParams.paymentBasepoint, localPerCommitmentPoint)
val localRevocationPubkey = Generators.revocationPubKey(localParams.revocationSecret.point, localPerCommitmentPoint)
val localRevocationPubkey = Generators.revocationPubKey(localParams.revocationSecret.toPoint, localPerCommitmentPoint)
Transactions.makeCommitTx(commitmentInput, localRevocationPubkey, localParams.toSelfDelay, localPubkey, remotePubkey, spec)
}
def makeRemoteTxs(localParams: LocalParams, remoteParams: RemoteParams, commitmentInput: InputInfo, remotePerCommitmentPoint: Point, spec: CommitmentSpec): (CommitTx, Seq[HtlcTimeoutTx], Seq[HtlcSuccessTx]) = {
val localPubkey = Generators.derivePubKey(localParams.paymentSecret.point, remotePerCommitmentPoint)
val localPubkey = Generators.derivePubKey(localParams.paymentSecret.toPoint, remotePerCommitmentPoint)
val remotePubkey = Generators.derivePubKey(remoteParams.delayedPaymentBasepoint, remotePerCommitmentPoint)
val remoteRevocationPubkey = Generators.revocationPubKey(remoteParams.revocationBasepoint, remotePerCommitmentPoint)
val commitTx = Transactions.makeCommitTx(commitmentInput, remoteRevocationPubkey, remoteParams.toSelfDelay, remotePubkey, localPubkey, spec)

View file

@ -1,7 +1,8 @@
package fr.acinq.eclair.transactions
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.bitcoin.Crypto.{PrivateKey, PublicKey, hash160, ripemd160, sha256}
import fr.acinq.bitcoin.Script._
import fr.acinq.bitcoin.{BinaryData, Crypto, LexicographicalOrdering, 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
/**
@ -23,16 +24,7 @@ object Scripts {
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))
def multiSig2of2(pubkey1: PublicKey, pubkey2: PublicKey): Seq[ScriptElt] = if (LexicographicalOrdering.isLessThan(pubkey1.toBin, pubkey2.toBin))
Script.createMultiSigMofN(2, Seq(pubkey1, pubkey2))
else
Script.createMultiSigMofN(2, Seq(pubkey2, pubkey1))
@ -45,26 +37,14 @@ object Scripts {
* @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)))
def witness2of2(sig1: BinaryData, sig2: BinaryData, pubkey1: PublicKey, pubkey2: PublicKey): ScriptWitness = {
if (LexicographicalOrdering.isLessThan(pubkey1.toBin, pubkey2.toBin))
ScriptWitness(Seq(BinaryData.empty, sig1, sig2, write(multiSig2of2(pubkey1, pubkey2))))
else
ScriptWitness(Seq(BinaryData.empty, sig2, sig1, multiSig2of2(pubkey1, pubkey2)))
ScriptWitness(Seq(BinaryData.empty, sig2, sig1, write(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
@ -76,12 +56,12 @@ object Scripts {
* @param key private key that can redeem the funding tx
* @return a signed funding tx
*/
def makeFundingTx(pubkey1: BinaryData, pubkey2: BinaryData, amount: Long, previousTx: Transaction, outputIndex: Int, key: BinaryData): (Transaction, Int) = {
def makeFundingTx(pubkey1: PublicKey, pubkey2: PublicKey, amount: Long, previousTx: Transaction, outputIndex: Int, key: PrivateKey): (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 pub: BinaryData = key.toPoint
val pkh = OP_0 :: OP_PUSHDATA(Crypto.hash160(pub)) :: Nil
val p2sh: BinaryData = Script.write(pay2sh(pkh))
@ -89,7 +69,7 @@ object Scripts {
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 sig = Crypto.encodeSignature(Crypto.sign(hash, key)) :+ 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)
@ -175,7 +155,7 @@ object Scripts {
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(
LexicographicalOrdering.sort(Transaction(
version = 2,
txIn = inputs,
txOut = Seq(

View file

@ -1,9 +1,9 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin.Crypto.ripemd160
import fr.acinq.bitcoin.Crypto.{Point, PrivateKey, Scalar, ripemd160}
import fr.acinq.bitcoin.Script._
import fr.acinq.bitcoin.SigVersion.SIGVERSION_WITNESS_V0
import fr.acinq.bitcoin.{BinaryData, OutPoint, SIGHASH_ALL, Satoshi, Script, ScriptElt, ScriptFlags, Transaction, TxIn, TxOut}
import fr.acinq.eclair.crypto.Generators.Point
import fr.acinq.bitcoin.{BinaryData, LexicographicalOrdering, OutPoint, SIGHASH_ALL, Satoshi, ScriptElt, ScriptFlags, Transaction, TxIn, TxOut}
import fr.acinq.eclair.transactions.Scripts._
import fr.acinq.eclair.wire.UpdateAddHtlc
@ -43,15 +43,15 @@ object Transactions {
txIn = TxIn(commitTxInput.outPoint, Array.emptyByteArray, 0xffffffffL) :: Nil,
txOut = toLocalDelayedOutput_opt.toSeq ++ toRemoteOutput_opt.toSeq ++ htlcOfferedOutputs.toSeq ++ htlcReceivedOutputs.toSeq,
lockTime = 0)
CommitTx(commitTxInput, permuteOutputs(tx))
CommitTx(commitTxInput, LexicographicalOrdering.sort(tx))
}
def makeHtlcTimeoutTx(commitTx: Transaction, localRevocationPubkey: BinaryData, toLocalDelay: Int, localPubkey: BinaryData, remotePubkey: BinaryData, htlc: UpdateAddHtlc): HtlcTimeoutTx = {
val redeemScript = htlcOffered(localPubkey, remotePubkey, ripemd160(htlc.paymentHash))
val pubkeyScript = Script.write(pay2wsh(redeemScript))
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript)
require(outputIndex >= 0, "output not found")
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), Script.write(redeemScript))
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
HtlcTimeoutTx(input, Transaction(
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, 0xffffffffL) :: Nil,
@ -61,10 +61,10 @@ object Transactions {
def makeHtlcSuccessTx(commitTx: Transaction, localRevocationPubkey: BinaryData, toLocalDelay: Int, localPubkey: BinaryData, remotePubkey: BinaryData, htlc: UpdateAddHtlc): HtlcSuccessTx = {
val redeemScript = htlcReceived(localPubkey, remotePubkey, ripemd160(htlc.paymentHash), htlc.expiry)
val pubkeyScript = Script.write(pay2wsh(redeemScript))
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript)
require(outputIndex >= 0, "output not found")
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), Script.write(redeemScript))
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
HtlcSuccessTx(input, Transaction(
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, 0xffffffffL) :: Nil,
@ -87,10 +87,10 @@ object Transactions {
def makeClaimHtlcSuccessTx(commitTx: Transaction, localPubkey: BinaryData, remotePubkey: BinaryData, finalLocalPubkey: BinaryData, htlc: UpdateAddHtlc): ClaimHtlcSuccessTx = {
val redeemScript = htlcOffered(remotePubkey, localPubkey, ripemd160(htlc.paymentHash))
val pubkeyScript = Script.write(pay2wsh(redeemScript))
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript)
require(outputIndex >= 0, "output not found")
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), Script.write(redeemScript))
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
ClaimHtlcSuccessTx(input, Transaction(
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, 0xffffffffL) :: Nil,
@ -100,10 +100,10 @@ object Transactions {
def makeClaimHtlcTimeoutTx(commitTx: Transaction, localPubkey: BinaryData, remotePubkey: BinaryData, finalLocalPubkey: BinaryData, htlc: UpdateAddHtlc): ClaimHtlcTimeoutTx = {
val redeemScript = htlcReceived(remotePubkey, localPubkey, ripemd160(htlc.paymentHash), htlc.expiry)
val pubkeyScript = Script.write(pay2wsh(redeemScript))
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript)
require(outputIndex >= 0, "output not found")
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), Script.write(redeemScript))
val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript))
ClaimHtlcTimeoutTx(input, Transaction(
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, 0x00000000L) :: Nil,
@ -113,10 +113,10 @@ object Transactions {
def makeClaimHtlcDelayed(htlcSuccessOrTimeoutTx: Transaction, localRevocationPubkey: BinaryData, toLocalDelay: Int, localPubkey: BinaryData, finalLocalPubkey: BinaryData, htlc: UpdateAddHtlc): ClaimHtlcDelayed = {
val redeemScript = htlcSuccessOrTimeout(localRevocationPubkey, toLocalDelay, localPubkey)
val pubkeyScript = Script.write(pay2wsh(redeemScript))
val pubkeyScript = write(pay2wsh(redeemScript))
val outputIndex = findPubKeyScriptIndex(htlcSuccessOrTimeoutTx, pubkeyScript)
require(outputIndex >= 0, "output not found")
val input = InputInfo(OutPoint(htlcSuccessOrTimeoutTx, outputIndex), htlcSuccessOrTimeoutTx.txOut(outputIndex), Script.write(redeemScript))
val input = InputInfo(OutPoint(htlcSuccessOrTimeoutTx, outputIndex), htlcSuccessOrTimeoutTx.txOut(outputIndex), write(redeemScript))
ClaimHtlcDelayed(input, Transaction(
version = 2,
txIn = TxIn(input.outPoint, Array.emptyByteArray, toLocalDelay) :: Nil,
@ -126,15 +126,13 @@ object Transactions {
def findPubKeyScriptIndex(tx: Transaction, pubkeyScript: BinaryData): Int = tx.txOut.indexWhere(_.publicKeyScript == pubkeyScript)
def findPubKeyScriptIndex(tx: Transaction, pubkeyScript: Seq[ScriptElt]): Int = findPubKeyScriptIndex(tx, Script.write(pubkeyScript))
def findPubKeyScriptIndex(tx: Transaction, pubkeyScript: Seq[ScriptElt]): Int = findPubKeyScriptIndex(tx, write(pubkeyScript))
def sign(tx: Transaction, inputIndex: Int, redeemScript: BinaryData, amount: Satoshi, key: BinaryData): BinaryData = {
// this is because by convention in bitcoin-core-speak 32B keys are 'uncompressed' and 33B keys (ending by 0x01) are 'compressed'
val compressedKey: Seq[Byte] = key.data :+ 1.toByte
Transaction.signInput(tx, inputIndex, redeemScript, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0, compressedKey)
def sign(tx: Transaction, inputIndex: Int, redeemScript: BinaryData, amount: Satoshi, key: PrivateKey): BinaryData = {
Transaction.signInput(tx, inputIndex, redeemScript, SIGHASH_ALL, amount, SIGVERSION_WITNESS_V0, key)
}
def sign(txinfo: TransactionWithInputInfo, key: BinaryData): BinaryData = {
def sign(txinfo: TransactionWithInputInfo, key: PrivateKey): BinaryData = {
require(txinfo.tx.txIn.size == 1, "only one input allowed")
sign(txinfo.tx, inputIndex = 0, txinfo.input.redeemScript, txinfo.input.txOut.amount, key)
}

View file

@ -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, Scripts.pay2wsh(Scripts.multiSig2of2(ourCommitPub, theirCommitPub))) :: Nil,
txOut = TxOut(amount, Script.pay2wsh(Scripts.multiSig2of2(ourCommitPub, theirCommitPub))) :: Nil,
lockTime = 0
)
Future.successful((anchorTx, 0))

View file

@ -1,8 +1,8 @@
package fr.acinq.eclair
import fr.acinq.bitcoin.Crypto
import fr.acinq.bitcoin.Crypto.Scalar
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.Generators.Scalar
/**
* Created by PM on 26/04/2016.
@ -19,11 +19,11 @@ object TestConstants {
feeratePerKw = 10000,
toSelfDelay = 144,
maxAcceptedHtlcs = 100,
fundingPrivkey = Scalar(Array.fill[Byte](32)(1)),
revocationSecret = Scalar(Array.fill[Byte](32)(2)),
paymentSecret = Scalar(Array.fill[Byte](32)(3)),
delayedPaymentKey = Scalar(Array.fill[Byte](32)(4)),
finalPrivKey = Scalar(Array.fill[Byte](32)(5)),
fundingPrivkey = Scalar(Array.fill[Byte](32)(1) :+ 1.toByte),
revocationSecret = Scalar(Array.fill[Byte](32)(2) :+ 1.toByte),
paymentSecret = Scalar(Array.fill[Byte](32)(3) :+ 1.toByte),
delayedPaymentKey = Scalar(Array.fill[Byte](32)(4) :+ 1.toByte),
finalPrivKey = Scalar(Array.fill[Byte](32)(5) :+ 1.toByte),
shaSeed = Crypto.sha256("alice-seed".getBytes())
)
}
@ -37,11 +37,11 @@ object TestConstants {
feeratePerKw = 10000,
toSelfDelay = 144,
maxAcceptedHtlcs = 100,
fundingPrivkey = Scalar(Array.fill[Byte](32)(11)),
revocationSecret = Scalar(Array.fill[Byte](32)(12)),
paymentSecret = Scalar(Array.fill[Byte](32)(13)),
delayedPaymentKey = Scalar(Array.fill[Byte](32)(14)),
finalPrivKey = Scalar(Array.fill[Byte](32)(15)),
fundingPrivkey = Scalar(Array.fill[Byte](32)(11) :+ 1.toByte),
revocationSecret = Scalar(Array.fill[Byte](32)(12) :+ 1.toByte),
paymentSecret = Scalar(Array.fill[Byte](32)(13) :+ 1.toByte),
delayedPaymentKey = Scalar(Array.fill[Byte](32)(14) :+ 1.toByte),
finalPrivKey = Scalar(Array.fill[Byte](32)(15) :+ 1.toByte),
shaSeed = Crypto.sha256("alice-seed".getBytes())
)
}

View file

@ -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(Scripts.pay2wpkh(Alice.channelParams.finalPrivKey.point)))))
assert(punishTx.txOut == Seq(TxOut(Satoshi(301670), Script.write(Script.pay2wpkh(Alice.channelParams.finalPrivKey.toPoint)))))
}
}

View file

@ -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(Scripts.pay2wpkh(Alice.channelParams.finalPrivKey.point)))))
assert(punishTx.txOut == Seq(TxOut(Satoshi(500000), Script.write(Script.pay2wpkh(Alice.channelParams.finalPrivKey.toPoint)))))
}
}

View file

@ -43,21 +43,21 @@ class ClaimReceivedHtlcSpec extends FunSuite {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(Hash.Zeroes, 0), Array.emptyByteArray, 0xffffffffL) :: Nil,
txOut = TxOut(10 satoshi, pay2wsh(htlcScript)) :: Nil,
txOut = TxOut(10 satoshi, Script.pay2wsh(htlcScript)) :: Nil,
lockTime = 0)
// this tx tries to spend the previous tx
val tx1 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, 0xffffffff) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = 0)
test("Alice can spend this HTLC after a delay if she knows the payment hash") {
val tx2 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, reltimeout + 1) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = abstimeout + 1)
val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, tx.txOut(0).amount, 1, Alice.finalKey)
@ -71,7 +71,7 @@ class ClaimReceivedHtlcSpec extends FunSuite {
val tx2 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, reltimeout + 1) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Bob.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Bob.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = abstimeout + 1)
val sig = Transaction.signInput(tx2, 0, Script.write(htlcScript), SIGHASH_ALL, tx.txOut(0).amount, 1, Bob.finalKey)

View file

@ -44,21 +44,21 @@ class ClaimSentHtlcSpec extends FunSuite {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(Hash.Zeroes, 0), Array.emptyByteArray, 0xffffffffL) :: Nil,
txOut = TxOut(10 satoshi, pay2wsh(htlcScript)) :: Nil,
txOut = TxOut(10 satoshi, Script.pay2wsh(htlcScript)) :: Nil,
lockTime = 0)
// this tx tries to spend the previous tx
val tx1 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, 0xffffffff) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = 0)
test("Alice can spend this HTLC after a delay") {
val tx2 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = reltimeout + 1) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = abstimeout + 1)
val sig = Transaction.signInput(tx2, 0, redeemScript, SIGHASH_ALL, tx.txOut(0).amount, 1, Alice.finalKey)
@ -72,7 +72,7 @@ class ClaimSentHtlcSpec extends FunSuite {
val tx2 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = reltimeout + 1) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = abstimeout - 1)
val sig = Transaction.signInput(tx2, 0, redeemScript, SIGHASH_ALL, tx.txOut(0).amount, 1, Alice.finalKey)
@ -89,7 +89,7 @@ class ClaimSentHtlcSpec extends FunSuite {
val tx2 = Transaction(
version = 2,
txIn = TxIn(OutPoint(tx, 0), Array.emptyByteArray, sequence = reltimeout - 1) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
txOut = TxOut(10 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(Alice.finalPubKey.toBin)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil) :: Nil,
lockTime = abstimeout + 1)
val sig = Transaction.signInput(tx2, 0, redeemScript, SIGHASH_ALL, tx.txOut(0).amount, 1, Alice.finalKey)

View file

@ -1,33 +0,0 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin._
import fr.acinq.eclair.transactions.Scripts._
import org.junit.runner.RunWith
import org.scalatest.FlatSpec
import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class PermuteOutputSpec extends FlatSpec {
"permuteOutputs" should "permute tx output in a determinstic way" in {
val pub1: BinaryData = "0394D30868076AB1EA7736ED3BDBEC99497A6AD30B25AFD709CDF3804CD389996A"
val pub2: BinaryData = "032C58BC9615A6FF24E9132CEF33F1EF373D97DC6DA7933755BC8BB86DBEE9F55C"
val pub3: BinaryData = "02C4D72D99CA5AD12C17C9CFE043DC4E777075E8835AF96F46D8E3CCD929FE1926"
val outputs = Seq(
TxOut(5 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub1)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil),
TxOut(7 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub2)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil),
TxOut(11 satoshi, OP_DUP :: OP_HASH160 :: OP_PUSHDATA(Crypto.hash160(pub3)) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil)
)
val tx = Transaction(version = 1, txIn = Seq.empty[TxIn], txOut = Seq.empty[TxOut], lockTime = 0)
val txs = Seq(
tx.copy(txOut = Seq(outputs(0), outputs(1), outputs(2))),
tx.copy(txOut = Seq(outputs(0), outputs(2), outputs(1))),
tx.copy(txOut = Seq(outputs(1), outputs(0), outputs(2))),
tx.copy(txOut = Seq(outputs(1), outputs(2), outputs(0))),
tx.copy(txOut = Seq(outputs(2), outputs(0), outputs(1))),
tx.copy(txOut = Seq(outputs(2), outputs(1), outputs(0)))
)
assert(txs.map(permuteOutputs).map(_.hash).toSet.size === 1)
}
}

View file

@ -1,9 +1,8 @@
package fr.acinq.eclair.transactions
import fr.acinq.bitcoin.Crypto.sha256
import fr.acinq.bitcoin.Crypto.{Scalar, sha256}
import fr.acinq.bitcoin.{BinaryData, Btc, MilliBtc, millibtc2satoshi}
import fr.acinq.eclair.channel.Helpers.Funding
import fr.acinq.eclair.crypto.Generators.Scalar
import fr.acinq.eclair.transactions.Transactions._
import fr.acinq.eclair.wire.UpdateAddHtlc
import org.junit.runner.RunWith
@ -16,12 +15,12 @@ import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class TransactionsSpec extends FunSuite {
val fundingLocalPriv = Scalar(BinaryData("aa" * 32))
val fundingRemotePriv = Scalar(BinaryData("bb" * 32))
val commitInput = Funding.makeFundingInputInfo(BinaryData("12" * 32), 0, Btc(1), fundingLocalPriv.point, fundingRemotePriv.point)
val localRevocationPriv = Scalar(BinaryData("cc" * 32))
val localPaymentPriv = Scalar(BinaryData("dd" * 32))
val remotePaymentPriv = Scalar(BinaryData("ee" * 32))
val fundingLocalPriv = Scalar(BinaryData("aa" * 32) :+ 1.toByte)
val fundingRemotePriv = Scalar(BinaryData("bb" * 32) :+ 1.toByte)
val commitInput = Funding.makeFundingInputInfo(BinaryData("12" * 32), 0, Btc(1), fundingLocalPriv.toPoint, fundingRemotePriv.toPoint)
val localRevocationPriv = Scalar(BinaryData("cc" * 32) :+ 1.toByte)
val localPaymentPriv = Scalar(BinaryData("dd" * 32) :+ 1.toByte)
val remotePaymentPriv = Scalar(BinaryData("ee" * 32) :+ 1.toByte)
val toLocalDelay = 144
test("generate valid commitment and htlc transactions") {
@ -38,9 +37,9 @@ class TransactionsSpec extends FunSuite {
to_local_msat = millibtc2satoshi(MilliBtc(400)).amount * 1000,
to_remote_msat = millibtc2satoshi(MilliBtc(300)).amount * 1000)
val commitTx = makeCommitTx(commitInput, localRevocationPriv.point, toLocalDelay, localPaymentPriv.point, remotePaymentPriv.point, spec)
val commitTx = makeCommitTx(commitInput, localRevocationPriv.toPoint, toLocalDelay, localPaymentPriv.toPoint, remotePaymentPriv.toPoint, spec)
val (htlcTimeoutTxs, htlcSuccessTxs) = makeHtlcTxs(commitTx.tx, localRevocationPriv.point, toLocalDelay, localPaymentPriv.point, remotePaymentPriv.point, spec)
val (htlcTimeoutTxs, htlcSuccessTxs) = makeHtlcTxs(commitTx.tx, localRevocationPriv.toPoint, toLocalDelay, localPaymentPriv.toPoint, remotePaymentPriv.toPoint, spec)
assert(htlcTimeoutTxs.size == 1)
assert(htlcSuccessTxs.size == 1)
@ -58,7 +57,7 @@ class TransactionsSpec extends FunSuite {
// local spends delayed output of htlc timeout tx
val htlcTimeoutTx = htlcTimeoutTxs(0)
val localFinalPriv = Scalar(BinaryData("ff" * 32))
val claimHtlcDelayed = makeClaimHtlcDelayed(htlcTimeoutTx.tx, localRevocationPriv.point, toLocalDelay, localPaymentPriv.point, localFinalPriv.point, htlc1)
val claimHtlcDelayed = makeClaimHtlcDelayed(htlcTimeoutTx.tx, localRevocationPriv.toPoint, toLocalDelay, localPaymentPriv.toPoint, localFinalPriv.toPoint, htlc1)
val localSig = sign(claimHtlcDelayed, localPaymentPriv)
val signedTx = addSigs(claimHtlcDelayed, localSig)
assert(checkSig(signedTx).isSuccess)
@ -66,8 +65,8 @@ class TransactionsSpec extends FunSuite {
{
// remote spends local->remote htlc output directly in case of success
val remoteFinalPriv = Scalar(BinaryData("ff" * 32))
val claimHtlcSuccessTx = makeClaimHtlcSuccessTx(commitTx.tx, remotePaymentPriv.point, localPaymentPriv.point, remoteFinalPriv.point, htlc1)
val remoteFinalPriv = Scalar(BinaryData("ff" * 32) :+ 1.toByte)
val claimHtlcSuccessTx = makeClaimHtlcSuccessTx(commitTx.tx, remotePaymentPriv.toPoint, localPaymentPriv.toPoint, remoteFinalPriv.toPoint, htlc1)
val localSig = sign(claimHtlcSuccessTx, remotePaymentPriv)
val signed = addSigs(claimHtlcSuccessTx, localSig, paymentPreimage1)
assert(checkSig(signed).isSuccess)
@ -85,8 +84,8 @@ class TransactionsSpec extends FunSuite {
{
// local spends delayed output of htlc success tx
val htlcSuccessTx = htlcSuccessTxs(0)
val localFinalPriv = Scalar(BinaryData("ff" * 32))
val claimHtlcDelayed = makeClaimHtlcDelayed(htlcSuccessTx.tx, localRevocationPriv.point, toLocalDelay, localPaymentPriv.point, localFinalPriv.point, htlc1)
val localFinalPriv = Scalar(BinaryData("ff" * 32) :+ 1.toByte)
val claimHtlcDelayed = makeClaimHtlcDelayed(htlcSuccessTx.tx, localRevocationPriv.toPoint, toLocalDelay, localPaymentPriv.toPoint, localFinalPriv.toPoint, htlc1)
val localSig = sign(claimHtlcDelayed, localPaymentPriv)
val signedTx = addSigs(claimHtlcDelayed, localSig)
assert(checkSig(signedTx).isSuccess)
@ -94,8 +93,8 @@ class TransactionsSpec extends FunSuite {
{
// remote spends remote->local htlc output directly in case of timeout
val remoteFinalPriv = Scalar(BinaryData("ff" * 32))
val claimHtlcTimeoutTx = makeClaimHtlcTimeoutTx(commitTx.tx, remotePaymentPriv.point, localPaymentPriv.point, remoteFinalPriv.point, htlc2)
val remoteFinalPriv = Scalar(BinaryData("ff" * 32) :+ 1.toByte)
val claimHtlcTimeoutTx = makeClaimHtlcTimeoutTx(commitTx.tx, remotePaymentPriv.toPoint, localPaymentPriv.toPoint, remoteFinalPriv.toPoint, htlc2)
val localSig = sign(claimHtlcTimeoutTx, remotePaymentPriv)
val signed = addSigs(claimHtlcTimeoutTx, localSig)
assert(checkSig(signed).isSuccess)

View file

@ -1,366 +0,0 @@
package fr.acinq.protos
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import fr.acinq.bitcoin._
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.Scripts
import org.junit.runner.RunWith
import org.scalatest.FunSuite
import org.scalatest.junit.JUnitRunner
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.util.Try
@RunWith(classOf[JUnitRunner])
class Bolt3Spec extends FunSuite {
val (Base58.Prefix.SecretKeyTestnet, localPrivKey) = Base58Check.decode("cVuzKWCszfvjkoJyUasvsrRdECriz8hSd1BDinRNzytwnXmX7m1g")
val (Base58.Prefix.SecretKeyTestnet, remotePrivKey) = Base58Check.decode("cRUfvpbRtMSqCFD1ADdvgPn5HfRLYuHCFYAr2noWnaRDNger2AoA")
val localPubKey: BinaryData = Crypto.publicKeyFromPrivateKey(localPrivKey)
val remotePubKey: BinaryData = Crypto.publicKeyFromPrivateKey(remotePrivKey)
val (Base58.Prefix.SecretKeyTestnet, revocationPrivKey) = Base58Check.decode("cSupnaiBh6jgTcQf9QANCB5fZtXojxkJQczq5kwfSBeULjNd5Ypo")
val revocationPubKey = Crypto.publicKeyFromPrivateKey(revocationPrivKey)
val amount = 40000 + 30000 + 20000 + 15000 satoshi
val config = ConfigFactory.load()
// run this test with -Dbolt3-test.use-bitcoind=true to generate publishable tx
val useBitcoind = Try(config.getBoolean("bolt3-test.use-bitcoind")).getOrElse(false)
val bitcoin: Option[ExtendedBitcoinClient] = if (useBitcoind) {
implicit val system = ActorSystem("mySystem")
val client = new ExtendedBitcoinClient(new BitcoinJsonRPCClient(
user = config.getString("eclair.bitcoind.rpcuser"),
password = config.getString("eclair.bitcoind.rpcpassword"),
host = config.getString("eclair.bitcoind.host"),
port = config.getInt("eclair.bitcoind.rpcport")))
Some(client)
} else None
val (fundingTx, fundingPos) = bitcoin match {
case Some(client) => Await.result(client.makeAnchorTx(localPubKey, remotePubKey, amount), 5 seconds)
case None => (Transaction(version = 2, txIn = Nil, txOut = TxOut(amount, Script.pay2wsh(Bolt3.fundingScript(localPubKey, remotePubKey))) :: Nil, lockTime = 0), 0)
}
def hex(tx: Transaction) = toHexString(Transaction.write(tx))
println(s"funding tx (use output $fundingPos): ${hex(fundingTx)}")
val localDelayedKey = localPubKey
val paymentPreimage1 = Hash.Zeroes
val paymentPreimage2 = Hash.One
val paymentHash1 = Crypto.hash160(paymentPreimage1)
val paymentHash2 = Crypto.hash160(paymentPreimage2)
// this is an absolute timeout (i.e. a block height or UNIX timestamp) that will be used with OP_CLTV
val htlcTimeout = 10000
// this is a relative (to the parent tx) timeout, expressed in number of blocks or seconds
val selfDelay = 20
val fee = 5000 satoshi
// create our local commit tx, with an HTLC that we've offered and a HTLC that we've received
val commitTx = {
val tx = LexicographicalOrdering.sort(
Transaction(
version = 2,
txIn = TxIn(OutPoint(fundingTx, fundingPos), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = Seq(
TxOut(40000.satoshi - fee / 4, Script.pay2wsh(Bolt3.toLocal(revocationPubKey, selfDelay, localDelayedKey))),
TxOut(30000.satoshi - fee / 4, Script.pay2wpkh(remotePubKey)),
TxOut(20000.satoshi - fee / 4, Script.pay2wsh(Bolt3.htlcOffered(localPubKey, remotePubKey, paymentHash1))),
TxOut(15000.satoshi - fee / 4, Script.pay2wsh(Bolt3.htlcReceived(localPubKey, remotePubKey, paymentHash2, htlcTimeout)))
),
lockTime = 0)
)
val redeemScript: BinaryData = Bolt3.fundingScript(localPubKey, remotePubKey)
println(s"size of funding tx script: ${redeemScript.length}")
val localSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, fundingTx.txOut(fundingPos).amount, SigVersion.SIGVERSION_WITNESS_V0, localPrivKey)
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 (Scripts.isLess(localPubKey, remotePubKey))
ScriptWitness(BinaryData.empty :: localSig :: remoteSig :: redeemScript :: Nil)
else
ScriptWitness(BinaryData.empty :: remoteSig :: localSig :: redeemScript :: Nil)
println(s"witness size: ${ScriptWitness.write(witness).length}")
val tx1 = tx.updateWitness(0, witness)
println(s"signed commit tx base size: ${Bolt3.baseSize(tx1)} total size: ${Bolt3.totalSize(tx1)} weight: ${Bolt3.weight(tx1)}")
tx1
}
println(s"commit tx: ${hex(commitTx)}")
def findPubKeyScriptIndex(tx: Transaction, script: BinaryData): Int = tx.txOut.indexWhere(_.publicKeyScript == script)
def findPubKeyScriptIndex(tx: Transaction, script: Seq[ScriptElt]): Int = findPubKeyScriptIndex(tx, Script.write(script))
// create our local HTLC timeout tx for the HTLC that we've offered
// it is signed by both parties
val htlcTimeoutTx = {
val redeemScript: BinaryData = Script.write(Bolt3.htlcOffered(localPubKey, remotePubKey, paymentHash1))
val index = findPubKeyScriptIndex(commitTx, Script.pay2wsh(redeemScript))
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wsh(Bolt3.htlcSuccessOrTimeout(revocationPubKey, selfDelay, localDelayedKey))) :: Nil,
lockTime = 0)
// both parties sign the unsigned tx
println(s"size of htlcOffered script: ${redeemScript.length}")
val localSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, localPrivKey)
println(s"local sig size: ${localSig.length}")
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, remotePrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(BinaryData.empty :: remoteSig :: localSig :: BinaryData.empty :: redeemScript :: Nil)
println(s"witness size: ${ScriptWitness.write(witness).length}")
val tx1 = tx.updateWitness(0, witness)
println(s"signed htlcTimeoutTx tx: base size: ${Bolt3.baseSize(tx1)} total size: ${Bolt3.totalSize(tx1)} weight: ${Bolt3.weight(tx1)}")
tx1
}
println(s"htlc timeout tx: ${hex(htlcTimeoutTx)}")
// create our local HTLC success tx for the HTLC that we've received
// it is signed by both parties and its witness contains the HTLC payment preimage
val htlcSuccessTx = {
val redeemScript: BinaryData = Script.write(Bolt3.htlcReceived(localPubKey, remotePubKey, Crypto.hash160(paymentPreimage2), htlcTimeout))
val index = findPubKeyScriptIndex(commitTx, Script.pay2wsh(redeemScript))
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wsh(Bolt3.htlcSuccessOrTimeout(revocationPubKey, selfDelay, localDelayedKey))) :: Nil,
lockTime = 0)
// both parties sign the unsigned tx
println(s"size of htlcReceived script: ${redeemScript.length}")
val localSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, localPrivKey)
println(s"local sig size: ${localSig.length}")
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, remotePrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(BinaryData.empty :: remoteSig :: localSig :: paymentPreimage2 :: redeemScript :: Nil)
println(s"witness size: ${ScriptWitness.write(witness).length}")
val tx1 = tx.updateWitness(0, witness)
println(s"signed htlcSuccessTx base size: ${Bolt3.baseSize(tx1)} total size: ${Bolt3.totalSize(tx1)} weight: ${Bolt3.weight(tx1)}")
tx1
}
println(s"htlc success tx: ${hex(htlcSuccessTx)}")
test("commit tx spends the funding tx") {
Transaction.correctlySpends(commitTx, fundingTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
}
test("HTLC timeout tx spends the commit tx") {
Transaction.correctlySpends(htlcTimeoutTx, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
}
test("HTLC success tx spends the commit tx") {
Transaction.correctlySpends(htlcSuccessTx, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
}
test("we can spend our commit tx output after a delay") {
val spendOurOutput = {
val redeemScript: BinaryData = Script.write(Bolt3.toLocal(revocationPubKey, selfDelay, localDelayedKey))
val index = findPubKeyScriptIndex(commitTx, Script.pay2wsh(redeemScript))
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = selfDelay + 1) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wpkh(localPubKey)) :: Nil,
lockTime = 0)
println(s"size of toLocal script: ${redeemScript.length}")
val localSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, localPrivKey)
println(s"local sig size: ${localSig.length}")
val witness = ScriptWitness(localSig :: BinaryData.empty :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(spendOurOutput, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"we-spend-our-output tx: ${hex(spendOurOutput)}")
println(s"you need to publish the commit tx and generate ${selfDelay} blocks before you can publish this tx")
}
test("they can spend their commit tx immediately") {
val index = findPubKeyScriptIndex(commitTx, Script.pay2wpkh(remotePubKey))
val spendTheirOutput = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wpkh(remotePubKey)) :: Nil,
lockTime = 0)
val redeemScript: BinaryData = Script.write(Script.pay2pkh(remotePubKey))
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, remotePrivKey)
val witness = ScriptWitness(remoteSig :: remotePubKey :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(spendTheirOutput, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"they-spend-their-output tx: ${hex(spendTheirOutput)}")
}
test("they can spend our commit tx output immediately if they have the revocation key") {
val redeemScript: BinaryData = Script.write(Bolt3.toLocal(revocationPubKey, selfDelay, localDelayedKey))
val index = findPubKeyScriptIndex(commitTx, Script.pay2wsh(redeemScript))
val penaltyTx = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wpkh(localPubKey)) :: Nil,
lockTime = 0)
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, revocationPrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(remoteSig :: BinaryData("01") :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(penaltyTx, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"they-spend-our-output tx: ${hex(penaltyTx)}")
}
test("we can claim the received HTLC timeout tx after a delay") {
val spendHtlcTimeout = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(htlcTimeoutTx, 0), signatureScript = Nil, sequence = selfDelay + 1) :: Nil,
txOut = TxOut(htlcTimeoutTx.txOut(0).amount - fee, Script.pay2wpkh(localPubKey)) :: Nil,
lockTime = 0)
val redeemScript: BinaryData = Script.write(Bolt3.htlcSuccessOrTimeout(revocationPubKey, selfDelay, localDelayedKey))
println(s"size of htlcSuccessOrTimeout script: ${redeemScript.length}")
val localSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, htlcTimeoutTx.txOut(0).amount, SigVersion.SIGVERSION_WITNESS_V0, localPrivKey)
println(s"local sig size: ${localSig.length}")
val witness = ScriptWitness(localSig :: BinaryData.empty :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(spendHtlcTimeout, htlcTimeoutTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"we-spend-offered-htlc-timeout tx: ${hex(spendHtlcTimeout)}")
println(s"you need to publish the htlc timeout tx and generate ${selfDelay} blocks before you can publish this tx")
}
test("we can claim the received HTLC success tx after a delay") {
val spendHtlcSuccess = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(htlcSuccessTx, 0), signatureScript = Nil, sequence = selfDelay + 1) :: Nil,
txOut = TxOut(htlcSuccessTx.txOut(0).amount - fee, Script.pay2wpkh(localPubKey)) :: Nil,
lockTime = 0)
val redeemScript: BinaryData = Script.write(Bolt3.htlcSuccessOrTimeout(revocationPubKey, selfDelay, localDelayedKey))
val localSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, htlcSuccessTx.txOut(0).amount, SigVersion.SIGVERSION_WITNESS_V0, localPrivKey)
println(s"local sig size: ${localSig.length}")
val witness = ScriptWitness(localSig :: BinaryData.empty :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(spendHtlcSuccess, htlcSuccessTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"we-spend-received-htlc tx: ${hex(spendHtlcSuccess)}")
println(s"you need to publish the htlc success tx and generate ${selfDelay} blocks before you can publish this tx")
}
test("they can spend the offered HTLC with the payment preimage") {
val spendOfferedHtlc = {
val redeemScript: BinaryData = Script.write(Bolt3.htlcOffered(localPubKey, remotePubKey, Crypto.hash160(paymentPreimage1)))
val index = findPubKeyScriptIndex(commitTx, Script.pay2wsh(redeemScript))
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = selfDelay + 1) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wpkh(remotePubKey)) :: Nil,
lockTime = 0)
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, remotePrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(remoteSig :: paymentPreimage1 :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(spendOfferedHtlc, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"they-spend-offered-htlc tx: ${hex(spendOfferedHtlc)}")
}
test("they can timeout the received HTLC after a delay") {
val redeemScript: BinaryData = Script.write(Bolt3.htlcReceived(localPubKey, remotePubKey, Crypto.hash160(paymentPreimage2), htlcTimeout))
val index = findPubKeyScriptIndex(commitTx, Script.pay2wsh(redeemScript))
val timeoutReceivedHtlc = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(commitTx, index), signatureScript = Nil, sequence = 0) :: Nil,
txOut = TxOut(commitTx.txOut(index).amount - fee, Script.pay2wpkh(remotePubKey)) :: Nil,
lockTime = htlcTimeout + 1)
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, commitTx.txOut(index).amount, SigVersion.SIGVERSION_WITNESS_V0, remotePrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(remoteSig :: BinaryData.empty :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(timeoutReceivedHtlc, commitTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"they-timeout-received-htlc tx: ${hex(timeoutReceivedHtlc)}")
}
test("they can spend our HTLC timeout tx immediately if they know the revocation private key") {
val penaltyTx = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(htlcTimeoutTx, 0), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(htlcTimeoutTx.txOut(0).amount - fee, Script.pay2wpkh(remotePubKey)) :: Nil,
lockTime = 0)
val redeemScript: BinaryData = Script.write(Bolt3.htlcSuccessOrTimeout(revocationPubKey, selfDelay, localDelayedKey))
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, htlcTimeoutTx.txOut(0).amount, SigVersion.SIGVERSION_WITNESS_V0, revocationPrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(remoteSig :: BinaryData("01") :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(penaltyTx, htlcTimeoutTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"penalty for out htlc timeout tx: ${hex(penaltyTx)}")
}
test("they can spend our HTLC success tx immediately if they know the revocation private key") {
val penaltyTx = {
val tx = Transaction(
version = 2,
txIn = TxIn(OutPoint(htlcSuccessTx, 0), signatureScript = Nil, sequence = TxIn.SEQUENCE_FINAL) :: Nil,
txOut = TxOut(htlcSuccessTx.txOut(0).amount - fee, Script.pay2wpkh(remotePubKey)) :: Nil,
lockTime = 0)
val redeemScript: BinaryData = Script.write(Bolt3.htlcSuccessOrTimeout(revocationPubKey, selfDelay, localDelayedKey))
val remoteSig: BinaryData = Transaction.signInput(tx, 0, redeemScript, SIGHASH_ALL, htlcSuccessTx.txOut(0).amount, SigVersion.SIGVERSION_WITNESS_V0, revocationPrivKey)
println(s"remote sig size: ${remoteSig.length}")
val witness = ScriptWitness(remoteSig :: BinaryData("01") :: redeemScript :: Nil)
tx.updateWitness(0, witness)
}
Transaction.correctlySpends(penaltyTx, htlcSuccessTx :: Nil, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
println(s"penalty for out htlc success tx: ${hex(penaltyTx)}")
}
test("derive revocation key") {
object Local {
val revocationSecret = Scalar(Crypto.sha256("local foo".getBytes()))
val revocationBasePoint = revocationSecret.point
val perCommitSecret = Scalar(Crypto.sha256("local bar".getBytes()))
}
object Remote {
val revocationSecret = Scalar(Crypto.sha256("remote foo".getBytes()))
val perCommitSecret = Scalar(Crypto.sha256("remote bar".getBytes()))
val perCommitBasePoint = perCommitSecret.point
}
// I can compute their revocation pubkey
val theirRevocationPubKey = Generators.revocationPubKey(Local.revocationBasePoint, Remote.perCommitBasePoint)
// and if they give me their per-commit secret I can compute their revocation privkey
val theirRevocationPrivKey = Generators.revocationPrivKey(Local.revocationSecret, Remote.perCommitSecret)
assert(theirRevocationPrivKey.point == theirRevocationPubKey)
}
test("derive local/remote/delayed keys") {
object Local {
val secret = Scalar(Crypto.sha256("local foo".getBytes()))
val basePoint = secret.point
val perCommitSecret = Scalar(Crypto.sha256("local bar".getBytes()))
val perCommitBasePoint = perCommitSecret.point
}
object Remote {
val secret = Scalar(Crypto.sha256("remote foo".getBytes()))
val perCommitSecret = Scalar(Crypto.sha256("remote bar".getBytes()))
val perCommitBasePoint = perCommitSecret.point
}
val localKey = Generators.derivePrivKey(Local.secret, Local.perCommitBasePoint)
val localPubKey = Generators.derivePubKey(Local.basePoint, Local.perCommitBasePoint)
assert(localKey.point == localPubKey)
}
}

View file

@ -49,7 +49,7 @@
<bitcoinlib.version>0.9.6</bitcoinlib.version>
<acinqtools.version>1.2</acinqtools.version>
<akka.version>2.4.12</akka.version>
<bitcoinlib.version>0.9.7</bitcoinlib.version>
<bitcoinlib.version>0.9.8</bitcoinlib.version>
<scalapb.version>0.4.21</scalapb.version>
</properties>