diff --git a/eclair-demo/src/main/scala/fr/acinq/eclair/Boot.scala b/eclair-demo/src/main/scala/fr/acinq/eclair/Boot.scala index 966c145f0..3754c904e 100644 --- a/eclair-demo/src/main/scala/fr/acinq/eclair/Boot.scala +++ b/eclair-demo/src/main/scala/fr/acinq/eclair/Boot.scala @@ -1,6 +1,7 @@ package fr.acinq.eclair import akka.actor.{Props, ActorSystem} +import fr.acinq.bitcoin._ import lightning.sha256_hash import org.bouncycastle.util.encoders.Hex @@ -11,8 +12,10 @@ object Boot extends App { val system = ActorSystem() - val alice = system.actorOf(Props(new Node(Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), Hex.decode("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), INIT_WITHANCHOR)), name = "alice") - val bob = system.actorOf(Props(new Node(Hex.decode("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), Hex.decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"), INIT_NOANCHOR)), name = "bob") + val previousTx = Transaction.read("0100000001bb4f5a244b29dc733c56f80c0fed7dd395367d9d3b416c01767c5123ef124f82000000006b4830450221009e6ed264343e43dfee2373b925915f7a4468e0bc68216606e40064561e6c097a022030f2a50546a908579d0fab539d5726a1f83cfd48d29b89ab078d649a8e2131a0012103c80b6c289bf0421d010485cec5f02636d18fb4ed0f33bfa6412e20918ebd7a34ffffffff0200093d00000000001976a9145dbf52b8d7af4fb5f9b75b808f0a8284493531b388acf0b0b805000000001976a914807c74c89592e8a260f04b5a3bc63e7bef8c282588ac00000000") + + val alice = system.actorOf(Props(new Node(Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), Hex.decode("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), Some(AnchorInput(100000000L, OutPoint(previousTx, 0), SignData(previousTx.txOut(0).publicKeyScript, Base58Check.decode("cV7LGVeY2VPuCyCSarqEqFCUNig2NzwiAEBTTA89vNRQ4Vqjfurs")._2))))), name = "alice") + val bob = system.actorOf(Props(new Node(Hex.decode("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), Hex.decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"), None)), name = "bob") bob.tell(INPUT_NONE, alice) alice.tell(INPUT_NONE, bob) diff --git a/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala b/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala index 41f68d31f..727a845ea 100644 --- a/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala +++ b/eclair-demo/src/main/scala/fr/acinq/eclair/Node.scala @@ -46,12 +46,13 @@ final case class SendHtlcUpdate(amount: Long, finalPayee: String, rHash: sha256_ // DATA sealed trait Data -case object Uninitialized extends Data +case object Nothing extends Data +final case class AnchorInput(amount: Long, previousTxOutput: OutPoint, signData: SignData) extends Data final case class ChannelParams(delay: locktime, commitKey: bitcoin_pubkey, finalKey: bitcoin_pubkey, minDepth: Int, commitmentFee: Long) final case class Anchor(txid: sha256_hash, outputIndex: Int, amount: Long) final case class CommitmentTx(tx: Transaction, ourRevocationPreimage: sha256_hash) final case class DATA_OPEN_WAIT_FOR_OPEN_NOANCHOR(ourParams: ChannelParams, ourRevocationPreimage: sha256_hash) extends Data -final case class DATA_OPEN_WAIT_FOR_OPEN_WITHANCHOR(ourParams: ChannelParams, ourRevocationPreimage: sha256_hash) extends Data +final case class DATA_OPEN_WAIT_FOR_OPEN_WITHANCHOR(ourParams: ChannelParams, anchorInput: AnchorInput, ourRevocationPreimage: sha256_hash) extends Data final case class DATA_OPEN_WAIT_FOR_ANCHOR(ourParams: ChannelParams, theirParams: ChannelParams, ourRevocationPreimage: sha256_hash, theirRevocationHash: sha256_hash) extends Data final case class DATA_OPEN_WAIT_FOR_COMMIT_SIG(ourParams: ChannelParams, theirParams: ChannelParams, anchor: Anchor, ourRevocationPreimage: sha256_hash, theirRevocationHash: sha256_hash) extends Data final case class DATA_OPEN_WAITING(ourParams: ChannelParams, theirParams: ChannelParams, anchor: Anchor, commitmentTx: CommitmentTx, otherPartyOpen: Boolean = false) extends Data @@ -59,7 +60,7 @@ final case class DATA_NORMAL(ourParams: ChannelParams, theirParams: ChannelParam // -class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val initialState: State = INIT_NOANCHOR) extends LoggingFSM[State, Data] { +class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val anchorDataOpt: Option[AnchorInput]) extends LoggingFSM[State, Data] { val DEFAULT_delay = locktime(Blocks(1)) val DEFAULT_minDepth = 2 @@ -71,7 +72,10 @@ class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val init // TODO var them: ActorRef = null - startWith(initialState, Uninitialized) + anchorDataOpt match { + case None => startWith(INIT_NOANCHOR, Nothing) + case Some(anchorData) => startWith(INIT_WITHANCHOR, anchorData) + } when(INIT_NOANCHOR) { case Event(INPUT_NONE, _) => @@ -84,13 +88,13 @@ class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val init } when(INIT_WITHANCHOR) { - case Event(INPUT_NONE, _) => + case Event(INPUT_NONE, anchorInput: AnchorInput) => them = sender val ourParams = ChannelParams(DEFAULT_delay, commitPubKey, finalPubKey, DEFAULT_minDepth, DEFAULT_commitmentFee) val ourRevocationHashPreimage = sha256_hash(1, 2, 3, 4) //TODO : should be random val ourRevocationHash = Crypto.sha256(ourRevocationHashPreimage) sender ! open_channel(ourParams.delay, ourRevocationHash, ourParams.commitKey, ourParams.finalKey, WILL_CREATE_ANCHOR, Some(ourParams.minDepth), ourParams.commitmentFee) - goto(OPEN_WAIT_FOR_OPEN_WITHANCHOR) using DATA_OPEN_WAIT_FOR_OPEN_WITHANCHOR(ourParams, ourRevocationHashPreimage) + goto(OPEN_WAIT_FOR_OPEN_WITHANCHOR) using DATA_OPEN_WAIT_FOR_OPEN_WITHANCHOR(ourParams, anchorInput, ourRevocationHashPreimage) } when(OPEN_WAIT_FOR_OPEN_NOANCHOR) { @@ -100,14 +104,14 @@ class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val init } when(OPEN_WAIT_FOR_OPEN_WITHANCHOR) { - case Event(open_channel(delay, revocationHash, commitKey, finalKey, WONT_CREATE_ANCHOR, minDepth, commitmentFee), DATA_OPEN_WAIT_FOR_OPEN_WITHANCHOR(ourParams, ourRevocationHashPreimage)) => + case Event(open_channel(delay, revocationHash, commitKey, finalKey, WONT_CREATE_ANCHOR, minDepth, commitmentFee), DATA_OPEN_WAIT_FOR_OPEN_WITHANCHOR(ourParams, anchorInput, ourRevocationHashPreimage)) => val theirParams = ChannelParams(delay, commitKey, finalKey, minDepth.get, commitmentFee) // TODO create the anchor (without publishing it !) - val anchor = Anchor(sha256_hash(1, 2, 3, 4), 0, 100000) + val anchor = Anchor(anchorInput.previousTxOutput.hash, anchorInput.previousTxOutput.index.toInt, anchorInput.amount) // then we build their commitment tx and sign it val state = ChannelState(ChannelOneSide(anchor.amount, 0, Seq()), ChannelOneSide(0, 0, Seq())) val theirCommitTx = makeCommitTx(theirParams.finalKey, ourParams.finalKey, ourParams.delay, anchor.txid, anchor.outputIndex, Crypto.sha256(ourRevocationHashPreimage), state.copy(a = state.b, b = state.a)) - val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, pubkey2bin(commitPrivKey))) + val ourSigForThem = bin2signature(Transaction.signInput(theirCommitTx, anchor.outputIndex, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, pubkey2bin(commitPrivKey))) sender ! open_anchor(anchor.txid, anchor.outputIndex, anchor.amount, ourSigForThem) goto(OPEN_WAIT_FOR_COMMIT_SIG) using DATA_OPEN_WAIT_FOR_COMMIT_SIG(ourParams, theirParams, anchor, ourRevocationHashPreimage, revocationHash) } @@ -118,7 +122,7 @@ class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val init val state = ChannelState(ChannelOneSide(amount, 0, Seq()), ChannelOneSide(0, 0, Seq())) // we build our commitment tx, sign it and check that it is spendable using the counterparty's sig val ourCommitTx = makeCommitTx(ourParams.finalKey, theirParams.finalKey, theirParams.delay, anchor.txid, anchor.outputIndex, theirRevocationHash, state) - val ourSig = Transaction.signInput(ourCommitTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, pubkey2bin(commitPrivKey)) + val ourSig = Transaction.signInput(ourCommitTx, anchor.outputIndex, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, pubkey2bin(commitPrivKey)) val signedCommitTx = ourCommitTx.updateSigScript(0, sigScript2of2(theirSig, ourSig, theirParams.commitKey, ourParams.commitKey)) val ok = Try(Transaction.correctlySpends(signedCommitTx, Map(OutPoint(txid, outputIndex) -> multiSig2of2(ourParams.commitKey, theirParams.commitKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)).isSuccess // TODO : return Error and close channel if !ok @@ -135,7 +139,7 @@ class Node(val commitPrivKey: BinaryData, val finalPrivKey: BinaryData, val init val state = ChannelState(ChannelOneSide(anchor.amount, 0, Seq()), ChannelOneSide(0, 0, Seq())) // we build our commitment tx, sign it and check that it is spendable using the counterparty's sig val ourCommitTx = makeCommitTx(ourParams.finalKey, theirParams.finalKey, theirParams.delay, anchor.txid, anchor.outputIndex, theirRevocationHash, state) - val ourSig = Transaction.signInput(ourCommitTx, 0, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, pubkey2bin(commitPrivKey)) + val ourSig = Transaction.signInput(ourCommitTx, anchor.outputIndex, multiSig2of2(ourParams.commitKey, theirParams.commitKey), SIGHASH_ALL, pubkey2bin(commitPrivKey)) val signedCommitTx = ourCommitTx.updateSigScript(0, sigScript2of2(theirSig, ourSig, theirParams.commitKey, ourParams.commitKey)) val ok = Try(Transaction.correctlySpends(signedCommitTx, Map(OutPoint(anchor.txid, anchor.outputIndex) -> multiSig2of2(ourParams.commitKey, theirParams.commitKey)), ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)).isSuccess // TODO : return Error and close channel if !ok