1
0
mirror of https://github.com/ACINQ/eclair.git synced 2024-11-19 18:10:42 +01:00

fix initial signature exchange

This commit is contained in:
sstone 2016-05-23 18:54:54 +02:00
parent 7903e2b53f
commit b886388b88
3 changed files with 63 additions and 20 deletions

View File

@ -63,9 +63,10 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
case Event((anchorTx: Transaction, anchorOutputIndex: Int), DATA_OPEN_WITH_ANCHOR_WAIT_FOR_ANCHOR(ourParams, theirParams, theirRevocationHash, theirNextRevocationHash)) =>
log.info(s"anchor txid=${anchorTx.txid}")
val amount = anchorTx.txOut(anchorOutputIndex).amount.toLong
// FIXME. fees and amount are wrong
val spec = CommitmentSpec(Set.empty[Htlc], theirParams.initialFeeRate, amount * 1000, 0)
val theirTx = makeTheirTx(ourParams, theirParams, TxIn(OutPoint(anchorTx.hash, anchorOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, theirRevocationHash, spec)
val ourSig = sign(ourParams, theirParams, theirTx)
val ourSig = sign(ourParams, theirParams, amount, theirTx)
them ! open_anchor(anchorTx.hash, anchorOutputIndex, amount, ourSig)
goto(OPEN_WAIT_FOR_COMMIT_SIG) using DATA_OPEN_WAIT_FOR_COMMIT_SIG(ourParams, theirParams, anchorTx, anchorOutputIndex, TheirCommit(0, spec, theirRevocationHash), theirNextRevocationHash)
@ -77,22 +78,37 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
val anchorTxid = anchorTxHash.reverse //see https://github.com/ElementsProject/lightning/issues/17
val anchorOutput = TxOut(Satoshi(anchorAmount), publicKeyScript = Scripts.anchorPubkeyScript(ourParams.commitPubKey, theirParams.commitPubKey))
// they fund the channel with their anchor tx, so the money is theirs
val spec = CommitmentSpec(Set.empty[Htlc], theirParams.initialFeeRate, 0, anchorAmount * 1000)
// they fund the channel with their anchor tx, so the money is theirs
val ourSpec = {
val fee = ChannelState.computeFee(ourParams.initialFeeRate, 0)
val pay_msat = (anchorAmount - fee) * 1000
val fee_msat = fee * 1000
CommitmentSpec(Set.empty[Htlc], fee_msat, amount_us = 0, amount_them = pay_msat)
}
val theirSpec = {
val fee = ChannelState.computeFee(theirParams.initialFeeRate, 0)
val pay_msat = (anchorAmount - fee) * 1000
val fee_msat = fee * 1000
CommitmentSpec(Set.empty[Htlc], fee_msat, amount_us = pay_msat, amount_them = 0)
}
// we build our commitment tx, sign it and check that it is spendable using the counterparty's sig
val ourRevocationHash = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 0))
val ourTx = makeOurTx(ourParams, theirParams, TxIn(OutPoint(anchorTxHash, anchorOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, ourRevocationHash, spec)
val ourSig = sign(ourParams, theirParams, ourTx)
val signedTx = addSigs(ourParams, theirParams, ourTx, ourSig, theirSig)
val ourTx = makeOurTx(ourParams, theirParams, TxIn(OutPoint(anchorTxHash, anchorOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, ourRevocationHash, ourSpec)
val ourSig = sign(ourParams, theirParams, anchorAmount, ourTx)
val signedTx = addSigs(ourParams, theirParams, anchorAmount: Long, ourTx, ourSig, theirSig)
checksig(ourParams, theirParams, anchorOutput, signedTx) match {
case false =>
them ! error(Some("Bad signature"))
goto(CLOSED)
case true =>
them ! open_commit_sig(ourSig)
val theirTx = makeTheirTx(ourParams, theirParams, TxIn(OutPoint(anchorTxHash, anchorOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, theirRevocationHash, theirSpec)
val ourSigForThem = sign(ourParams, theirParams, anchorAmount, theirTx)
them ! open_commit_sig(ourSigForThem)
blockchain ! WatchConfirmed(self, anchorTxid, ourParams.minDepth, BITCOIN_ANCHOR_DEPTHOK)
blockchain ! WatchSpent(self, anchorTxid, anchorOutputIndex, 0, BITCOIN_ANCHOR_SPENT)
goto(OPEN_WAITING_THEIRANCHOR) using DATA_OPEN_WAITING(ourParams, theirParams, ShaChain.init, OurCommit(0, spec, signedTx), TheirCommit(0, spec, theirRevocationHash), theirNextRevocationHash, None, anchorOutput)
goto(OPEN_WAITING_THEIRANCHOR) using DATA_OPEN_WAITING(ourParams, theirParams, ShaChain.init, OurCommit(0, ourSpec, signedTx), TheirCommit(0, theirSpec, theirRevocationHash), theirNextRevocationHash, None, anchorOutput)
}
case Event(CMD_CLOSE(_), _) => goto(CLOSED)
@ -104,8 +120,9 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
// we build our commitment tx, sign it and check that it is spendable using the counterparty's sig
val ourRevocationHash = Crypto.sha256(ShaChain.shaChainFromSeed(ourParams.shaSeed, 0))
val ourTx = makeOurTx(ourParams, theirParams, TxIn(OutPoint(anchorTx, anchorOutputIndex), Array.emptyByteArray, 0xffffffffL) :: Nil, ourRevocationHash, spec)
val ourSig = sign(ourParams, theirParams, ourTx)
val signedTx: Transaction = addSigs(ourParams, theirParams, ourTx, ourSig, theirSig)
val anchorAmount = anchorTx.txOut(anchorOutputIndex).amount.toLong
val ourSig = sign(ourParams, theirParams, anchorAmount, ourTx)
val signedTx: Transaction = addSigs(ourParams, theirParams, anchorAmount, ourTx, ourSig, theirSig)
val anchorOutput = anchorTx.txOut(anchorOutputIndex)
checksig(ourParams, theirParams, anchorOutput, signedTx) match {
case false =>
@ -315,7 +332,7 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
case Some(theirNextRevocationHash) =>
val spec = reduce(theirCommit.spec, Nil, ourChanges.proposed)
val theirTx = makeTheirTx(ourParams, theirParams, ourCommit.publishableTx.txIn, theirNextRevocationHash, spec)
val ourSig = sign(ourParams, theirParams, theirTx)
val ourSig = sign(ourParams, theirParams, anchorOutput.amount.toLong, theirTx)
them ! update_commit(ourSig)
stay using d.copy(theirCommit = TheirCommit(theirCommit.index + 1, spec, theirNextRevocationHash), ourChanges = ourChanges.copy(proposed = Nil, signed = ourChanges.proposed))
case None => throw new RuntimeException(s"cannot send two update_commit in a row (must wait for revocation)")
@ -326,8 +343,8 @@ class Channel(val them: ActorRef, val blockchain: ActorRef, val params: OurChann
val ourRevocationPreimage = ShaChain.shaChainFromSeed(ourParams.shaSeed, ourCommit.index)
val ourRevocationHash = Crypto.sha256(ourRevocationPreimage)
val ourTx = makeOurTx(ourParams, theirParams, ourCommit.publishableTx.txIn, ourRevocationHash, spec)
val ourSig = sign(ourParams, theirParams, ourTx)
val signedTx = addSigs(ourParams, theirParams, ourTx, ourSig, theirSig)
val ourSig = sign(ourParams, theirParams, anchorOutput.amount.toLong, ourTx)
val signedTx = addSigs(ourParams, theirParams, anchorOutput.amount.toLong, ourTx, ourSig, theirSig)
checksig(ourParams, theirParams, anchorOutput, ourTx) match {
case false =>
them ! error(Some("Bad signature"))

View File

@ -42,18 +42,19 @@ object Helpers {
def makeOurTx(ourParams: OurChannelParams, theirParams: TheirChannelParams, inputs: Seq[TxIn], ourRevocationHash: sha256_hash, spec: CommitmentSpec): Transaction =
makeCommitTx(inputs, ourParams.finalPubKey, theirParams.finalPubKey, ourParams.delay, ourRevocationHash, null)
makeCommitTx(inputs, ourParams.finalPubKey, theirParams.finalPubKey, ourParams.delay, ourRevocationHash, spec)
def makeTheirTx(ourParams: OurChannelParams, theirParams: TheirChannelParams, inputs: Seq[TxIn], theirRevocationHash: sha256_hash, spec: CommitmentSpec): Transaction =
makeCommitTx(inputs, theirParams.finalPubKey, ourParams.finalPubKey, theirParams.delay, theirRevocationHash, null)
makeCommitTx(inputs, theirParams.finalPubKey, ourParams.finalPubKey, theirParams.delay, theirRevocationHash, spec)
def sign(ourParams: OurChannelParams, theirParams: TheirChannelParams, tx: Transaction): signature =
bin2signature(Transaction.signInput(tx, 0, multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey))
def sign(ourParams: OurChannelParams, theirParams: TheirChannelParams, anchorAmount: Long, tx: Transaction): signature =
bin2signature(Transaction.signInput(tx, 0, multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, anchorAmount, 1, ourParams.commitPrivKey))
def addSigs(ourParams: OurChannelParams, theirParams: TheirChannelParams, tx: Transaction, ourSig: signature, theirSig: signature): Transaction = {
def addSigs(ourParams: OurChannelParams, theirParams: TheirChannelParams, anchorAmount: Long, tx: Transaction, ourSig: signature, theirSig: signature): Transaction = {
// TODO : Transaction.sign(...) should handle multisig
val ourSig = Transaction.signInput(tx, 0, multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, ourParams.commitPrivKey)
tx.updateSigScript(0, sigScript2of2(theirSig, ourSig, theirParams.commitPubKey, ourParams.commitPubKey))
val ourSig = Transaction.signInput(tx, 0, multiSig2of2(ourParams.commitPubKey, theirParams.commitPubKey), SIGHASH_ALL, anchorAmount, 1, ourParams.commitPrivKey)
val witness = witness2of2(theirSig, ourSig, theirParams.commitPubKey, ourParams.commitPubKey)
tx.copy(witness = Seq(witness))
}
def checksig(ourParams: OurChannelParams, theirParams: TheirChannelParams, anchorOutput: TxOut, tx: Transaction): Boolean =

View File

@ -174,6 +174,31 @@ object Scripts {
permuteOutputs(tx1)
}
def makeCommitTx(inputs: Seq[TxIn], ourFinalKey: BinaryData, theirFinalKey: BinaryData, theirDelay: locktime, revocationHash: BinaryData, commitmentSpec: CommitmentSpec): Transaction = {
val redeemScript = redeemSecretOrDelay(ourFinalKey, locktime2long_csv(theirDelay), theirFinalKey, revocationHash: BinaryData)
val outputs = Seq(
// TODO : is that the correct way to handle sub-satoshi balances ?
TxOut(amount = Satoshi(commitmentSpec.amount_us / 1000), publicKeyScript = pay2wsh(redeemScript)),
TxOut(amount = Satoshi(commitmentSpec.amount_them / 1000), publicKeyScript = pay2wpkh(theirFinalKey))
).filterNot(_.amount.toLong < 546) // do not add dust
val tx = Transaction(
version = 2,
txIn = inputs,
txOut = outputs,
lockTime = 0)
val sendOuts = commitmentSpec.htlcs.filter(_.direction == OUT).map(htlc =>
TxOut(Satoshi(htlc.amountMsat / 1000), pay2wsh(scriptPubKeyHtlcSend(ourFinalKey, theirFinalKey, locktime2long_cltv(htlc.expiry), locktime2long_csv(theirDelay), htlc.rHash, revocationHash)))
)
val receiveOuts = commitmentSpec.htlcs.filter(_.direction == IN).map(htlc =>
TxOut(Satoshi(htlc.amountMsat / 1000), pay2wsh(scriptPubKeyHtlcReceive(ourFinalKey, theirFinalKey, locktime2long_cltv(htlc.expiry), locktime2long_csv(theirDelay), htlc.rHash, revocationHash)))
)
val tx1 = tx.copy(txOut = tx.txOut ++ sendOuts ++ receiveOuts)
permuteOutputs(tx1)
}
/**
* This is a simple tx with a multisig input and two pay2sh output
*