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:
parent
4fb26c6c89
commit
e1ae6d2a2e
24 changed files with 110 additions and 579 deletions
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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())
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)))))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)))))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
2
pom.xml
2
pom.xml
|
@ -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>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue