From e57a484956c6e320980123dd3f13c7bc05594cdd Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 15:48:16 +0100 Subject: [PATCH 01/21] Added message flags for ACK messages --- .../communication/layer/high/AckMessage.java | 8 ++++ .../layer/high/AckMessageImpl.java | 44 +++++++++++++++++++ .../layer/high/AckableMessage.java | 7 +++ .../layer/high/ChannelSpecificMessage.java | 8 ++++ .../layer/high/NumberedMessage.java | 8 ++++ .../high/payments/messages/LNPayment.java | 6 --- 6 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessage.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessageImpl.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckableMessage.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelSpecificMessage.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/NumberedMessage.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessage.java new file mode 100644 index 00000000..f087fddb --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessage.java @@ -0,0 +1,8 @@ +package network.thunder.core.communication.layer.high; + +import network.thunder.core.communication.layer.Message; + +public interface AckMessage extends Message { + long getMessageNumberToAck (); + void setMessageNumberToAck (long number); +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessageImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessageImpl.java new file mode 100644 index 00000000..60ec8c51 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckMessageImpl.java @@ -0,0 +1,44 @@ +package network.thunder.core.communication.layer.high; + +import com.google.common.base.Preconditions; + +public class AckMessageImpl implements AckMessage, NumberedMessage { + long messageNumberToAck; + long messageNumber; + + public AckMessageImpl (long messageNumberToAck) { + this.messageNumberToAck = messageNumberToAck; + } + + @Override + public long getMessageNumber () { + return messageNumber; + } + + @Override + public void setMessageNumber (long number) { + this.messageNumber = number; + } + + @Override + public long getMessageNumberToAck () { + return messageNumberToAck; + } + + @Override + public void setMessageNumberToAck (long number) { + this.messageNumberToAck = number; + } + + @Override + public void verify () { + Preconditions.checkArgument(messageNumberToAck != 0); + } + + @Override + public String toString () { + return "AckMessageImpl{" + + "messageNumberToAck=" + messageNumberToAck + + '}'; + } +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckableMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckableMessage.java new file mode 100644 index 00000000..57975eec --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/AckableMessage.java @@ -0,0 +1,7 @@ +package network.thunder.core.communication.layer.high; + +import network.thunder.core.communication.layer.Message; + +public abstract class AckableMessage implements Message, NumberedMessage { + +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelSpecificMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelSpecificMessage.java new file mode 100644 index 00000000..f1d23b34 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelSpecificMessage.java @@ -0,0 +1,8 @@ +package network.thunder.core.communication.layer.high; + +import org.bitcoinj.core.Sha256Hash; + +public interface ChannelSpecificMessage { + Sha256Hash getChannelHash(); + void setChannelHash(Sha256Hash hash); +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/NumberedMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/NumberedMessage.java new file mode 100644 index 00000000..f3abe6a2 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/NumberedMessage.java @@ -0,0 +1,8 @@ +package network.thunder.core.communication.layer.high; + +import network.thunder.core.communication.layer.Message; + +public interface NumberedMessage extends Message { + long getMessageNumber (); + void setMessageNumber (long number); +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java deleted file mode 100644 index e35f0105..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java +++ /dev/null @@ -1,6 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.messages; - -import network.thunder.core.communication.layer.Message; - -public interface LNPayment extends Message { -} From 6ef2915004fed993f082dfee37dc8ae0d55f8e1a Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 16:53:28 +0100 Subject: [PATCH 02/21] Reworked payment negotiation With more stricter ordering and resending of messages, the protocol for negotiating a new channel status can get simplified. As resending lost messages means that a started exchange can not be aborted anymore, we can already define the next revocation hash at the end of the current exchange. With a known revocation hash, the initiator can directly send signatures for the update. Also, LNPaymentProcessor and LNPaymentLogic have been made less stateful with relying on the database as single source of truth. This means that much logic has been moved into the DBHandler. --- .../thunder/core/communication/NodeKey.java | 18 + .../communication/layer/MessageWrapper.kt | 12 + .../layer/high/RevocationHash.java | 87 +-- .../layer/high/payments/LNPaymentHelper.java | 12 +- .../high/payments/LNPaymentHelperImpl.java | 166 +++--- .../layer/high/payments/LNPaymentLogic.java | 38 +- .../high/payments/LNPaymentLogicImpl.java | 270 +++------- .../high/payments/LNPaymentProcessor.java | 14 +- .../high/payments/LNPaymentProcessorImpl.java | 463 ---------------- .../high/payments/LNPaymentProcessorImpl.kt | 369 +++++++++++++ .../layer/high/payments/PaymentResponse.kt | 13 + .../layer/high/payments/PaymentSecret.java | 7 + .../layer/high/payments/PaymentState.kt | 13 + .../high/payments/messages/ChannelUpdate.java | 2 +- .../high/payments/messages/LNPayment.java | 44 ++ .../payments/messages/LNPaymentAMessage.java | 38 +- .../payments/messages/LNPaymentBMessage.java | 33 +- .../payments/messages/LNPaymentCMessage.java | 29 +- .../payments/messages/LNPaymentDMessage.java | 25 - .../messages/LNPaymentMessageFactory.java | 11 +- .../messages/LNPaymentMessageFactoryImpl.java | 38 -- .../high/payments/messages/OnionObject.java | 10 +- .../high/payments/queue/QueueElement.java | 15 - .../payments/queue/QueueElementPayment.java | 38 -- .../payments/queue/QueueElementRedeem.java | 40 -- .../payments/queue/QueueElementRefund.java | 38 -- .../payments/queue/QueueElementUpdate.java | 13 - .../thunder/core/database/DBHandler.java | 61 ++- .../core/database/InMemoryDBHandler.java | 500 ++++++++++++++---- .../core/database/objects/PaymentStatus.java | 12 +- .../core/database/objects/PaymentWrapper.java | 22 +- .../network/thunder/core/etc/Constants.java | 3 + .../thunder/core/helper/HashDerivation.java | 99 ---- .../core/helper/events/LNEventHelper.java | 5 +- .../core/helper/events/LNEventHelperImpl.java | 10 +- .../core/helper/events/LNEventListener.java | 5 + 36 files changed, 1230 insertions(+), 1343 deletions(-) create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/MessageWrapper.kt delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentState.kt create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentDMessage.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactoryImpl.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElement.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementPayment.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRedeem.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRefund.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementUpdate.java delete mode 100644 thunder-core/src/main/java/network/thunder/core/helper/HashDerivation.java diff --git a/thunder-core/src/main/java/network/thunder/core/communication/NodeKey.java b/thunder-core/src/main/java/network/thunder/core/communication/NodeKey.java index bfc686d3..cbbb6f9a 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/NodeKey.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/NodeKey.java @@ -1,5 +1,6 @@ package network.thunder.core.communication; +import network.thunder.core.etc.Tools; import org.bitcoinj.core.ECKey; import java.util.Arrays; @@ -15,6 +16,19 @@ public NodeKey (ECKey nodeKey) { this.nodeKey = nodeKey; } + public byte[] getPubKey () { + return nodeKey.getPubKey(); + } + + public String getPubKeyHex () { + return Tools.bytesToHex(nodeKey.getPubKey()); + } + + @Override + public String toString () { + return getPubKeyHex().substring(0, 10); + } + @Override public boolean equals (Object o) { if (this == o) { @@ -34,4 +48,8 @@ public boolean equals (Object o) { public int hashCode () { return nodeKey != null ? nodeKey.hashCode() : 0; } + + public static NodeKey wrap (ECKey key) { + return new NodeKey(key); + } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageWrapper.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageWrapper.kt new file mode 100644 index 00000000..3843aaf6 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageWrapper.kt @@ -0,0 +1,12 @@ +package network.thunder.core.communication.layer + +import network.thunder.core.etc.Tools + +data class MessageWrapper( + val message: Message, + val timestamp: Int = Tools.currentTime(), + val direction: DIRECTION = DIRECTION.SENT +) + + +enum class DIRECTION {RECEIVED, SENT } \ No newline at end of file diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java index a94f27c8..9affe6df 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java @@ -21,39 +21,48 @@ import network.thunder.core.etc.Tools; +import java.nio.ByteBuffer; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; +import static network.thunder.core.etc.Tools.hashSecret; + /** * Class for revocation hash. * Even though it is in theory the same as a payment hash, we use a different class, to avoid confusion. (which would lead to a direct loss of funds..) */ public class RevocationHash { - private int depth; - private int child; - private byte[] secret; - private byte[] secretHash; - - public RevocationHash (int depth, int child, byte[] secret, byte[] secretHash) { - this.depth = depth; - this.child = child; + public int index; + public byte[] secret; + public byte[] secretHash; + + public RevocationHash (int depth, byte[] secret, byte[] secretHash) { + this.index = depth; this.secret = secret; this.secretHash = secretHash; } - public RevocationHash (int depth, int child, byte[] secret) { - this.depth = depth; - this.child = child; - this.secret = secret; - this.secretHash = Tools.hashSecret(secret); + public RevocationHash (int index, byte[] seed) { + this.index = index; + + //TODO implement shachain here + ByteBuffer byteBuffer = ByteBuffer.allocate(4 + seed.length); + byteBuffer.putInt(index); + byteBuffer.put(seed); + this.secret = hashSecret(byteBuffer.array()); + this.secretHash = hashSecret(this.secret); + } public RevocationHash (ResultSet set) throws SQLException { this.secretHash = set.getBytes("secretHash"); this.secret = set.getBytes("secret"); - this.depth = set.getInt("depth"); - this.child = set.getInt("child"); + this.index = set.getInt("index"); + } + + public RevocationHash copy () { + return new RevocationHash(this.index, this.secret, this.secretHash); } /** @@ -62,49 +71,12 @@ public RevocationHash (ResultSet set) throws SQLException { * @return true, if successful */ public boolean check () { - /* - * Child = 0 is - per convention - a new masterkey. We will check it later. - */ - if (child == 0) { - return true; - } - - if (secret == null) { - return false; - } - - return Arrays.equals(secretHash, Tools.hashSecret(secret)); - - } - - public int getChild () { - return child; - } - - public int getDepth () { - return depth; - } - - /** - * The preimage corresponding to the hash. - * Losing it to the counterparty before the revocation may lead to loss of funds. - */ - - public byte[] getSecret () { - return secret; - } - - /** - * The hash necessary for the transactions to be revocable. - */ - public byte[] getSecretHash () { - return secretHash; + return secret != null && Arrays.equals(secretHash, hashSecret(secret)); } @Override public int hashCode () { - int result = depth; - result = 31 * result + child; + int result = index; result = 31 * result + (secret != null ? Arrays.hashCode(secret) : 0); result = 31 * result + (secretHash != null ? Arrays.hashCode(secretHash) : 0); return result; @@ -121,10 +93,7 @@ public boolean equals (Object o) { RevocationHash that = (RevocationHash) o; - if (depth != that.depth) { - return false; - } - if (child != that.child) { + if (index != that.index) { return false; } if (!Arrays.equals(secret, that.secret)) { @@ -136,7 +105,7 @@ public boolean equals (Object o) { @Override public String toString () { - return "RevocationHash{" + + return "RevocationHash{" + index + ": " + "" + Tools.bytesToHex(secretHash).substring(0, 6) + ".." + '}'; } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelper.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelper.java index ec3c657e..44dfe3f3 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelper.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelper.java @@ -1,15 +1,11 @@ package network.thunder.core.communication.layer.high.payments; -public interface LNPaymentHelper { - void addProcessor (LNPaymentProcessor processor); +import network.thunder.core.communication.NodeKey; - void removeProcessor (LNPaymentProcessor processor); +public interface LNPaymentHelper { + void addProcessor (NodeKey nodeKey, LNPaymentProcessor processor); - void relayPayment (LNPaymentProcessor paymentProcessor, PaymentData paymentData); + void removeProcessor (NodeKey nodeKey); void makePayment (PaymentData paymentData); - - void paymentRedeemed (PaymentSecret paymentSecret); - - void paymentRefunded (PaymentData paymentSecret); } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelperImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelperImpl.java index dcb57c1d..c0b18c3a 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelperImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentHelperImpl.java @@ -1,17 +1,17 @@ package network.thunder.core.communication.layer.high.payments; import network.thunder.core.communication.LNConfiguration; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.ServerObject; import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.high.payments.messages.PeeledOnion; import network.thunder.core.communication.processor.exceptions.LNPaymentException; import network.thunder.core.database.DBHandler; -import network.thunder.core.etc.Tools; import network.thunder.core.helper.events.LNEventHelper; -import org.bitcoinj.core.ECKey; +import network.thunder.core.helper.events.LNEventListener; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class LNPaymentHelperImpl implements LNPaymentHelper { @@ -21,7 +21,7 @@ public class LNPaymentHelperImpl implements LNPaymentHelper { ServerObject serverObject; LNConfiguration configuration; - List processorList = new ArrayList<>(); + Map processorList = new ConcurrentHashMap<>(); public LNPaymentHelperImpl (ContextFactory contextFactory, DBHandler dbHandler) { this.onionHelper = contextFactory.getOnionHelper(); @@ -29,62 +29,87 @@ public LNPaymentHelperImpl (ContextFactory contextFactory, DBHandler dbHandler) this.eventHelper = contextFactory.getEventHelper(); this.serverObject = contextFactory.getServerSettings(); configuration = serverObject.configuration; + + this.eventHelper.addListener(new LNEventListener() { + @Override + public void onPaymentAdded (NodeKey nodeKey, PaymentData payment) { + relayPayment(nodeKey, payment); + } + + @Override + public void onPaymentCompleted (PaymentData payment) { + NodeKey senderOfPayment = dbHandler.getSenderOfPayment(payment.secret); + if (senderOfPayment != null) { + pingProcessor(senderOfPayment); + } + } + + @Override + public void onPaymentRefunded (PaymentData payment) { + NodeKey senderOfPayment = dbHandler.getSenderOfPayment(payment.secret); + if (senderOfPayment != null) { + pingProcessor(senderOfPayment); + } else { + System.out.println("LNPaymentHelperImpl.onPaymentRefunded - we were the sender?"); + } + } + }); } @Override - public void addProcessor (LNPaymentProcessor processor) { - processorList.add(processor); + public void addProcessor (NodeKey nodeKey, LNPaymentProcessor processor) { + processorList.put(nodeKey, processor); } @Override - public void removeProcessor (LNPaymentProcessor processor) { - processorList.remove(processor); + public void removeProcessor (NodeKey nodeKey) { + processorList.remove(nodeKey); } - @Override - public synchronized void relayPayment (LNPaymentProcessor processorSent, PaymentData paymentData) { - try { + private boolean pingProcessor (NodeKey nodeKey) { + LNPaymentProcessor processor = processorList.get(nodeKey); + if (processor != null) { + processor.ping(); + return true; + } + return false; + } + public void relayPayment (NodeKey sender, PaymentData paymentData) { + try { PeeledOnion peeledOnion = getPeeledOnion(paymentData); - saveReceiverToDatabase(paymentData, peeledOnion); - if (peeledOnion.isLastHop) { - PaymentSecret secret = dbHandler.getPaymentSecret(paymentData.secret); - if (secret == null) { - processorSent.refundPayment(paymentData); - } else { - processorSent.redeemPayment(secret); - } - + pingProcessor(sender); } else { paymentData.onionObject = peeledOnion.onionObject; - ECKey nextHop = peeledOnion.nextHop; - - if (!relayPaymentToCorrectProcessor(paymentData, nextHop)) { - //TODO Can't connect the payment right now. Check the DB if we even have a channel with - // the next node, refund the payment back if we don't... - System.out.println("Currently not connected with " + Tools.bytesToHex(nextHop.getPubKey()) + ". Refund..."); - processorSent.refundPayment(paymentData); + NodeKey receiver = peeledOnion.nextHop; + if (!pingProcessor(receiver)) { + if (dbHandler.getOpenChannel(receiver).size() == 0) { + //No payment channel with next hop, will just send back + pingProcessor(sender); + } } } - } catch (Exception e) { e.printStackTrace(); - processorSent.refundPayment(paymentData); + LNPaymentProcessor senderProcessor = processorList.get(sender); + if (senderProcessor != null) { + senderProcessor.ping(); + } } - } @Override public void makePayment (PaymentData paymentData) { try { PeeledOnion peeledOnion = getPeeledOnion(paymentData); - saveReceiverToDatabase(paymentData, peeledOnion); - paymentData.onionObject = peeledOnion.onionObject; - ECKey nextHop = peeledOnion.nextHop; + NodeKey nextHop = peeledOnion.nextHop; - if (!relayPaymentToCorrectProcessor(paymentData, nextHop)) { + if (processorList.containsKey(nextHop)) { + dbHandler.addPayment(nextHop, paymentData); + pingProcessor(nextHop); + } else { throw new LNPaymentException("Not connected to next hop " + nextHop); } @@ -94,78 +119,7 @@ public void makePayment (PaymentData paymentData) { } } - private boolean relayPaymentToCorrectProcessor (PaymentData paymentData, ECKey nextHop) { - for (LNPaymentProcessor processor : processorList) { - if (processor.connectsToNodeId(nextHop.getPubKey())) { - PaymentData copy = paymentData.cloneObject(); - copy.sending = true; - copy.timestampOpen = Tools.currentTime(); - copy.timestampRefund -= configuration.getTimeToReduceWhenRelayingPayment(); - //Last check to see if there is sufficient refund time left.. - if ((copy.timestampRefund - Tools.currentTime()) < (configuration.MIN_OVERLAY_REFUND * configuration.MIN_REFUND_DELAY)) { - System.out.println("Not sufficient refund time left - refund!"); - return false; - } - processor.makePayment(copy); - return true; - } - } - return false; - } - private PeeledOnion getPeeledOnion (PaymentData paymentData) { return onionHelper.loadMessage(serverObject.pubKeyServer, paymentData.onionObject); } - - private void saveReceiverToDatabase (PaymentData payment, PeeledOnion peeledOnion) { - if (peeledOnion.isLastHop) { - dbHandler.updatePaymentAddReceiverAddress(payment.secret, new byte[0]); - } else { - dbHandler.updatePaymentAddReceiverAddress(payment.secret, peeledOnion.nextHop.getPubKey()); - } - } - - @Override - public void paymentRedeemed (PaymentSecret paymentSecret) { - byte[] sender = dbHandler.getSenderOfPayment(paymentSecret); - - if (isEmptyByte(sender)) { - return; - } else { - for (LNPaymentProcessor processor : processorList) { - if (processor.connectsToNodeId(sender)) { - processor.redeemPayment(paymentSecret); - return; - } - } - System.out.println("Aren't connected to redeem payment.."); - //TODO sender of payment is offline right now - have to close channel if he does not come back online in time.. - //TODO redeem payment from other party on blockchain if channel is closed already.. - } - } - - @Override - public void paymentRefunded (PaymentData paymentData) { - - byte[] sender = dbHandler.getSenderOfPayment(paymentData.secret); - - if (sender == null) { - System.out.println("Can't find sender when trying to refund..?"); - //TODO ??? - this should not happen - but can't really resolve it either.. - } - - for (LNPaymentProcessor processor : processorList) { - if (processor.connectsToNodeId(sender)) { - processor.refundPayment(paymentData); - return; - } - } - - //TODO sender of payment is offline right now - he will close the channel if we can't get back to him in time.. - - } - - private static boolean isEmptyByte (byte[] bytes) { - return bytes.length == 0; - } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogic.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogic.java index 3f05c5ea..496fa18e 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogic.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogic.java @@ -1,32 +1,34 @@ package network.thunder.core.communication.layer.high.payments; +import network.thunder.core.communication.LNConfiguration; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.ChannelStatus; import network.thunder.core.communication.layer.high.channel.ChannelSignatures; import network.thunder.core.communication.layer.high.payments.messages.*; import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionOutPoint; +import java.util.List; + //Slowly moving away from not storing state in PaymentLogic anymore, but just business logic public interface LNPaymentLogic { - ChannelSignatures getSignatureObject (Channel channel, Transaction channelTransaction); - void initialise (Channel channel); - - void checkMessageIncoming (LNPayment message); - Transaction getChannelTransaction (TransactionOutPoint anchor, ChannelStatus channelStatus, ECKey client, ECKey server); - void checkSignatures (ECKey keyServer, ECKey keyClient, ChannelSignatures channelSignatures, Transaction channelTransaction, ChannelStatus status); - - Channel updateChannel (Channel channel); - - ChannelUpdate getChannelUpdate (); - - LNPaymentAMessage getAMessage (ChannelUpdate update); - - LNPaymentBMessage getBMessage (); - - LNPaymentCMessage getCMessage (); - - LNPaymentDMessage getDMessage (); + ChannelSignatures getSignatureObject (Channel channel, Transaction channelTransaction, List paymentTransactions); + List getPaymentTransactions (Sha256Hash parentTransactionHash, ChannelStatus channelStatus, ECKey keyServer, ECKey keyClient); + + void checkUpdate (LNConfiguration configuration, Channel channel, ChannelUpdate channelUpdate); + void checkSignatures (ECKey keyServer, ECKey keyClient, ChannelSignatures channelSignatures, Transaction channelTransaction, List + paymentTransactions, ChannelStatus status); + + Transaction getChannelTransaction (Channel channel, SIDE side); + ChannelSignatures getSignatureObject (Channel channel); + List getPaymentTransactions (Channel channel, SIDE side); + void checkSignatures (Channel channel, ChannelSignatures channelSignatures, SIDE side); + + public enum SIDE { + SERVER, + CLIENT + } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java index 482723fc..6e4ca151 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java @@ -1,14 +1,13 @@ package network.thunder.core.communication.layer.high.payments; -import com.google.common.base.Preconditions; import network.thunder.core.communication.LNConfiguration; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.ChannelStatus; import network.thunder.core.communication.layer.high.RevocationHash; import network.thunder.core.communication.layer.high.channel.ChannelSignatures; -import network.thunder.core.communication.layer.high.payments.messages.*; +import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; +import network.thunder.core.communication.layer.high.payments.messages.OnionObject; import network.thunder.core.communication.processor.exceptions.LNPaymentException; -import network.thunder.core.database.DBHandler; import network.thunder.core.etc.Constants; import network.thunder.core.etc.Tools; import network.thunder.core.helper.ScriptTools; @@ -19,39 +18,28 @@ import java.util.ArrayList; import java.util.List; -public class LNPaymentLogicImpl implements LNPaymentLogic { - - DBHandler dbHandler; - - Channel channel; - - ChannelStatus oldStatus; - ChannelStatus newStatus; - ChannelUpdate channelUpdate; - - ChannelSignatures channelSignatures; +import static network.thunder.core.communication.layer.high.payments.LNPaymentLogic.SIDE.*; - LNConfiguration configuration = new LNConfiguration(); - - LNPaymentMessageFactory messageFactory; +public class LNPaymentLogicImpl implements LNPaymentLogic { - public LNPaymentLogicImpl (LNPaymentMessageFactory messageFactory, DBHandler dbHandler) { - this.messageFactory = messageFactory; - this.dbHandler = dbHandler; - } + public static final int SIGNATURE_SIZE = 146; @Override public Transaction getChannelTransaction (TransactionOutPoint anchor, ChannelStatus channelStatus, ECKey client, ECKey server) { Transaction transaction = new Transaction(Constants.getNetwork()); transaction.addInput(anchor.getHash(), anchor.getIndex(), Tools.getDummyScript()); - transaction.addOutput(Coin.valueOf(0), ScriptTools.getChannelTxOutputRevocation(channelStatus.revocationHashClient, - client, server, Constants.ESCAPE_REVOCATION_TIME)); - transaction.addOutput(Coin.valueOf(0), channelStatus.addressServer); - transaction = addPayments(transaction, channelStatus, channelStatus.revocationHashClient, client, server); + transaction.addOutput(Coin.valueOf(0), + ScriptTools.getChannelTxOutputRevocation( + channelStatus.revoHashServerCurrent, + server, + client, + channelStatus.csvDelay)); + transaction.addOutput(Coin.valueOf(0), channelStatus.addressClient); + transaction = addPayments(transaction, channelStatus, channelStatus.revoHashServerCurrent, server, client); //Missing two signatures, max 146B - long fee = (long) Math.ceil((transaction.getMessageSize() + 146) * channelStatus.feePerByte / 2); + long fee = (long) Math.ceil((transaction.getMessageSize() + SIGNATURE_SIZE) * channelStatus.feePerByte / 2); transaction.getOutput(0).setValue(Coin.valueOf(channelStatus.amountClient - fee)); transaction.getOutput(1).setValue(Coin.valueOf(channelStatus.amountServer - fee)); @@ -76,79 +64,24 @@ public Transaction getChannelTransaction (TransactionOutPoint anchor, ChannelSta } @Override - public ChannelSignatures getSignatureObject (Channel channel, Transaction channelTransaction) { + public ChannelSignatures getSignatureObject (Channel channel, Transaction channelTransaction, List paymentTransactions) { ChannelSignatures channelSignatures = new ChannelSignatures(); - channelSignatures.channelSignatures = Tools.getChannelSignatures(channel, channelTransaction); - - List paymentTransactions = getPaymentTransactions(channelTransaction.getHash(), channel.channelStatus, channel.keyServer, channel.keyClient); List signatureList = new ArrayList<>(); int index = 2; for (Transaction t : paymentTransactions) { - TransactionSignature sig = Tools.getSignature(t, 0, channelTransaction.getOutput(index).getScriptBytes(), channel.getKeyServer()); + TransactionSignature sig = Tools.getSignature(t, 0, channelTransaction.getOutput(index).getScriptBytes(), channel.keyServer); signatureList.add(sig); index++; } channelSignatures.paymentSignatures = signatureList; - - return channelSignatures; } @Override - public void initialise (Channel channel) { - this.channel = channel; - } - - public Transaction getClientTransaction () { - Preconditions.checkNotNull(channel); - return getChannelTransaction( - new TransactionOutPoint(Constants.getNetwork(), 0, channel.anchorTxHash), - newStatus.getCloneReversed(), - channel.keyClient, - channel.keyServer); - } - - public Transaction getServerTransaction () { - Preconditions.checkNotNull(channel); - return getChannelTransaction( - new TransactionOutPoint(Constants.getNetwork(), 0, channel.anchorTxHash), - newStatus, - channel.keyServer, - channel.keyClient); - } - - public List getClientPaymentTransactions () { - Transaction t = getClientTransaction(); - return getPaymentTransactions(t.getHash(), newStatus.getCloneReversed(), channel.keyClient, channel.keyServer); - } - - public List getServerPaymentTransactions () { - Transaction t = getServerTransaction(); - return getPaymentTransactions(t.getHash(), newStatus, channel.keyServer, channel.keyClient); - } - - public List getChannelSignatures () { - //The channelTransaction is finished, we just need to produce the signatures.. - return Tools.getChannelSignatures(channel, getClientTransaction()); - } - - public List getPaymentSignatures () { - List paymentTransactions = getClientPaymentTransactions(); - List signatureList = new ArrayList<>(); - - int index = 2; - for (Transaction t : paymentTransactions) { - TransactionSignature sig = Tools.getSignature(t, 0, getClientTransaction().getOutput(index).getScriptBytes(), channel.getKeyServer()); - signatureList.add(sig); - index++; - } - return signatureList; - } - - private static List getPaymentTransactions (Sha256Hash parentTransactionHash, ChannelStatus channelStatus, ECKey keyServer, ECKey keyClient) { + public List getPaymentTransactions (Sha256Hash parentTransactionHash, ChannelStatus channelStatus, ECKey keyServer, ECKey keyClient) { List allPayments = new ArrayList<>(channelStatus.paymentList); List transactions = new ArrayList<>(allPayments.size()); @@ -161,7 +94,7 @@ private static List getPaymentTransactions (Sha256Hash parentTransa transaction.addInput(parentTransactionHash, index, Tools.getDummyScript()); Coin value = Coin.valueOf(payment.amount); - Script script = ScriptTools.getPaymentTxOutput(keyServer, keyClient, channelStatus.revocationHashServer, payment.csvDelay); + Script script = ScriptTools.getPaymentTxOutput(keyServer, keyClient, channelStatus.revoHashServerCurrent, channelStatus.csvDelay); transaction.addOutput(value, script); transactions.add(transaction); @@ -172,80 +105,16 @@ private static List getPaymentTransactions (Sha256Hash parentTransa } @Override - public void checkMessageIncoming (LNPayment message) { - Preconditions.checkNotNull(channel); - - if (message instanceof LNPaymentAMessage) { - parseAMessage((LNPaymentAMessage) message); - } else if (message instanceof LNPaymentBMessage) { - parseBMessage((LNPaymentBMessage) message); - } else if (message instanceof LNPaymentCMessage) { - parseCMessage((LNPaymentCMessage) message); - } else if (message instanceof LNPaymentDMessage) { - parseDMessage((LNPaymentDMessage) message); - } - } - - @Override - public Channel updateChannel (Channel channel) { - channel.channelSignatures = channelSignatures; - return channel; - } - - @Override - public ChannelUpdate getChannelUpdate () { - Preconditions.checkNotNull(channel); - return channelUpdate; - } - - @Override - public LNPaymentAMessage getAMessage (ChannelUpdate update) { - this.channelUpdate = update; - this.oldStatus = channel.channelStatus; - this.newStatus = oldStatus.getClone(); - this.newStatus.applyUpdate(update); - LNPaymentAMessage message = messageFactory.getMessageA(channel, update); - this.newStatus.revocationHashServer = message.newRevocation; - return message; - } - - @Override - public LNPaymentBMessage getBMessage () { - LNPaymentBMessage message = messageFactory.getMessageB(channel); - this.newStatus.revocationHashServer = message.newRevocation; - return message; - } - - @Override - public LNPaymentCMessage getCMessage () { - LNPaymentCMessage message = messageFactory.getMessageC(channel, getChannelSignatures(), getPaymentSignatures()); - return message; - } - - @Override - public LNPaymentDMessage getDMessage () { - LNPaymentDMessage message = messageFactory.getMessageD(channel); - return message; - } - - private void parseAMessage (LNPaymentAMessage message) { - //We can have a lot of operations here, like adding/removing payments. We need to verify if they are correct. - channelUpdate = message.channelStatus.getCloneReversed(); - oldStatus = channel.channelStatus; - newStatus = checkUpdate(configuration, channel, channelUpdate); - - dbHandler.insertRevocationHash(message.newRevocation); - newStatus.revocationHashClient = message.newRevocation; - } - - private static ChannelStatus checkUpdate (LNConfiguration configuration, Channel channel, ChannelUpdate channelUpdate) { + public void checkUpdate (LNConfiguration configuration, Channel channel, ChannelUpdate channelUpdate) { //We can have a lot of operations here, like adding/removing payments. We need to verify if they are correct. ChannelStatus oldStatus = channel.channelStatus; - ChannelStatus newStatus = oldStatus.getClone(); + ChannelStatus newStatus = oldStatus.copy(); newStatus.applyUpdate(channelUpdate); + newStatus.applyNextRevoHash(); - System.out.println("Old status: " + oldStatus); - System.out.println("New status: " + newStatus); + System.out.println("Receiving update:"); + System.out.println("Old statusSender: " + oldStatus); + System.out.println("New statusSender: " + newStatus); //Check if the update is allowed.. checkPaymentsInNewStatus(oldStatus, channelUpdate); @@ -262,9 +131,6 @@ private static ChannelStatus checkUpdate (LNConfiguration configuration, Channel if (diff > configuration.MAX_DIFF_TIMESTAMPS) { throw new LNPaymentException("timestampOpen is too far off. Calibrate your system clock. Diff: " + diff); } - if (payment.csvDelay < configuration.MIN_REVOCATION_DELAY || payment.csvDelay > configuration.MAX_REVOCATION_DELAY) { - throw new LNPaymentException("Payment-Revocation delay not within allowed boundaries. Is: " + payment.csvDelay); - } diff = payment.timestampRefund - payment.timestampOpen; if (diff > configuration.MAX_OVERLAY_REFUND * configuration.MAX_REFUND_DELAY * OnionObject.MAX_HOPS) { throw new LNPaymentException("Refund timeout is too large. Is: " + diff); @@ -275,27 +141,17 @@ private static ChannelStatus checkUpdate (LNConfiguration configuration, Channel } } if (newStatus.csvDelay < configuration.MIN_REVOCATION_DELAY || newStatus.csvDelay > configuration.MAX_REVOCATION_DELAY) { - throw new LNPaymentException("Change-Revocation delay not within allowed boundaries. Is: " + newStatus.csvDelay); + throw new LNPaymentException("Revocation delay not within allowed boundaries. Is: " + newStatus.csvDelay); } if (newStatus.feePerByte > configuration.MAX_FEE_PER_BYTE || newStatus.feePerByte < configuration.MIN_FEE_PER_BYTE) { throw new LNPaymentException("feePerByte not within allowed boundaries. Is: " + newStatus.feePerByte); } - return newStatus; - } - - private void parseBMessage (LNPaymentBMessage message) { - newStatus.revocationHashClient = message.newRevocation; - dbHandler.insertRevocationHash(message.newRevocation); - } - - private void parseCMessage (LNPaymentCMessage message) { - checkSignatures(channel.keyServer, channel.keyClient, message.getChannelSignatures(), getServerTransaction(), newStatus); - channelSignatures = message.getChannelSignatures(); } @Override public void checkSignatures - (ECKey keyServer, ECKey keyClient, ChannelSignatures channelSignatures, Transaction channelTransaction, ChannelStatus status) { + (ECKey keyServer, ECKey keyClient, ChannelSignatures channelSignatures, Transaction channelTransaction, List paymentTransactions, + ChannelStatus status) { Sha256Hash hash1 = channelTransaction.hashForSignature(0, ScriptTools.getAnchorOutputScript(keyClient, keyServer), Transaction.SigHash.ALL, false); //We only have one anchor for now.. @@ -304,7 +160,6 @@ private void parseCMessage (LNPaymentCMessage message) { } List allPayments = new ArrayList<>(status.paymentList); - List paymentTransactions = getPaymentTransactions(channelTransaction.getHash(), status, keyServer, keyClient); if (allPayments.size() != channelSignatures.paymentSignatures.size()) { throw new LNPaymentException("Size of payment signature list is incorrect"); @@ -323,24 +178,75 @@ private void parseCMessage (LNPaymentCMessage message) { } } - private void parseDMessage (LNPaymentDMessage message) { - for (RevocationHash hash : message.oldRevocationHashes) { - if (!hash.check()) { - throw new LNPaymentException("hash.check() returned false"); - } - } - if (!dbHandler.checkOldRevocationHashes(message.oldRevocationHashes)) { - throw new LNPaymentException("Could not verify all old revocation hashes.."); + @Override + public Transaction getChannelTransaction (Channel channel, SIDE side) { + if (side == SERVER) { + return this.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 0, channel.anchorTxHash), + channel.channelStatus, + channel.keyClient, + channel.keyServer); + } else { + return this.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 0, channel.anchorTxHash), + channel.channelStatus.reverse(), + channel.keyServer, + channel.keyClient); } } - private void saveSignatures () { + @Override + public ChannelSignatures getSignatureObject (Channel channel) { + Transaction channelTx = getChannelTransaction(channel, CLIENT); + List paymentTx = getPaymentTransactions(channel, CLIENT); + return getSignatureObject(channel, channelTx, paymentTx); + } + @Override + public List getPaymentTransactions (Channel channel, SIDE side) { + Transaction channelTx = getChannelTransaction(channel, side); + if (side == SERVER) { + return this.getPaymentTransactions( + channelTx.getHash(), + channel.channelStatus, + channel.keyServer, + channel.keyClient); + } else { + return this.getPaymentTransactions( + channelTx.getHash(), + channel.channelStatus.reverse(), + channel.keyClient, + channel.keyServer); + } + } + + @Override + public void checkSignatures (Channel channel, ChannelSignatures channelSignatures, SIDE side) { + Transaction channelTx = getChannelTransaction(channel, side); + List paymentTx = getPaymentTransactions(channel, side); + + if (side == SERVER) { + this.checkSignatures( + channel.keyServer, + channel.keyClient, + channelSignatures, + channelTx, + paymentTx, + channel.channelStatus); + } else { + this.checkSignatures( + channel.keyClient, + channel.keyServer, + channelSignatures, + channelTx, + paymentTx, + channel.channelStatus.reverse()); + } } private static void checkPaymentsInNewStatus (ChannelStatus oldStatus, ChannelUpdate channelUpdate) { - oldStatus = oldStatus.getClone(); + oldStatus = oldStatus.copy(); channelUpdate = channelUpdate.getClone(); for (PaymentData paymentData : channelUpdate.getRemovedPayments()) { diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessor.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessor.java index 9430f9d7..9e806566 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessor.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessor.java @@ -3,17 +3,5 @@ import network.thunder.core.communication.layer.Processor; public abstract class LNPaymentProcessor extends Processor { - public static int TIMEOUT_NEGOTIATION = 15 * 1000; - - public abstract boolean connectsToNodeId (byte[] nodeId); - - public abstract byte[] connectsTo (); - - public abstract boolean makePayment (PaymentData paymentData); - - public abstract boolean redeemPayment (PaymentSecret paymentData); - - public abstract boolean refundPayment (PaymentData paymentData); - - public abstract void abortCurrentExchange (); + public abstract void ping(); } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.java deleted file mode 100644 index 1e117161..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.java +++ /dev/null @@ -1,463 +0,0 @@ -package network.thunder.core.communication.layer.high.payments; - -import com.google.common.base.Preconditions; -import network.thunder.core.communication.ClientObject; -import network.thunder.core.communication.ServerObject; -import network.thunder.core.communication.layer.ContextFactory; -import network.thunder.core.communication.layer.Message; -import network.thunder.core.communication.layer.MessageExecutor; -import network.thunder.core.communication.layer.high.Channel; -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.messages.*; -import network.thunder.core.communication.layer.high.payments.queue.*; -import network.thunder.core.communication.processor.exceptions.LNPaymentException; -import network.thunder.core.database.DBHandler; -import network.thunder.core.database.objects.PaymentWrapper; -import network.thunder.core.helper.events.LNEventHelper; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -import static network.thunder.core.communication.layer.high.payments.LNPaymentProcessorImpl.Status.*; -import static network.thunder.core.database.objects.PaymentStatus.*; - -//TODO this class is very stateful. Currently the channel can break if a disconnect happens between the D messages. -// Save the state to disk after each message and be able to read it upon connection opening if we want to be able to replay lost messages.. -public class LNPaymentProcessorImpl extends LNPaymentProcessor { - LNPaymentMessageFactory messageFactory; - LNPaymentLogic paymentLogic; - DBHandler dbHandler; - LNPaymentHelper paymentHelper; - LNEventHelper eventHelper; - ClientObject node; - ServerObject serverObject; - - MessageExecutor messageExecutor; - - Channel channel; - - Status status = IDLE; - - BlockingDeque queueList = new LinkedBlockingDeque<>(10000); - List currentQueueElement = new ArrayList<>(); - - ChannelUpdate updateTemp; - boolean aborted = false; - boolean finished = false; - - boolean weStartedExchange = false; - - CountDownLatch countDownLatch = new CountDownLatch(1); - long currentTaskStarted; - - int latestDice = 0; - - boolean connectionClosed = false; - - public LNPaymentProcessorImpl (ContextFactory contextFactory, DBHandler dbHandler, ClientObject node) { - this.messageFactory = contextFactory.getLNPaymentMessageFactory(); - this.paymentLogic = contextFactory.getLNPaymentLogic(); - this.dbHandler = dbHandler; - this.paymentHelper = contextFactory.getPaymentHelper(); - this.eventHelper = contextFactory.getEventHelper(); - this.node = node; - this.serverObject = contextFactory.getServerSettings(); - } - - private void startQueueListener () { - new Thread(() -> { - while (!connectionClosed) { - try { - checkQueue(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }).start(); - } - - private void checkQueue () throws InterruptedException { - if (status == IDLE) { - QueueElement element = queueList.poll(100, TimeUnit.MILLISECONDS); - if (element != null) { - if (status == IDLE) { - - currentQueueElement.add(element); - while (queueList.size() > 0 && currentQueueElement.size() < 50) { - currentQueueElement.add(queueList.poll()); - } - - buildChannelStatus(); - if (!checkForUpdates()) { - return; - } - - sendMessageA(); - restartCountDown(TIMEOUT_NEGOTIATION); - - if (weStartedExchange && status != IDLE) { - //Time is over - we try it again after some random delay? - putQueueElementsBackInQueue(); - setStatus(IDLE); - } - - } else { - queueList.addFirst(element); - } - } - - } else { - //When we get here and Status is not IDLE, the other party started the exchange.. - long timeToFinish = TIMEOUT_NEGOTIATION - (System.currentTimeMillis() - currentTaskStarted); - restartCountDown(timeToFinish); - if (status != IDLE) { - //Other party started the exchange, but we hit the timeout for negotiation. Just abort it on our side.. - setStatus(IDLE); - } - } - } - - private void buildChannelStatus () { - ChannelUpdate update = new ChannelUpdate(); - update.applyConfiguration(serverObject.configuration); - for (QueueElement queueElement : currentQueueElement) { - update = queueElement.produceNewChannelStatus(channel.channelStatus, update, paymentHelper); - } - this.updateTemp = update; - } - - private boolean checkForUpdates () { - int totalChanges = updateTemp.newPayments.size() + updateTemp.redeemedPayments.size() + updateTemp.refundedPayments.size(); - return totalChanges > 0; - } - - private void putQueueElementsBackInQueue () { - for (int i = currentQueueElement.size() - 1; i >= 0; --i) { - queueList.addFirst(currentQueueElement.get(i)); - } - currentQueueElement.clear(); - } - - public void restartCountDown (long sleepTime) throws InterruptedException { - countDownLatch = new CountDownLatch(1); - countDownLatch.await(sleepTime, TimeUnit.MILLISECONDS); - } - - public void abortCountDown () { - countDownLatch.countDown(); - } - - @Override - public boolean makePayment (PaymentData paymentData) { - QueueElement payment = new QueueElementPayment(paymentData); - queueList.add(payment); - return true; - } - - @Override - public boolean redeemPayment (PaymentSecret paymentSecret) { - QueueElementRedeem payment = new QueueElementRedeem(paymentSecret); - queueList.add(payment); - return true; - } - - @Override - public boolean refundPayment (PaymentData paymentData) { - QueueElementRefund payment = new QueueElementRefund(paymentData.secret); - queueList.add(payment); - return true; - } - - public void abortCurrentExchange () { - abortCountDown(); - } - - private void sendMessageA () { - testStatus(IDLE); - weStartedExchange = true; - - LNPaymentAMessage message = paymentLogic.getAMessage(updateTemp); - latestDice = message.dice; - setStatus(SENT_A); - sendMessage(message); - } - - private void sendMessageB () { - testStatus(RECEIVED_A); - - LNPaymentBMessage message = paymentLogic.getBMessage(); - setStatus(SENT_B); - sendMessage(message); - - } - - private void sendMessageC () { - if (weStartedExchange) { - testStatus(RECEIVED_B); - } else { - testStatus(RECEIVED_C); - } - - LNPayment message = paymentLogic.getCMessage(); - setStatus(SENT_C); - sendMessage(message); - } - - private void sendMessageD () { - if (weStartedExchange) { - testStatus(RECEIVED_C); - } else { - testStatus(RECEIVED_D); - } - - LNPayment message = paymentLogic.getDMessage(); - setStatus(SENT_D); - sendMessage(message); - - if (!weStartedExchange) { - successCurrentTask(); - } - - } - - private void readMessageA (LNPaymentAMessage message) { - if (status == SENT_A) { - if (message.dice > latestDice) { - abortCurrentTask(); - } else { - return; - } - } else if (status != IDLE) { - // Ignore new request.. - return; - } - - weStartedExchange = false; - currentTaskStarted = System.currentTimeMillis(); - - paymentLogic.checkMessageIncoming(message); - - setStatus(RECEIVED_A); - sendMessageB(); - } - - private void readMessageB (LNPaymentBMessage message) { - testStatus(SENT_A); - - paymentLogic.checkMessageIncoming(message); - setStatus(RECEIVED_B); - sendMessageC(); - - } - - private void readMessageC (LNPaymentCMessage message) { - if ((weStartedExchange && status != SENT_C) || (!weStartedExchange && status != SENT_B)) { - //TODO ERROR - } else { - paymentLogic.checkMessageIncoming(message); - setStatus(RECEIVED_C); - if (weStartedExchange) { - sendMessageD(); - } else { - sendMessageC(); - } - } - } - - private void readMessageD (LNPaymentDMessage message) { - if ((weStartedExchange && status != SENT_D) || (!weStartedExchange && status != SENT_C)) { - //TODO ERROR - return; - } else { - paymentLogic.checkMessageIncoming(message); - setStatus(RECEIVED_D); - } - if (weStartedExchange) { - successCurrentTask(); - } else { - sendMessageD(); - } - } - - public void testStatus (Status expected) { - if (status != expected) { - throw new LNPaymentException("Expected " + expected + ". Was: " + status); - } - } - - private void abortCurrentTask () { - aborted = true; - putQueueElementsBackInQueue(); - - //TODO - weStartedExchange = false; - currentQueueElement.add(new QueueElementUpdate()); - - abortCountDown(); - } - - private void successCurrentTask () { - this.updateTemp = paymentLogic.getChannelUpdate(); - currentQueueElement.clear(); - updatePaymentsDatabase(); - evaluateUpdates(); - channel = paymentLogic.updateChannel(channel); - dbHandler.updateChannel(channel); - eventHelper.onPaymentExchangeDone(); - - finished = true; - aborted = false; - setStatus(IDLE); - abortCountDown(); - } - - private void updatePaymentsDatabase () { - for (PaymentData payment : updateTemp.newPayments) { - if (weStartedExchange) { - PaymentWrapper wrapper = dbHandler.getPayment(payment.secret); - if (wrapper == null) { - wrapper = new PaymentWrapper(new byte[0], payment); - wrapper.statusReceiver = EMBEDDED; - dbHandler.addPayment(wrapper); - } else { - wrapper.statusReceiver = EMBEDDED; - dbHandler.updatePaymentReceiver(wrapper); - } - } else { - PaymentWrapper wrapper = new PaymentWrapper(node.pubKeyClient.getPubKey(), payment); - dbHandler.addPayment(wrapper); - } - } - for (PaymentData payment : updateTemp.refundedPayments) { - PaymentWrapper wrapper = dbHandler.getPayment(payment.secret); - if (weStartedExchange) { - wrapper.statusReceiver = REFUNDED; - dbHandler.updatePaymentReceiver(wrapper); - } else { - wrapper.statusSender = REFUNDED; - dbHandler.updatePaymentSender(wrapper); - } - } - for (PaymentData payment : updateTemp.redeemedPayments) { - PaymentWrapper wrapper = dbHandler.getPayment(payment.secret); - if (weStartedExchange) { - wrapper.statusReceiver = REDEEMED; - dbHandler.updatePaymentReceiver(wrapper); - } else { - wrapper.statusSender = REDEEMED; - dbHandler.updatePaymentSender(wrapper); - } - eventHelper.onPaymentCompleted(payment); - } - eventHelper.onPaymentExchangeDone(); - - } - - private void evaluateUpdates () { - channel.channelStatus.applyUpdate(updateTemp); - if (!weStartedExchange) { - for (PaymentData newPayment : updateTemp.newPayments) { - paymentHelper.relayPayment(this, newPayment); - } - - for (PaymentData redeemedPayment : updateTemp.redeemedPayments) { - paymentHelper.paymentRedeemed(redeemedPayment.secret); - } - - for (PaymentData refundedPayment : updateTemp.refundedPayments) { - paymentHelper.paymentRefunded(refundedPayment); - } - } - - } - - private synchronized void sendMessage (Message message) { - messageExecutor.sendMessageUpwards(message); - } - - private void setStatus (Status status) { - this.status = status; - } - - @Override - public void onInboundMessage (Message message) { - try { - if (message instanceof LNPaymentAMessage) { - readMessageA((LNPaymentAMessage) message); - } else if (message instanceof LNPaymentBMessage) { - readMessageB((LNPaymentBMessage) message); - } else if (message instanceof LNPaymentCMessage) { - readMessageC((LNPaymentCMessage) message); - } else if (message instanceof LNPaymentDMessage) { - readMessageD((LNPaymentDMessage) message); - } - } catch (LNPaymentException e) { - sendMessage(messageFactory.getFailureMessage(e.getMessage())); - } - } - - @Override - public void onLayerActive (MessageExecutor messageExecutor) { - List openChannel = dbHandler.getOpenChannel(node.pubKeyClient); - Preconditions.checkArgument(openChannel.size() > 0); - - this.channel = openChannel.get(0); - paymentLogic.initialise(channel); - this.messageExecutor = messageExecutor; - this.paymentHelper.addProcessor(this); - startQueueListener(); - } - - @Override - public void onLayerClose () { - this.connectionClosed = true; - this.paymentHelper.removeProcessor(this); - } - - @Override - public boolean consumesInboundMessage (Object object) { - return (object instanceof LNPayment); - } - - @Override - public boolean consumesOutboundMessage (Object object) { - return false; - } - - public ChannelStatus getStatusTemp () { - ChannelStatus statusTemp = channel.channelStatus.getClone(); - return statusTemp; - } - - public Channel getChannel () { - return channel; - } - - @Override - public boolean connectsToNodeId (byte[] nodeId) { - return Arrays.equals(nodeId, node.pubKeyClient.getPubKey()); - } - - @Override - public byte[] connectsTo () { - return node.pubKeyClient.getPubKey(); - } - - public enum Status { - IDLE, - SENT_A, - RECEIVED_A, - SENT_B, - RECEIVED_B, - SENT_C, - RECEIVED_C, - SENT_D, - RECEIVED_D - } - -} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt new file mode 100644 index 00000000..8e18310c --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt @@ -0,0 +1,369 @@ +package network.thunder.core.communication.layer.high.payments + +import network.thunder.core.communication.ClientObject +import network.thunder.core.communication.ServerObject +import network.thunder.core.communication.layer.ContextFactory +import network.thunder.core.communication.layer.DIRECTION.RECEIVED +import network.thunder.core.communication.layer.DIRECTION.SENT +import network.thunder.core.communication.layer.Message +import network.thunder.core.communication.layer.MessageExecutor +import network.thunder.core.communication.layer.high.AckMessageImpl +import network.thunder.core.communication.layer.high.NumberedMessage +import network.thunder.core.communication.layer.high.RevocationHash +import network.thunder.core.communication.layer.high.payments.LNPaymentLogic.SIDE.SERVER +import network.thunder.core.communication.layer.high.payments.messages.* +import network.thunder.core.communication.processor.exceptions.LNPaymentException +import network.thunder.core.database.DBHandler +import network.thunder.core.etc.Constants +import network.thunder.core.etc.Tools +import network.thunder.core.helper.events.LNEventHelper +import java.util.* +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock + +class LNPaymentProcessorImpl( + contextFactory: ContextFactory, + var dbHandler: DBHandler, + var node: ClientObject) : LNPaymentProcessor() { + + val paymentLogic: LNPaymentLogic + val paymentHelper: LNPaymentHelper + val eventHelper: LNEventHelper + val serverObject: ServerObject + + lateinit var messageExecutor: MessageExecutor + + val executingLock = ReentrantLock() + + var connected = true + + init { + this.paymentLogic = contextFactory.lnPaymentLogic + this.paymentHelper = contextFactory.paymentHelper + this.eventHelper = contextFactory.eventHelper + this.serverObject = contextFactory.serverSettings + } + + fun startPingThread() { + Thread(Runnable { + while (connected) { + ping() + Thread.sleep(1000) + } + }).start() + } + + override fun ping() { + if (executingLock.tryLock()) { + try { + val channelList = dbHandler.getChannel(node.nodeKey) + for (channel in channelList) { + //TODO support multiple channels.. + val messages = dbHandler.getMessageList(node.nodeKey, channel.hash, LNPayment::class.java).toList() + val state = PaymentState(channel.copy(), messages) + if (messages.isEmpty() || messages.last().message is LNPaymentCMessage || messages.last().message is AckMessageImpl) { + val newMessage = startNewExchange(state, dbHandler) + if (newMessage != null) { + sendMessage(newMessage) + return + } + } + } + } finally { + executingLock.unlock() + } + } + } + + override fun onInboundMessage(message: Message) { + executingLock.tryLock(1, TimeUnit.MINUTES) + try { + if (message is LNPayment) { + val messages = dbHandler.getMessageList(node.nodeKey, message.channelHash, LNPayment::class.java).toList() + val channel = dbHandler.getChannel(message.channelHash).copy() + val state = PaymentState(channel, messages) + + var (newChannel, newUpdate, oldRevocation, newMessage) = + if (message is LNPaymentAMessage) { + readMessageA(state, message) + } else if (message is LNPaymentBMessage) { + readMessageB(state, message) + } else { + readMessageC(state, message as LNPaymentCMessage) + } + + + var response: NumberedMessage? = null; + if (newMessage != null) { + newMessage.messageNumberToAck = message.messageNumber + response = newMessage + } else { + response = AckMessageImpl(message.messageNumber) + } + + //One atomic transaction here + dbHandler.updateChannelStatus( + node.nodeKey, + message.channelHash, + serverObject.pubKeyServer, + newChannel, + newUpdate?.clone, + oldRevocation, + message, + response) + + + sendMessage(response) + + if (newUpdate != null) { + for (paymentData in newUpdate.newPayments.filter { !it.sending }) { + eventHelper.onPaymentAdded(node.nodeKey, paymentData); + } + for (paymentData in newUpdate.redeemedPayments) { + eventHelper.onPaymentRedeemed(paymentData); + } + for (paymentData in newUpdate.refundedPayments) { + eventHelper.onPaymentRefunded(paymentData); + } + } + + + } else { + throw LNPaymentException("Wrong message? " + message); + } + } finally { + executingLock.unlock() + } + if (executingLock.isLocked) { + executingLock.unlock() + } + ping() + } + + fun startNewExchange(state: PaymentState, + dbHandler: DBHandler): LNPaymentAMessage? { + //We lock all payments that could get included into the next update + //The ones that did not make it into this update will get released automatically upon completion + //This prevents accidentally refunding the payment on the other side while adding it here.. + val paymentsNew = dbHandler.lockPaymentsToBeMade(node.nodeKey) + val paymentsRefund = dbHandler.lockPaymentsToBeRefunded(node.nodeKey) + val paymentsRedeem = dbHandler.lockPaymentsToBeRedeemed(node.nodeKey) + + //TODO once we have multiple channels, we have to separate refunds/redemptions to their respective channels + + if (state.channel.channelStatus.paymentList.size > Constants.MAX_HTLC_PER_CHANNEL + && paymentsRedeem.size == 0 && paymentsRefund.size == 0) { + return null; + } + + if (paymentsNew.size + paymentsRedeem.size + paymentsRefund.size > 0) { + val messageA = createMessageA(state, paymentsNew, paymentsRefund, paymentsRedeem) + dbHandler.saveMessage(node.nodeKey, messageA, SENT) + return messageA; + } else { + return null + } + } + + fun createMessageA(state: PaymentState, + paymentsNew: List, + paymentsRefund: List, + paymentsRedeem: List): LNPaymentAMessage { + + //Let's add all redeems and refunds first, since they will always succeed + val channel = state.channel + val statusTemp = channel.channelStatus.copy() + val update = ChannelUpdate() + update.applyConfiguration(serverObject.configuration) + for (data in paymentsRefund) { + if (statusTemp.paymentList.remove(data)) { + statusTemp.amountClient += data.amount + update.refundedPayments.add(data) + data.sending = false; + } else { + throw RuntimeException("Want to refund a payment not included in current statusSender.."); + } + } + + for (data in paymentsRedeem) { + if (statusTemp.paymentList.remove(data)) { + statusTemp.amountServer += data.amount + update.redeemedPayments.add(data) + data.sending = false; + } else { + throw RuntimeException("Want to redeem a payment not included in current statusSender.."); + } + } + + //Okay, now we can try to squeeze in as many payments as possible + for (data in paymentsNew) { + if (statusTemp.amountServer > data.amount) { + data.timestampOpen = Tools.currentTime() + data.sending = true; + statusTemp.amountServer -= data.amount + update.newPayments.add(data) + } + if (update.newPayments.size + channel.channelStatus.paymentList.size > Constants.MAX_HTLC_PER_CHANNEL) { + break; + } + } + val status = channel.channelStatus.copy() + status.applyUpdate(update) + status.applyNextRevoHash() + + println("Sending update: ") + println("Old statusSender: " + state.channel.channelStatus) + println("New statusSender: " + status) + + channel.channelStatus = status + + val signatures = paymentLogic.getSignatureObject(channel) + + val message = LNPaymentAMessage(update, signatures) + message.channelHash = channel.hash + + return message + } + + + fun readMessageA(state: PaymentState, message: LNPaymentAMessage): PaymentResponse { + val lastMessage = state.messages.lastOrNull() + + if (lastMessage != null && lastMessage.message is LNPaymentAMessage) { + if (lastMessage.direction == SENT) { + if (message.dice < lastMessage.message.dice) { + //Our dice was higher, we just ignore their message.. + return PaymentResponse(null, null, null, null) + } else { + } + } else { + throw LNPaymentException("Wrong message received in readMessageA"); + } + } + + //Only process the message if we completed the last exchange or if concurrent A exchange + if (lastMessage == null || lastMessage.message is LNPaymentAMessage || lastMessage.message is LNPaymentCMessage) { + val channel = state.channel + paymentLogic.checkUpdate(serverObject.configuration, channel, message.channelUpdate.cloneReversed) + + channel.channelStatus.applyUpdate(message.channelUpdate.cloneReversed) + channel.channelStatus.applyNextRevoHash(); + + paymentLogic.checkSignatures(channel, message.getChannelSignatures(), SERVER) + + val signatures = paymentLogic.getSignatureObject(channel) + + val oldRevocation = RevocationHash(channel.shaChainDepth, channel.masterPrivateKeyServer) + val newRevocation = RevocationHash(channel.shaChainDepth + 2, channel.masterPrivateKeyServer) + newRevocation.secret = null //IMPORTANT: remove your secret before sending it + + + val outboundMessage = LNPaymentBMessage(signatures, oldRevocation, newRevocation) + outboundMessage.channelHash = message.channelHash + + return PaymentResponse(null, null, null, outboundMessage) + } else { + throw LNPaymentException("Wrong message received in readMessageA: " + lastMessage); + } + } + + + fun readMessageB(state: PaymentState, message: LNPaymentBMessage): PaymentResponse { + val messageA = state.messages.last { it.direction == SENT && it.message is LNPaymentAMessage } + + if (messageA.message !is LNPaymentAMessage || messageA.direction != SENT) { + throw LNPaymentException("Wrong message received in readMessageB"); + } + val channel = state.channel + var status = channel.channelStatus + + if (!message.oldRevocation.check() || !Arrays.equals(state.channel.channelStatus.revoHashClientCurrent.secretHash, message.oldRevocation.secretHash)) { + throw LNPaymentException("Old revocation check failed. " + + "Old one on file: ${channel.channelStatus.revoHashClientCurrent}. Received: ${message.oldRevocation}") + } + + if (message.newRevocation.index != channel.channelStatus.revoHashClientNext.index + 1) { + throw LNPaymentException("New revocation hash does not append chain") + } + + status.applyUpdate(messageA.message.channelUpdate) + status.applyNextRevoHash() + paymentLogic.checkSignatures(channel, message.getChannelSignatures(), SERVER) + + val oldRevocation = RevocationHash(channel.shaChainDepth, channel.masterPrivateKeyServer) + val newRevocation = RevocationHash(channel.shaChainDepth + 2, channel.masterPrivateKeyServer) + newRevocation.secret = null //IMPORTANT: remove secret before sending it + + + channel.channelSignatures = message.getChannelSignatures() + channel.channelStatus = status + channel.shaChainDepth++ + channel.channelStatus.revoHashClientNext = message.newRevocation + channel.channelStatus.revoHashServerNext = newRevocation //TODO + channel.timestampForceClose = status.paymentList.map { it.timestampRefund }.min() ?: 0 + + + val outboundMessage = LNPaymentCMessage(oldRevocation, newRevocation) + outboundMessage.channelHash = message.channelHash + + return PaymentResponse(channel, messageA.message.channelUpdate, message.oldRevocation, outboundMessage) + } + + fun readMessageC(state: PaymentState, message: LNPaymentCMessage): PaymentResponse { + val messageA = state.messages.last { it.direction == RECEIVED && it.message is LNPaymentAMessage }.message as LNPaymentAMessage + val messageB = state.messages.last { it.direction == SENT && it.message is LNPaymentBMessage } + + val channel = state.channel + + if (messageB.message !is LNPaymentBMessage || messageB.direction != SENT) { + throw LNPaymentException("Wrong message received in readMessageC"); + } + + if (!message.oldRevocation.check() || !Arrays.equals(channel.channelStatus.revoHashClientCurrent.secretHash, message.oldRevocation.secretHash)) { + throw LNPaymentException("Old revocation check failed") + } + + if (message.newRevocation.index != channel.channelStatus.revoHashClientNext.index + 1) { + throw LNPaymentException("New revocation hash does not append chain") + } + + + var status = state.channel.channelStatus.copy() + status.applyUpdate(messageA.channelUpdate.cloneReversed) + status.applyNextRevoHash() + + + channel.channelSignatures = messageA.getChannelSignatures() + channel.channelStatus = status + channel.shaChainDepth++ + channel.channelStatus.revoHashClientNext = message.newRevocation + channel.channelStatus.revoHashServerNext = messageB.message.newRevocation + channel.timestampForceClose = status.paymentList.map { it.timestampRefund }.min() ?: 0 + + return PaymentResponse(channel, messageA.channelUpdate.cloneReversed, message.oldRevocation, null) + } + + fun sendMessage(m: Message) { + messageExecutor.sendMessageUpwards(m) + } + + + override fun onLayerActive(messageExecutor: MessageExecutor) { + this.messageExecutor = messageExecutor + this.paymentHelper.addProcessor(node.nodeKey, this); + startPingThread() + } + + override fun onLayerClose() { + this.paymentHelper.removeProcessor(node.nodeKey) + connected = false + } + + override fun consumesInboundMessage(`object`: Any): Boolean { + return `object` is LNPayment + } + + override fun consumesOutboundMessage(`object`: Any): Boolean { + return false + } +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt new file mode 100644 index 00000000..87f7b889 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt @@ -0,0 +1,13 @@ +package network.thunder.core.communication.layer.high.payments + +import network.thunder.core.communication.layer.high.Channel +import network.thunder.core.communication.layer.high.RevocationHash +import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate +import network.thunder.core.communication.layer.high.payments.messages.LNPayment + +data class PaymentResponse( + val channel: Channel?, + val update: ChannelUpdate?, + val revocationHash: RevocationHash?, + val messages: LNPayment?) + diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentSecret.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentSecret.java index 5c7d3974..c91c78a0 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentSecret.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentSecret.java @@ -60,6 +60,13 @@ public boolean equals (Object o) { } + @Override + public String toString () { + return "PaymentSecret{" + + "hash=" + Tools.bytesToHex(hash).substring(0, 10) + + '}'; + } + @Override public int hashCode () { return Arrays.hashCode(hash); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentState.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentState.kt new file mode 100644 index 00000000..43bf61c3 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentState.kt @@ -0,0 +1,13 @@ +package network.thunder.core.communication.layer.high.payments + +import network.thunder.core.communication.layer.MessageWrapper +import network.thunder.core.communication.layer.high.Channel + +class PaymentState( + private val channelInternal: Channel, + val messages: List +) { + val channel: Channel + get() = this.channelInternal.copy() + +} \ No newline at end of file diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/ChannelUpdate.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/ChannelUpdate.java index c23fe45a..88421ff8 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/ChannelUpdate.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/ChannelUpdate.java @@ -12,7 +12,7 @@ public class ChannelUpdate implements Cloneable { public List redeemedPayments = new ArrayList<>(); public int feePerByte; - public long csvDelay; + public int csvDelay; public void applyConfiguration (LNConfiguration configuration) { this.feePerByte = configuration.DEFAULT_FEE_PER_BYTE; diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java new file mode 100644 index 00000000..ca54eee6 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPayment.java @@ -0,0 +1,44 @@ +package network.thunder.core.communication.layer.high.payments.messages; + +import network.thunder.core.communication.layer.Message; +import network.thunder.core.communication.layer.high.AckMessage; +import network.thunder.core.communication.layer.high.AckableMessage; +import network.thunder.core.communication.layer.high.ChannelSpecificMessage; +import network.thunder.core.communication.layer.high.NumberedMessage; +import org.bitcoinj.core.Sha256Hash; + +public abstract class LNPayment extends AckableMessage implements Message, AckMessage, NumberedMessage, ChannelSpecificMessage { + private byte[] channelHash; + private long messageNumberToAck; + private long messageNumber; + + @Override + public long getMessageNumberToAck () { + return messageNumberToAck; + } + + @Override + public void setMessageNumberToAck (long number) { + this.messageNumberToAck = number; + } + + @Override + public long getMessageNumber () { + return messageNumber; + } + + @Override + public void setMessageNumber (long number) { + this.messageNumber = number; + } + + @Override + public Sha256Hash getChannelHash () { + return Sha256Hash.wrap(channelHash); + } + + @Override + public void setChannelHash (Sha256Hash hash) { + this.channelHash = hash.getBytes(); + } +} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java index 12bbe364..68d92b96 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java @@ -1,33 +1,51 @@ package network.thunder.core.communication.layer.high.payments.messages; import com.google.common.base.Preconditions; -import network.thunder.core.communication.layer.high.RevocationHash; +import network.thunder.core.communication.layer.high.channel.ChannelSignatures; +import org.bitcoinj.crypto.TransactionSignature; +import java.util.ArrayList; +import java.util.List; import java.util.Random; +import java.util.stream.Collectors; -public class LNPaymentAMessage implements LNPayment { +public class LNPaymentAMessage extends LNPayment { public int dice; - public ChannelUpdate channelStatus; - public RevocationHash newRevocation; + public ChannelUpdate channelUpdate; - public LNPaymentAMessage (ChannelUpdate channelUpdate, RevocationHash newRevocation) { + public List channelSignatures = new ArrayList<>(); + public List paymentSignatures = new ArrayList<>(); + + public LNPaymentAMessage (ChannelUpdate channelUpdate, ChannelSignatures channelSignatures) { this.dice = new Random().nextInt(Integer.MAX_VALUE); + this.channelUpdate = channelUpdate; + + this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + } - this.channelStatus = channelUpdate; - this.newRevocation = newRevocation; + public ChannelSignatures getChannelSignatures () { + ChannelSignatures signatures = new ChannelSignatures(); + signatures.paymentSignatures = paymentSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); + signatures.channelSignatures = channelSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); + return signatures; } @Override public void verify () { - Preconditions.checkNotNull(channelStatus); - Preconditions.checkNotNull(newRevocation); + Preconditions.checkNotNull(channelUpdate); + Preconditions.checkNotNull(channelSignatures); + Preconditions.checkNotNull(paymentSignatures); } @Override public String toString () { return "LNPaymentAMessage{dice=" + dice + ", " + - "channelUpdate=" + (channelStatus.newPayments.size() + channelStatus.redeemedPayments.size() + channelStatus.refundedPayments.size()) + + "channelUpdate=" + + (channelUpdate.newPayments.size() + " " + + channelUpdate.redeemedPayments.size() + " " + + channelUpdate.refundedPayments.size()) + '}'; } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java index 4132589c..27c5b11f 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java @@ -2,17 +2,46 @@ import com.google.common.base.Preconditions; import network.thunder.core.communication.layer.high.RevocationHash; +import network.thunder.core.communication.layer.high.channel.ChannelSignatures; +import org.bitcoinj.crypto.TransactionSignature; -public class LNPaymentBMessage implements LNPayment { +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +public class LNPaymentBMessage extends LNPayment { + + public RevocationHash oldRevocation; public RevocationHash newRevocation; - public LNPaymentBMessage (RevocationHash newRevocation) { + public List channelSignatures = new ArrayList<>(); + public List paymentSignatures = new ArrayList<>(); + + public LNPaymentBMessage (ChannelSignatures channelSignatures, RevocationHash oldRevocation, RevocationHash newRevocation) { + this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + + this.oldRevocation = oldRevocation; this.newRevocation = newRevocation; } + public ChannelSignatures getChannelSignatures () { + ChannelSignatures signatures = new ChannelSignatures(); + signatures.paymentSignatures = paymentSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); + signatures.channelSignatures = channelSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); + return signatures; + } + @Override public void verify () { + Preconditions.checkNotNull(oldRevocation); Preconditions.checkNotNull(newRevocation); + Preconditions.checkNotNull(channelSignatures); + Preconditions.checkNotNull(paymentSignatures); + } + + @Override + public String toString () { + return "LNPaymentBMessage{}"; } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java index df741d1c..7eaf551d 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java @@ -1,33 +1,22 @@ package network.thunder.core.communication.layer.high.payments.messages; import com.google.common.base.Preconditions; -import network.thunder.core.communication.layer.high.channel.ChannelSignatures; -import org.bitcoinj.crypto.TransactionSignature; +import network.thunder.core.communication.layer.high.RevocationHash; -import java.util.List; -import java.util.stream.Collectors; +public class LNPaymentCMessage extends LNPayment { -public class LNPaymentCMessage implements LNPayment { - public List channelSignatures; - public List paymentSignatures; + public RevocationHash oldRevocation; + public RevocationHash newRevocation; - public LNPaymentCMessage (ChannelSignatures channelSignatures) { - this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); - this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); - } - - public ChannelSignatures getChannelSignatures () { - ChannelSignatures signatures = new ChannelSignatures(); - signatures.paymentSignatures = paymentSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); - signatures.channelSignatures = channelSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); - - return signatures; + public LNPaymentCMessage (RevocationHash oldRevocation, RevocationHash newRevocation) { + this.oldRevocation = oldRevocation; + this.newRevocation = newRevocation; } @Override public void verify () { - Preconditions.checkNotNull(channelSignatures); - Preconditions.checkNotNull(paymentSignatures); + Preconditions.checkNotNull(oldRevocation); + Preconditions.checkNotNull(newRevocation); } @Override diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentDMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentDMessage.java deleted file mode 100644 index 76b60864..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentDMessage.java +++ /dev/null @@ -1,25 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.messages; - -import com.google.common.base.Preconditions; -import network.thunder.core.communication.layer.high.RevocationHash; - -import java.util.List; - -public class LNPaymentDMessage implements LNPayment { - - public List oldRevocationHashes; - - public LNPaymentDMessage (List oldRevocationHashes) { - this.oldRevocationHashes = oldRevocationHashes; - } - - @Override - public void verify () { - Preconditions.checkNotNull(oldRevocationHashes); - } - - @Override - public String toString () { - return "LNPaymentDMessage{}"; - } -} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactory.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactory.java index 484adc98..a97b6aa2 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactory.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactory.java @@ -1,17 +1,8 @@ package network.thunder.core.communication.layer.high.payments.messages; import network.thunder.core.communication.layer.MessageFactory; -import network.thunder.core.communication.layer.high.Channel; -import org.bitcoinj.crypto.TransactionSignature; -import java.util.List; +public class LNPaymentMessageFactory implements MessageFactory { -public interface LNPaymentMessageFactory extends MessageFactory { - LNPaymentAMessage getMessageA (Channel channel, ChannelUpdate update); - LNPaymentBMessage getMessageB (Channel channel); - - LNPaymentCMessage getMessageC (Channel channel, List channelSignatures, List paymentSignatures); - - LNPaymentDMessage getMessageD (Channel channel); } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactoryImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactoryImpl.java deleted file mode 100644 index bc18902d..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentMessageFactoryImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.messages; - -import network.thunder.core.communication.layer.MesssageFactoryImpl; -import network.thunder.core.communication.layer.high.Channel; -import network.thunder.core.communication.layer.high.channel.ChannelSignatures; -import network.thunder.core.database.DBHandler; -import org.bitcoinj.crypto.TransactionSignature; - -import java.util.List; - -public class LNPaymentMessageFactoryImpl extends MesssageFactoryImpl implements LNPaymentMessageFactory { - - public LNPaymentMessageFactoryImpl (DBHandler dbHandler) { - this.dbHandler = dbHandler; - } - - DBHandler dbHandler; - - @Override - public LNPaymentAMessage getMessageA (Channel channel, ChannelUpdate statusTemp) { - return new LNPaymentAMessage(statusTemp, dbHandler.createRevocationHash(channel)); - } - - @Override - public LNPaymentBMessage getMessageB (Channel channel) { - return new LNPaymentBMessage(dbHandler.createRevocationHash(channel)); - } - - @Override - public LNPaymentCMessage getMessageC (Channel channel, List channelSignatures, List paymentSignatures) { - return new LNPaymentCMessage(new ChannelSignatures(channelSignatures, paymentSignatures)); - } - - @Override - public LNPaymentDMessage getMessageD (Channel channel) { - return new LNPaymentDMessage(dbHandler.getOldRevocationHashes(channel)); - } -} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/OnionObject.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/OnionObject.java index 9a97f27f..f8eeda52 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/OnionObject.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/OnionObject.java @@ -1,6 +1,8 @@ package network.thunder.core.communication.layer.high.payments.messages; -public class OnionObject implements LNPayment { +import org.bitcoinj.core.Sha256Hash; + +public class OnionObject { public final static int MAX_HOPS = 10; public final static int KEY_LENGTH = 33; @@ -15,7 +17,9 @@ public OnionObject (byte[] data) { } @Override - public void verify () { - + public String toString () { + return "OnionObject{" + + "data=" + Sha256Hash.of(data).toString() + + '}'; } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElement.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElement.java deleted file mode 100644 index 140efd71..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElement.java +++ /dev/null @@ -1,15 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.queue; - -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; -import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; - -public abstract class QueueElement { - long addedTimestamp; - - public QueueElement () { - addedTimestamp = System.currentTimeMillis(); - } - - abstract public ChannelUpdate produceNewChannelStatus (ChannelStatus channel, ChannelUpdate channelUpdate, LNPaymentHelper paymentHelper); -} \ No newline at end of file diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementPayment.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementPayment.java deleted file mode 100644 index 74d89190..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementPayment.java +++ /dev/null @@ -1,38 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.queue; - -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; -import network.thunder.core.communication.layer.high.payments.PaymentData; -import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; -import network.thunder.core.etc.Tools; - -public class QueueElementPayment extends QueueElement { - - public QueueElementPayment (PaymentData paymentData) { - this.paymentData = paymentData; - } - - public PaymentData paymentData; - - @Override - public ChannelUpdate produceNewChannelStatus (ChannelStatus channel, ChannelUpdate channelUpdate, LNPaymentHelper paymentHelper) { - ChannelStatus status = channel.getClone(); - ChannelUpdate update = channelUpdate.getClone(); - - this.paymentData.timestampOpen = Tools.currentTime(); - - update.newPayments.add(this.paymentData); - - status.applyUpdate(update); - - //TODO consider fees and dust amounts.. - if (status.amountServer > 0) { - return update; - } else { - //TODO Once we can populate a message back to the sender, we should add some useful information here.. - System.out.println("Payment amount too big - refund.."); - paymentHelper.paymentRefunded(paymentData); - return channelUpdate; - } - } -} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRedeem.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRedeem.java deleted file mode 100644 index 01248a79..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRedeem.java +++ /dev/null @@ -1,40 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.queue; - -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; -import network.thunder.core.communication.layer.high.payments.PaymentData; -import network.thunder.core.communication.layer.high.payments.PaymentSecret; -import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; - -public class QueueElementRedeem extends QueueElement { - - PaymentSecret paymentSecret; - - public QueueElementRedeem (PaymentSecret paymentSecret) { - this.paymentSecret = paymentSecret; - } - - @Override - public ChannelUpdate produceNewChannelStatus (ChannelStatus channel, ChannelUpdate channelUpdate, LNPaymentHelper paymentHelper) { - //TODO Also test yet-to-be-included payments for refund.. - - ChannelStatus status = channel.getClone(); - - PaymentData paymentData = null; - for (PaymentData p : channel.paymentList) { - if (p.secret.equals(paymentSecret)) { - paymentData = p; - } - } - - if (paymentData == null) { - //TODO We want to redeem a payment, but apparently it's no longer within the oldpayments? - System.out.println("QueueElementRedeem: Can't redeem, not part of old payments.."); - return channelUpdate; - } - - paymentData.secret.secret = paymentSecret.secret; - channelUpdate.redeemedPayments.add(paymentData); - return channelUpdate; - } -} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRefund.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRefund.java deleted file mode 100644 index 6116e77c..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementRefund.java +++ /dev/null @@ -1,38 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.queue; - -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; -import network.thunder.core.communication.layer.high.payments.PaymentData; -import network.thunder.core.communication.layer.high.payments.PaymentSecret; -import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; - -public class QueueElementRefund extends QueueElement { - - PaymentSecret paymentSecret; - - public QueueElementRefund (PaymentSecret paymentSecret) { - this.paymentSecret = paymentSecret; - } - - @Override - public ChannelUpdate produceNewChannelStatus (ChannelStatus channel, ChannelUpdate channelUpdate, LNPaymentHelper paymentHelper) { - //TODO Also test yet-to-be-included payments for refund.. - - PaymentData paymentRefund = null; - for (PaymentData payment : channel.paymentList) { - if (payment.secret.equals(this.paymentSecret)) { - paymentRefund = payment; - } - } - if (paymentRefund == null) { - //TODO Payment to be refunded is not in old payments..? - System.out.println("QueueElementRefund could not find old payment.."); - return channelUpdate; - } - - //TODO add some disincentives into refunding - channelUpdate.refundedPayments.add(paymentRefund); - return channelUpdate; - - } -} diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementUpdate.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementUpdate.java deleted file mode 100644 index dea85edb..00000000 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/queue/QueueElementUpdate.java +++ /dev/null @@ -1,13 +0,0 @@ -package network.thunder.core.communication.layer.high.payments.queue; - -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; -import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; - -public class QueueElementUpdate extends QueueElement { - - @Override - public ChannelUpdate produceNewChannelStatus (ChannelStatus channel, ChannelUpdate channelUpdate, LNPaymentHelper paymentHelper) { - return channelUpdate; - } -} diff --git a/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java b/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java index 4907e377..ede314af 100644 --- a/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java +++ b/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java @@ -1,19 +1,37 @@ package network.thunder.core.database; +import network.thunder.core.communication.NodeKey; +import network.thunder.core.communication.layer.DIRECTION; +import network.thunder.core.communication.layer.MessageWrapper; +import network.thunder.core.communication.layer.high.AckableMessage; import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.layer.high.NumberedMessage; import network.thunder.core.communication.layer.high.RevocationHash; +import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.high.payments.PaymentSecret; +import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; import network.thunder.core.communication.layer.middle.broadcasting.types.ChannelStatusObject; import network.thunder.core.communication.layer.middle.broadcasting.types.P2PDataObject; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; import network.thunder.core.database.objects.PaymentWrapper; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; +import org.jetbrains.annotations.NotNull; import java.util.List; public interface DBHandler { + //Messages + List getMessageList (NodeKey nodeKey, Sha256Hash channelHash, Class c); + List getUnackedMessageList (NodeKey nodeKey); + NumberedMessage getMessageResponse (NodeKey nodeKey, long messageIdReceived); + void setMessageAcked (NodeKey nodeKey, long messageId); + void setMessageProcessed (NodeKey nodeKey, NumberedMessage message); + long lastProcessedMessaged (NodeKey nodeKey); + long saveMessage (NodeKey nodeKey, NumberedMessage message, DIRECTION direction); + void linkResponse (NodeKey nodeKey, long messageRequest, long messageResponse); + //Syncing / Gossiping List getSyncDataByFragmentIndex (int fragmentIndex); List getSyncDataIPObjects (); @@ -25,55 +43,40 @@ public interface DBHandler { void syncDatalist (List dataList); //Channels - void insertRevocationHash (RevocationHash hash); - RevocationHash createRevocationHash (Channel channel); - List getOldRevocationHashes (Channel channel); - boolean checkOldRevocationHashes (List revocationHashList); - Channel getChannel (int id); Channel getChannel (Sha256Hash hash); - List getChannel (ECKey nodeKey); - List getOpenChannel (ECKey nodeKey); - - int saveChannel (Channel channel); - - void updateChannel (Channel channel); - + List getChannel (NodeKey nodeKey); + List getOpenChannel (NodeKey nodeKey); List getOpenChannel (); - List getIPObjectsWithActiveChannel (); + void insertChannel (Channel channel); + void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, + Channel channel, ChannelUpdate update, RevocationHash revocationHash, NumberedMessage request, NumberedMessage response); + List getIPObjectsWithActiveChannel (); List getTopology (); //Payments - byte[] getSenderOfPayment (PaymentSecret paymentSecret); + List lockPaymentsToBeRefunded (NodeKey nodeKey); + List lockPaymentsToBeMade (NodeKey nodeKey); + List lockPaymentsToBeRedeemed (NodeKey nodeKey); - byte[] getReceiverOfPayment (PaymentSecret paymentSecret); + void checkPaymentsList (); + void unlockPayments (NodeKey nodeKey, List paymentList); - void addPayment (PaymentWrapper paymentWrapper); + NodeKey getSenderOfPayment (PaymentSecret paymentSecret); + void addPayment (NodeKey firstHop, PaymentData paymentWrapper); void updatePayment (PaymentWrapper paymentWrapper); - - void updatePaymentSender (PaymentWrapper paymentWrapper); - - void updatePaymentReceiver (PaymentWrapper paymentWrapper); - - void updatePaymentAddReceiverAddress (PaymentSecret secret, byte[] receiver); - PaymentWrapper getPayment (PaymentSecret paymentSecret); - - void addPaymentSecret (PaymentSecret secret); - PaymentSecret getPaymentSecret (PaymentSecret secret); + void addPaymentSecret (PaymentSecret secret); //GUI List getAllPayments (); - List getOpenPayments (); - List getRefundedPayments (); - List getRedeemedPayments (); } diff --git a/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java b/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java index 5cd82f57..88d20f2d 100644 --- a/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java +++ b/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java @@ -1,21 +1,37 @@ package network.thunder.core.database; -import network.thunder.core.communication.layer.high.Channel; -import network.thunder.core.communication.layer.high.RevocationHash; +import network.thunder.core.communication.NodeKey; +import network.thunder.core.communication.layer.DIRECTION; +import network.thunder.core.communication.layer.MessageWrapper; +import network.thunder.core.communication.layer.high.*; +import network.thunder.core.communication.layer.high.payments.LNOnionHelper; +import network.thunder.core.communication.layer.high.payments.LNOnionHelperImpl; +import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.high.payments.PaymentSecret; +import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; +import network.thunder.core.communication.layer.high.payments.messages.PeeledOnion; import network.thunder.core.communication.layer.middle.broadcasting.types.ChannelStatusObject; import network.thunder.core.communication.layer.middle.broadcasting.types.P2PDataObject; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyChannelObject; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; +import network.thunder.core.database.objects.PaymentStatus; import network.thunder.core.database.objects.PaymentWrapper; +import network.thunder.core.etc.Constants; import network.thunder.core.etc.Tools; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import static network.thunder.core.communication.layer.DIRECTION.RECEIVED; +import static network.thunder.core.communication.layer.DIRECTION.SENT; +import static network.thunder.core.communication.layer.high.Channel.Phase.OPEN; +import static network.thunder.core.database.objects.PaymentStatus.*; + public class InMemoryDBHandler implements DBHandler { public final List channelList = Collections.synchronizedList(new ArrayList<>()); @@ -28,12 +44,20 @@ public class InMemoryDBHandler implements DBHandler { public final List totalList = Collections.synchronizedList(new ArrayList<>()); private final Collection knownObjects = new HashSet<>(); - public List revocationHashListTheir = Collections.synchronizedList(new ArrayList<>()); - public List revocationHashListOurs = Collections.synchronizedList(new ArrayList<>()); + public Map> revocationHashListTheir = new ConcurrentHashMap<>(); final List payments = Collections.synchronizedList(new ArrayList<>()); + List secrets = new ArrayList<>(); + Map> messageList = new ConcurrentHashMap<>(); + Map messageCountList = new ConcurrentHashMap<>(); + Map> sentMessages = new ConcurrentHashMap<>(); + Map> linkedMessageMap = new ConcurrentHashMap<>(); + Map> unAckedMessageMap = new ConcurrentHashMap<>(); + + LNOnionHelper onionHelper = new LNOnionHelperImpl(); + public InMemoryDBHandler () { for (int i = 0; i < P2PDataObject.NUMBER_OF_FRAGMENTS + 1; i++) { fragmentToListMap.put(i, Collections.synchronizedList(new ArrayList<>())); @@ -184,31 +208,6 @@ public synchronized void syncDatalist (List dataList) { } } - @Override - public void insertRevocationHash (RevocationHash hash) { - revocationHashListTheir.add(hash); - } - - @Override - public RevocationHash createRevocationHash (Channel channel) { - RevocationHash hash = new RevocationHash(1, 1, Tools.getRandomByte(32)); - revocationHashListOurs.add(hash); - return hash; - } - - @Override - public List getOldRevocationHashes (Channel channel) { - List hashList = new ArrayList<>(revocationHashListOurs); - revocationHashListOurs.clear(); - return hashList; - } - - @Override - public boolean checkOldRevocationHashes (List revocationHashList) { - //TODO - return true; - } - @Override public Channel getChannel (int id) { synchronized (channelList) { @@ -234,50 +233,371 @@ public Channel getChannel (Sha256Hash hash) { } @Override - public List getChannel (ECKey nodeKey) { + public List getChannel (NodeKey nodeKey) { synchronized (channelList) { - return channelList.stream().filter(channel1 -> Arrays.equals(channel1.nodeKeyClient, nodeKey.getPubKey())).collect(Collectors.toList()); + return channelList.stream().filter(channel1 -> channel1.nodeKeyClient.equals(nodeKey)).collect(Collectors.toList()); } } @Override - public List getOpenChannel (ECKey nodeKey) { + public List getOpenChannel (NodeKey nodeKey) { synchronized (channelList) { - return getChannel(nodeKey).stream().filter(channel -> channel.isReady).collect(Collectors.toList()); + return getChannel(nodeKey).stream().filter(channel -> channel.phase == OPEN).collect(Collectors.toList()); } } @Override - public int saveChannel (Channel channel) { + public void insertChannel (Channel channel) { synchronized (channelList) { - channel.id = this.channelList.size(); this.channelList.add(channel); - return channel.id; } } @Override - public void updateChannel (Channel channel) { - synchronized (channelList) { - Iterator iterator = channelList.iterator(); - while (iterator.hasNext()) { - Channel c = iterator.next(); - if (c.getHash().equals(channel.getHash())) { - iterator.remove(); - channelList.add(channel); - return; + public void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, + Channel channel, ChannelUpdate update, RevocationHash revocationHash, NumberedMessage request, NumberedMessage response) { + + if (request != null) { + setMessageProcessed(nodeKey, request); + } + if (response != null) { + long messageId = saveMessage(nodeKey, response, SENT); + response.setMessageNumber(messageId); + if (request != null) { + linkResponse(nodeKey, request.getMessageNumber(), response.getMessageNumber()); + } + } + if (revocationHash != null) { + List revocationList = revocationHashListTheir.get(channelHash); + if (revocationList == null) { + revocationList = new ArrayList<>(); + revocationHashListTheir.put(channelHash, revocationList); + } + revocationList.add(revocationHash); + } + if (channel != null) { + synchronized (channelList) { + Iterator iterator = channelList.iterator(); + boolean updated = false; + while (iterator.hasNext()) { + Channel c = iterator.next(); + if (c.getHash().equals(channel.getHash())) { + iterator.remove(); + channelList.add(channel); + updated = true; + break; + } + } + if (!updated) { + throw new RuntimeException("Not able to find channel in list, not updated.."); } } - throw new RuntimeException("Not able to find channel in list, not updated.."); + } + synchronized (payments) { + if (update != null) { + for (PaymentData payment : update.newPayments) { + PaymentWrapper paymentWrapper; + if (!payment.sending) { + paymentWrapper = new PaymentWrapper(); + payments.add(paymentWrapper); + paymentWrapper.paymentData = payment; + paymentWrapper.sender = nodeKey; + paymentWrapper.statusSender = EMBEDDED; + + PeeledOnion onion = null; + onion = onionHelper.loadMessage(keyServer, payment.onionObject); + + if (secrets.contains(payment.secret)) { + paymentWrapper.paymentData.secret = getPaymentSecret(payment.secret); + paymentWrapper.statusSender = TO_BE_REDEEMED; + } else if (onion.isLastHop) { + System.out.println("Don't have the payment secret - refund.."); + paymentWrapper.statusSender = TO_BE_REFUNDED; + } else { + + NodeKey nextHop = onion.nextHop; + if (getOpenChannel(nextHop).size() > 0) { + paymentWrapper.statusReceiver = TO_BE_EMBEDDED; + paymentWrapper.receiver = nextHop; + payment.onionObject = onion.onionObject; + } else { + System.out.println("InMemoryDBHandler.updateChannelStatus to be refunded?"); + paymentWrapper.statusSender = TO_BE_REFUNDED; + } + } + } else { + paymentWrapper = getPayment(payment.secret); + paymentWrapper.statusReceiver = EMBEDDED; + } + } + + for (PaymentData payment : update.redeemedPayments) { + addPaymentSecret(payment.secret); + PaymentWrapper paymentWrapper = getPayment(payment.secret); + if (paymentWrapper == null) { + throw new RuntimeException("Redeemed an unknown payment?"); + } + + paymentWrapper.paymentData.secret = payment.secret; + if (Objects.equals(paymentWrapper.receiver, nodeKey)) { + paymentWrapper.statusReceiver = REDEEMED; + paymentWrapper.statusSender = TO_BE_REDEEMED; + } else if (Objects.equals(paymentWrapper.sender, nodeKey)) { + paymentWrapper.statusSender = REDEEMED; + } else { + throw new RuntimeException("Neither of the parties involved in payment is the one who got here?"); + } + } + + for (PaymentData payment : update.refundedPayments) { + PaymentWrapper paymentWrapper = getPayment(payment.secret); + if (paymentWrapper == null) { + throw new RuntimeException("Refunded an unknown payment?"); + } + + if (Objects.equals(paymentWrapper.receiver, nodeKey)) { + paymentWrapper.statusReceiver = REFUNDED; + paymentWrapper.statusSender = TO_BE_REFUNDED; + } else if (Objects.equals(paymentWrapper.sender, nodeKey)) { + paymentWrapper.statusSender = REFUNDED; + } else { + throw new RuntimeException("Neither of the parties involved in payment is the one who got here?"); + } + } + unlockPayments(nodeKey, payments.stream().map(p -> p.paymentData).collect(Collectors.toList())); + } + } + } + + @Override + public void checkPaymentsList () { + synchronized (payments) { + for (PaymentWrapper payment : payments) { + if (payment.statusSender == EMBEDDED && payment.statusReceiver == TO_BE_EMBEDDED) { + if (Tools.currentTime() - payment.paymentData.timestampOpen > Constants.PAYMENT_TIMEOUT) { + payment.statusReceiver = UNKNOWN; + payment.statusSender = TO_BE_REFUNDED; + } + } + } + } + } + + @Override + public List lockPaymentsToBeRefunded (NodeKey nodeKey) { + synchronized (payments) { + return getPaymentDatas(nodeKey, payments, true, TO_BE_REFUNDED, CURRENTLY_REFUNDING); + } + } + + @Override + public List lockPaymentsToBeMade (NodeKey nodeKey) { + synchronized (payments) { + return getPaymentDatas(nodeKey, payments, false, TO_BE_EMBEDDED, CURRENTLY_EMBEDDING); + } + } + + @Override + public List lockPaymentsToBeRedeemed (NodeKey nodeKey) { + synchronized (payments) { + return getPaymentDatas(nodeKey, payments, true, TO_BE_REDEEMED, CURRENTLY_REDEEMING); + } + } + + @NotNull + private static List getPaymentDatas (NodeKey nodeKey, List payments, boolean sender, PaymentStatus searchFor, PaymentStatus + replaceWith) { + List paymentList = new ArrayList<>(); + for (PaymentWrapper p : payments) { + if (sender) { + if (nodeKey.equals(p.sender)) { + if (p.statusSender == searchFor) { + p.statusSender = replaceWith; + paymentList.add(p.paymentData); + } + } + } else { + if (nodeKey.equals(p.receiver)) { + if (p.statusReceiver == searchFor) { + p.statusReceiver = replaceWith; + paymentList.add(p.paymentData); + } + } + } + } + return paymentList; + } + + @Override + public void unlockPayments (NodeKey nodeKey, List paymentList) { + synchronized (payments) { + + payments.stream() + .filter(p -> Objects.equals(p.receiver, nodeKey)) + .filter(p -> paymentList.contains(p.paymentData)) + .forEach(p -> { + if (p.statusReceiver == CURRENTLY_EMBEDDING) { + p.statusReceiver = TO_BE_EMBEDDED; + } + if (p.statusReceiver == CURRENTLY_REDEEMING) { + p.statusReceiver = TO_BE_REDEEMED; + } + if (p.statusReceiver == CURRENTLY_REFUNDING) { + p.statusReceiver = TO_BE_REFUNDED; + } + }); + + payments.stream() + .filter(p -> Objects.equals(p.sender, nodeKey)) + .filter(p -> paymentList.contains(p.paymentData)) + .forEach(p -> { + if (p.statusSender == CURRENTLY_EMBEDDING) { + p.statusSender = TO_BE_EMBEDDED; + } + if (p.statusSender == CURRENTLY_REDEEMING) { + p.statusSender = TO_BE_REDEEMED; + } + if (p.statusSender == CURRENTLY_REFUNDING) { + p.statusSender = TO_BE_REFUNDED; + } + }); } } @Override public List getOpenChannel () { synchronized (channelList) { - return channelList.stream().filter(channel -> channel.isReady).collect(Collectors.toList()); + return channelList.stream().filter(channel -> channel.phase == OPEN).collect(Collectors.toList()); + } + } + + @Override + public List getMessageList (NodeKey nodeKey, Sha256Hash channelHash, Class c) { + List wrapper = messageList.get(nodeKey); + if (wrapper == null) { + return Collections.emptyList(); + } + List total = + wrapper.stream() + .filter(message -> c.isAssignableFrom(message.getMessage().getClass())) + .collect(Collectors.toList()); + + return total; + } + + @Override + public void setMessageProcessed (NodeKey nodeKey, NumberedMessage message) { + saveMessage(nodeKey, message, RECEIVED); + } + + @Override + public long lastProcessedMessaged (NodeKey nodeKey) { + List messageWrappers = messageList.get(nodeKey); + if (messageWrappers != null) { + Optional o = messageWrappers.stream() + .filter(w -> w.getDirection() == RECEIVED) + .filter(w -> w.getMessage() instanceof NumberedMessage) + .map(MessageWrapper::getMessage) + .map(m -> (NumberedMessage) m) + .map(NumberedMessage::getMessageNumber) + .max(Long::compareTo); + + if (o.isPresent()) { + return o.get(); + } } + return 0; + } + + @Override + public synchronized long saveMessage (NodeKey nodeKey, NumberedMessage message, DIRECTION direction) { + if (direction == SENT) { + Long i = messageCountList.get(nodeKey); + if (i == null) { + i = 1L; + } else { + i++; + } + messageCountList.put(nodeKey, i); + Map messageMap = sentMessages.get(nodeKey); + if (messageMap == null) { + messageMap = new ConcurrentHashMap<>(); + sentMessages.put(nodeKey, messageMap); + } + message.setMessageNumber(i); + messageMap.put(i, message); + + if (message instanceof AckableMessage) { + List unAckedMessageList = unAckedMessageMap.get(nodeKey); + if (unAckedMessageList == null) { + unAckedMessageList = new ArrayList<>(); + unAckedMessageMap.put(nodeKey, unAckedMessageList); + } + unAckedMessageList.add((AckableMessage) message); + } + + List messageList = this.messageList.get(nodeKey); + if (messageList == null) { + messageList = new ArrayList<>(); + this.messageList.put(nodeKey, messageList); + } + messageList.add(new MessageWrapper(message, Tools.currentTime(), direction)); + return i; + } else { + List messageList = this.messageList.get(nodeKey); + if (messageList == null) { + messageList = new ArrayList<>(); + this.messageList.put(nodeKey, messageList); + } + messageList.add(new MessageWrapper(message, Tools.currentTime(), direction)); + return 0; + } + } + + @Override + public void linkResponse (NodeKey nodeKey, long messageRequest, long messageResponse) { + Map linkedMessages = linkedMessageMap.get(nodeKey); + if (linkedMessages == null) { + linkedMessages = new ConcurrentHashMap<>(); + linkedMessageMap.put(nodeKey, linkedMessages); + } + linkedMessages.put(messageRequest, messageResponse); + } + + @Override + public List getUnackedMessageList (NodeKey nodeKey) { + List unAckedMessageList = unAckedMessageMap.get(nodeKey); + if (unAckedMessageList == null || unAckedMessageList.size() == 0) { + return Collections.emptyList(); + } else { + return unAckedMessageList; + } + } + + @Override + public NumberedMessage getMessageResponse (NodeKey nodeKey, long messageIdReceived) { + Map linkMap = linkedMessageMap.get(nodeKey); + if (linkMap != null) { + Long response = linkMap.get(messageIdReceived); + if (response != null) { + Map messageMap = sentMessages.get(nodeKey); + if (messageMap != null) { + NumberedMessage m = messageMap.get(response); + if (m != null) { + return m; + } + } + throw new RuntimeException("We wanted to resend an old message, but can't find it.."); + } + } + return null; + } + + @Override + public void setMessageAcked (NodeKey nodeKey, long messageId) { + List unackedMessages = unAckedMessageMap.get(nodeKey); + unackedMessages.removeIf(message -> message.getMessageNumber() == messageId); } @Override @@ -332,12 +652,14 @@ public List getRedeemedPayments () { } @Override - public void addPayment (PaymentWrapper paymentWrapper) { - if (payments.contains(paymentWrapper)) { - return; -// throw new RuntimeException("Double payment added?"); + public void addPayment (NodeKey nodeKey, PaymentData paymentData) { + PaymentWrapper paymentWrapper = new PaymentWrapper(); + paymentWrapper.receiver = nodeKey; + paymentWrapper.statusReceiver = TO_BE_EMBEDDED; + paymentWrapper.paymentData = paymentData; + synchronized (payments) { + payments.add(paymentWrapper); } - payments.add(paymentWrapper); } @Override @@ -350,56 +672,32 @@ public void updatePayment (PaymentWrapper paymentWrapper) { p.sender = paymentWrapper.sender; p.statusReceiver = paymentWrapper.statusReceiver; p.statusSender = paymentWrapper.statusSender; + return; } } + payments.add(paymentWrapper); } } @Override - public void updatePaymentSender (PaymentWrapper paymentWrapper) { - synchronized (payments) { - for (PaymentWrapper p : payments) { - if (p.equals(paymentWrapper)) { - p.paymentData = paymentWrapper.paymentData; - p.statusSender = paymentWrapper.statusSender; - } - } - } - } - - @Override - public void updatePaymentReceiver (PaymentWrapper paymentWrapper) { + public PaymentWrapper getPayment (PaymentSecret paymentSecret) { synchronized (payments) { - for (PaymentWrapper p : payments) { - if (p.equals(paymentWrapper)) { - p.paymentData = paymentWrapper.paymentData; - p.statusReceiver = paymentWrapper.statusReceiver; - } - } - } - } + Optional paymentWrapper = payments.stream() + .filter(p -> !isPaymentComplete(p)) + .filter(p -> Objects.equals(p.paymentData.secret, paymentSecret)) + .findAny(); - @Override - public void updatePaymentAddReceiverAddress (PaymentSecret secret, byte[] receiver) { - synchronized (payments) { - for (PaymentWrapper p : payments) { - if (p.paymentData.secret.equals(secret)) { - p.receiver = receiver; - } + if (paymentWrapper.isPresent()) { + return paymentWrapper.get(); + } else { + return null; } } } - @Override - public PaymentWrapper getPayment (PaymentSecret paymentSecret) { - synchronized (payments) { - for (PaymentWrapper payment : payments) { - if (payment.paymentData.secret.equals(paymentSecret)) { - return payment; - } - } - return null; - } + private boolean isPaymentComplete (PaymentWrapper paymentWrapper) { + return paymentWrapper.statusSender == REDEEMED && paymentWrapper.statusReceiver == REDEEMED || + paymentWrapper.statusSender == REFUNDED && paymentWrapper.statusReceiver == REFUNDED; } @Override @@ -421,22 +719,14 @@ public PaymentSecret getPaymentSecret (PaymentSecret secret) { } @Override - public byte[] getSenderOfPayment (PaymentSecret paymentSecret) { - for (PaymentWrapper payment : payments) { - if (payment.paymentData.secret.equals(paymentSecret)) { - return payment.sender; - } - } - return null; - } - - @Override - public byte[] getReceiverOfPayment (PaymentSecret paymentSecret) { - for (PaymentWrapper payment : payments) { - if (payment.paymentData.secret.equals(paymentSecret)) { - return payment.receiver; + public NodeKey getSenderOfPayment (PaymentSecret paymentSecret) { + synchronized (payments) { + for (PaymentWrapper payment : payments) { + if (payment.paymentData.secret.equals(paymentSecret)) { + return payment.sender; + } } + return null; } - return null; } } diff --git a/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentStatus.java b/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentStatus.java index 9690fab0..b0fc9ee7 100644 --- a/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentStatus.java +++ b/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentStatus.java @@ -3,9 +3,15 @@ public enum PaymentStatus { UNKNOWN(0), SAVED(1), - EMBEDDED(2), - REFUNDED(3), - REDEEMED(4); + TO_BE_EMBEDDED(21), + CURRENTLY_EMBEDDING(22), + EMBEDDED(23), + TO_BE_REFUNDED(31), + CURRENTLY_REFUNDING(32), + REFUNDED(33), + TO_BE_REDEEMED(41), + CURRENTLY_REDEEMING(42), + REDEEMED(43); int status; diff --git a/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentWrapper.java b/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentWrapper.java index 7be0d8c2..4db62a77 100644 --- a/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentWrapper.java +++ b/thunder-core/src/main/java/network/thunder/core/database/objects/PaymentWrapper.java @@ -1,5 +1,6 @@ package network.thunder.core.database.objects; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.payments.PaymentData; import static network.thunder.core.database.objects.PaymentStatus.EMBEDDED; @@ -12,10 +13,14 @@ public class PaymentWrapper { public PaymentStatus statusSender; public PaymentStatus statusReceiver; - public byte[] sender; - public byte[] receiver; + public NodeKey sender; + public NodeKey receiver; - public PaymentWrapper (byte[] sender, PaymentData paymentData) { + public PaymentWrapper () { + + } + + public PaymentWrapper (NodeKey sender, PaymentData paymentData) { this.sender = sender; this.paymentData = paymentData; @@ -23,6 +28,17 @@ public PaymentWrapper (byte[] sender, PaymentData paymentData) { this.statusSender = UNKNOWN; } + @Override + public String toString () { + return "PaymentWrapper{" + + "paymentData=" + paymentData + + ", statusSender=" + statusSender + + ", statusReceiver=" + statusReceiver + + ", sender=" + sender + + ", receiver=" + receiver + + '}'; + } + @Override public boolean equals (Object o) { if (this == o) { diff --git a/thunder-core/src/main/java/network/thunder/core/etc/Constants.java b/thunder-core/src/main/java/network/thunder/core/etc/Constants.java index 17feed5a..ea98e192 100644 --- a/thunder-core/src/main/java/network/thunder/core/etc/Constants.java +++ b/thunder-core/src/main/java/network/thunder/core/etc/Constants.java @@ -28,6 +28,9 @@ public static NetworkParameters getNetwork () { } public static boolean USE_MOCK_BLOCKCHAIN = true; + public static int PAYMENT_TIMEOUT = 5; + public static long MESSAGE_RESEND_TIME = 15000; + public static int MAX_HTLC_PER_CHANNEL = 250; public static final int STANDARD_PORT = 2204; public static final int ESCAPE_REVOCATION_TIME = 24 * 60 * 60 / 10 * 60; //In blocks.. diff --git a/thunder-core/src/main/java/network/thunder/core/helper/HashDerivation.java b/thunder-core/src/main/java/network/thunder/core/helper/HashDerivation.java deleted file mode 100644 index 12aa5164..00000000 --- a/thunder-core/src/main/java/network/thunder/core/helper/HashDerivation.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * ThunderNetwork - Server Client Architecture to send Off-Chain Bitcoin Payments - * Copyright (C) 2015 Mats Jerratsch - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -package network.thunder.core.helper; - -import network.thunder.core.communication.layer.high.RevocationHash; -import network.thunder.core.etc.Tools; - -import java.nio.ByteBuffer; -import java.util.Arrays; - -/* - * For deterministic derivation of revocation hashes, use the following scheme - * Chose a master seed - for example the hash of private key. - * For obtaining a new childseed, hash the parentseed twice using SHA256. - * For obtaining a sibling of a seed, concat the hash with the 4-byte integer of the sibling id. - * The preimage will be the sibling seed hashed with RIPEMD160(SHA256) and the public hash will be the preimage hashed with RIPEMD160(SHA256) again. - * - * This ensures that one can give away the preimage without revealing all other siblings. - * Revealing the childseed will reveal all further children and all siblings. - */ -public class HashDerivation { - - /** - * The other party has breached the contract and submitted an old channel transaction. - * - * @param seed The latest masterseed we received from the other party - * @param target The hash we are looking for - * @param maxChildTries The maximum depth we will search to before giving up.. - * @param maxSiblingTries The amount of siblings calculating for each depth - * @return The preimage hashing to the desired hash. - */ - public static RevocationHash bruteForceHash (byte[] seed, byte[] target, int maxChildTries, int maxSiblingTries) { - - for (int i = 0; i < maxChildTries; i++) { - - for (int j = 0; j < maxSiblingTries; j++) { - RevocationHash test = HashDerivation.calculateRevocationHash(seed, 0, j); - if (Arrays.equals(test.getSecretHash(), target)) { - return new RevocationHash(i, j, test.getSecret(), test.getSecretHash()); - } - } - - seed = Tools.hashSecret(seed); - - } - return null; - } - - /** - * Calculate a revocation hash for a new channel state. - * - * @param seed A masterseed for this calculation - * @param depth The depth of the new RevocationHash - * @param childNumber The n. sibling at the specified depth - */ - public static RevocationHash calculateRevocationHash (byte[] seed, int depth, int childNumber) { - - byte[] childseed = seed; - for (int i = 0; i < depth; i++) { - childseed = Tools.hashSecret(childseed); - } - - if (childNumber == 0) { - return new RevocationHash(depth, childNumber, childseed, null); - } - - byte[] childseedWithNumber = new byte[24]; - System.arraycopy(childseed, 0, childseedWithNumber, 0, 20); - - ByteBuffer buffer = ByteBuffer.allocate(4); - buffer.putInt(childNumber); - buffer.flip(); - - System.arraycopy(buffer.array(), 0, childseedWithNumber, 20, 4); - - byte[] secret = Tools.hashSecret(childseedWithNumber); - byte[] secretHash = Tools.hashSecret(secret); - - return new RevocationHash(depth, childNumber, secret, secretHash); - - } - -} diff --git a/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelper.java b/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelper.java index f07915e6..6d27ac4e 100644 --- a/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelper.java +++ b/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelper.java @@ -1,6 +1,7 @@ package network.thunder.core.helper.events; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; @@ -27,7 +28,9 @@ public interface LNEventHelper { void onPaymentRefunded (PaymentData payment); - void onPaymentCompleted (PaymentData payment); + void onPaymentRedeemed (PaymentData payment); + + void onPaymentAdded (NodeKey nodeKey, PaymentData payment); void onPaymentExchangeDone (); diff --git a/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelperImpl.java b/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelperImpl.java index 129492f4..5e41dc04 100644 --- a/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelperImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventHelperImpl.java @@ -1,6 +1,7 @@ package network.thunder.core.helper.events; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; @@ -79,12 +80,19 @@ public void onPaymentExchangeDone () { } @Override - public void onPaymentCompleted (PaymentData payment) { + public void onPaymentRedeemed (PaymentData payment) { for (LNEventListener listener : listeners) { listener.onPaymentCompleted(payment); } } + @Override + public void onPaymentAdded (NodeKey nodeKey, PaymentData payment) { + for (LNEventListener listener : listeners) { + listener.onPaymentAdded(nodeKey, payment); + } + } + @Override public void onP2PDataReceived () { for (LNEventListener listener : listeners) { diff --git a/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventListener.java b/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventListener.java index a51d48d7..41186abf 100644 --- a/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventListener.java +++ b/thunder-core/src/main/java/network/thunder/core/helper/events/LNEventListener.java @@ -1,6 +1,7 @@ package network.thunder.core.helper.events; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; @@ -39,6 +40,10 @@ public void onPaymentCompleted (PaymentData payment) { onEvent(); } + public void onPaymentAdded (NodeKey nodeKey, PaymentData payment) { + onEvent(); + } + public void onPaymentExchangeDone () { onEvent(); } From e61ee830dbb53bcf4ff625436485356d609a0d31 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:21:38 +0100 Subject: [PATCH 03/21] Send the next revocation hash when establishing a payment channel --- .../layer/high/ChannelStatus.java | 57 +++++++++++-------- .../establish/LNEstablishProcessorImpl.java | 51 +++++++++++------ .../messages/LNEstablishAMessage.java | 23 +++++--- .../LNEstablishMessageFactoryImpl.java | 3 +- 4 files changed, 84 insertions(+), 50 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java index acc47a8f..3c502645 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java @@ -8,46 +8,50 @@ import java.util.Iterator; import java.util.List; -public class ChannelStatus implements Cloneable { +public class ChannelStatus { public long amountClient; public long amountServer; public List paymentList = new ArrayList<>(); public int feePerByte; - public long csvDelay; + public int csvDelay; - public RevocationHash revocationHashClient; - public RevocationHash revocationHashServer; + public RevocationHash revoHashClientCurrent; + public RevocationHash revoHashServerCurrent; + public RevocationHash revoHashClientNext; + public RevocationHash revoHashServerNext; public Address addressClient; public Address addressServer; - @Override - protected Object clone () throws CloneNotSupportedException { - return super.clone(); - } - - public ChannelStatus getClone () { - try { - ChannelStatus status = (ChannelStatus) this.clone(); - status.paymentList = clonePaymentList(this.paymentList); - return status; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } + public ChannelStatus copy () { + ChannelStatus status = new ChannelStatus(); + status.amountClient = this.amountClient; + status.amountServer = this.amountServer; + status.feePerByte = this.feePerByte; + status.csvDelay = this.csvDelay; + status.addressClient = this.addressClient; + status.addressServer = this.addressServer; + status.revoHashClientCurrent = revoHashClientCurrent.copy(); + status.revoHashClientNext = revoHashClientNext.copy(); + status.revoHashServerCurrent = revoHashServerCurrent.copy(); + status.revoHashServerNext = revoHashServerNext.copy(); + + status.paymentList = clonePaymentList(this.paymentList); + return status; } - public ChannelStatus getCloneReversed () { - ChannelStatus status = getClone(); + public ChannelStatus reverse () { + ChannelStatus status = copy(); long tempAmount = status.amountServer; status.amountServer = status.amountClient; status.amountClient = tempAmount; - RevocationHash tempRevocationHash = status.revocationHashServer; - status.revocationHashServer = status.revocationHashClient; - status.revocationHashClient = tempRevocationHash; + RevocationHash tempRevocationHash = status.revoHashServerCurrent; + status.revoHashServerCurrent = status.revoHashClientCurrent; + status.revoHashClientCurrent = tempRevocationHash; Address tempAddress = status.addressServer; status.addressServer = status.addressClient; @@ -100,6 +104,11 @@ public void applyUpdate (ChannelUpdate update) { this.csvDelay = update.csvDelay; } + public void applyNextRevoHash () { + this.revoHashClientCurrent = this.revoHashClientNext; + this.revoHashServerCurrent = this.revoHashServerNext; + } + private List reverseSending (List paymentDataList) { for (PaymentData payment : paymentDataList) { payment.sending = !payment.sending; @@ -125,8 +134,8 @@ public String toString () { return "ChannelStatus{" + "amountClient=" + amountClient + ", amountServer=" + amountServer + - ", addressServer=" + addressServer + - ", addressClient=" + addressClient + + ", revoServer=" + revoHashServerCurrent + + ", revoClient=" + revoHashClientCurrent + ", paymentList=" + paymentList.size() + '}'; } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/LNEstablishProcessorImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/LNEstablishProcessorImpl.java index 86a99c3a..a47f7d93 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/LNEstablishProcessorImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/LNEstablishProcessorImpl.java @@ -1,12 +1,12 @@ package network.thunder.core.communication.layer.high.channel.establish; import network.thunder.core.communication.ClientObject; -import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.ServerObject; import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.Message; import network.thunder.core.communication.layer.MessageExecutor; import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.layer.high.RevocationHash; import network.thunder.core.communication.layer.high.channel.ChannelManager; import network.thunder.core.communication.layer.high.channel.ChannelOpener; import network.thunder.core.communication.layer.high.channel.establish.messages.*; @@ -20,17 +20,21 @@ import network.thunder.core.etc.Tools; import network.thunder.core.helper.blockchain.BlockchainHelper; import network.thunder.core.helper.callback.ChannelOpenListener; -import network.thunder.core.helper.callback.results.ChannelCreatedResult; +import network.thunder.core.helper.callback.results.FailureResult; import network.thunder.core.helper.callback.results.SuccessResult; import network.thunder.core.helper.events.LNEventHelper; import network.thunder.core.helper.wallet.WalletHelper; import org.bitcoinj.core.Transaction; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static network.thunder.core.communication.layer.high.Channel.Phase.ESTABLISH_WAITING_FOR_BLOCKCHAIN_CONFIRMATION; +import static network.thunder.core.communication.layer.high.Channel.Phase.OPEN; + /* * Processor handling the creation of a channel. * @@ -41,7 +45,7 @@ * - The problem with it is that we need a way to determine the value of the UTXOs * - Have a anchor_complete message * - Check after X confirmation for an anchor_complete, close and free our anchor again - * - Add various changes to the channel status / updates to the database + * - Add various changes to the channel statusSender / updates to the database * - Reload a half-done channel from database if the connection breaks down * * Currently we are exchanging 8 messages in total, @@ -169,8 +173,7 @@ public void onInboundMessage (Message message) { consumeMessage(message); } catch (Exception e) { e.printStackTrace(); - messageExecutor.sendMessageUpwards(messageFactory.getFailureMessage(e.getMessage())); - node.resultCallback.execute(new ChannelCreatedResult(establishProgress.channel)); + node.resultCallback.execute(new FailureResult(e.getMessage())); throw e; } } @@ -187,7 +190,7 @@ public boolean consumesOutboundMessage (Object object) { @Override public void onLayerActive (MessageExecutor messageExecutor) { - setNode(new NodeKey(node.pubKeyClient)); + setNode(node.nodeKey); this.messageExecutor = messageExecutor; channelManager.addChannelOpener(getNode(), this); sendNextLayerActiveIfOpenChannelExists(); @@ -200,7 +203,7 @@ public void onLayerClose () { } private void sendNextLayerActiveIfOpenChannelExists () { - List openChannel = dbHandler.getOpenChannel(node.pubKeyClient); + List openChannel = dbHandler.getOpenChannel(node.nodeKey); if (openChannel.size() > 0) { startScheduledBroadcasting(); messageExecutor.sendNextLayerActive(); @@ -229,7 +232,7 @@ private void consumeMessage (Message message) { @Override public void openChannel (Channel channel, ChannelOpenListener callback) { //Only support one channel per connection for now.. - if(dbHandler.getOpenChannel(node.pubKeyClient).size() > 0) { + if (dbHandler.getOpenChannel(node.nodeKey).size() > 0) { System.out.println("LNEstablishProcessorImpl.openChannel - already connected!"); callback.onSuccess.execute(); return; @@ -260,7 +263,7 @@ private void processMessageA (LNEstablishAMessage message) { if (testProgressReceivingMessageAmount(0)) { //TODO test for validity of establish settings //TODO test if inputs are paying adequate fees and are paying from SegWit outputs - this.establishProgress.channel.nodeKeyClient = node.pubKeyClient.getPubKey(); + this.establishProgress.channel.nodeKeyClient = node.nodeKey; message.saveToChannel(establishProgress.channel); establishProgress.messages.add(message); if (establishProgress.weStartedExchange) { @@ -294,6 +297,7 @@ private void processMessageB (LNEstablishBMessage message) { establishProgress.channel.keyClient, establishProgress.channel.channelSignatures, channelTransaction, + Collections.emptyList(), establishProgress.channel.channelStatus ); @@ -339,8 +343,8 @@ private boolean testProgressReceivingMessageAmount (int amount) { private void onChannelEstablished () { establishProgress.channel.anchorTxHash = establishProgress.channel.anchorTx.getHash(); - establishProgress.channel.isReady = true; - dbHandler.saveChannel(establishProgress.channel); + establishProgress.channel.phase = ESTABLISH_WAITING_FOR_BLOCKCHAIN_CONFIRMATION; + dbHandler.insertChannel(establishProgress.channel); blockchainHelper.broadcastTransaction(establishProgress.channel.anchorTx); // channelManager.onExchangeDone(channel, this::onEnoughConfirmations); @@ -352,7 +356,18 @@ private void onChannelEstablished () { } private void onEnoughConfirmations () { - dbHandler.updateChannel(establishProgress.channel); + establishProgress.channel.phase = OPEN; + + dbHandler.updateChannelStatus( + getNode(), + establishProgress.channel.getHash(), + serverObject.pubKeyServer, + establishProgress.channel, + null, + null, + null, + null); + sendNextLayerActiveIfOpenChannelExists(); eventHelper.onChannelOpened(establishProgress.channel); @@ -362,8 +377,10 @@ private void onEnoughConfirmations () { } private void sendEstablishMessageA () { + establishProgress.channel.masterPrivateKeyServer = Tools.getRandomByte(20); establishProgress.channel.getAnchorTransactionServer(walletHelper); - establishProgress.channel.channelStatus.revocationHashServer = dbHandler.createRevocationHash(establishProgress.channel); + establishProgress.channel.channelStatus.revoHashServerCurrent = new RevocationHash(0, establishProgress.channel.masterPrivateKeyServer); + establishProgress.channel.channelStatus.revoHashServerNext = new RevocationHash(1, establishProgress.channel.masterPrivateKeyServer); establishProgress.channel.channelStatus.addressServer = walletHelper.fetchAddress(); LNEstablish message = messageFactory.getEstablishMessageA(establishProgress.channel); establishProgress.messages.add(message); @@ -374,12 +391,12 @@ private void sendEstablishMessageB () { Transaction channelTransaction = paymentLogic.getChannelTransaction( establishProgress.channel.anchorTx.getOutput(0).getOutPointFor(), - establishProgress.channel.channelStatus.getCloneReversed(), + establishProgress.channel.channelStatus.reverse(), establishProgress.channel.keyServer, establishProgress.channel.keyClient ); - establishProgress.channel.channelSignatures = paymentLogic.getSignatureObject(establishProgress.channel, channelTransaction); + establishProgress.channel.channelSignatures = paymentLogic.getSignatureObject(establishProgress.channel, channelTransaction, Collections.emptyList()); LNEstablish message = messageFactory.getEstablishMessageB(establishProgress.channel.channelSignatures.channelSignatures.get(0)); establishProgress.messages.add(message); @@ -403,7 +420,7 @@ private void broadcastChannelObject () { Channel channel = establishProgress.channel; PubkeyChannelObject channelObject = new PubkeyChannelObject(); channelObject.pubkeyA = serverObject.pubKeyServer.getPubKey(); - channelObject.pubkeyB = node.pubKeyClient.getPubKey(); + channelObject.pubkeyB = node.nodeKey.getPubKey(); channelObject.pubkeyA1 = channel.keyServer.getPubKey(); channelObject.pubkeyB1 = channel.keyClient.getPubKey(); channelObject.timestamp = Tools.currentTime(); @@ -412,7 +429,7 @@ private void broadcastChannelObject () { //TODO fill in some usable data into ChannelStatusObject ChannelStatusObject statusObject = new ChannelStatusObject(); statusObject.pubkeyA = serverObject.pubKeyServer.getPubKey(); - statusObject.pubkeyB = node.pubKeyClient.getPubKey(); + statusObject.pubkeyB = node.nodeKey.getPubKey(); statusObject.timestamp = Tools.currentTime(); broadcastHelper.broadcastNewObject(channelObject); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java index bef6cc78..229ee5cc 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java @@ -21,20 +21,25 @@ public class LNEstablishAMessage implements LNEstablish { //Data about the first commitment to be able to refund if necessary public RevocationHash revocationHash; + public RevocationHash revocationHashNext; public int feePerByte; - public long csvDelay; + public int csvDelay; - public LNEstablishAMessage (ECKey channelKeyServer, Transaction anchor, RevocationHash revocationHash, long clientAmount, long - serverAmount, int minConfirmationAnchor, Address address, int feePerByte, long csvDelay) { + public LNEstablishAMessage (ECKey channelKeyServer, Transaction anchor, RevocationHash revocationHash, RevocationHash revocationHashNext, long clientAmount, + long serverAmount, int minConfirmationAnchor, Address address, int feePerByte, int csvDelay) { this.channelKeyServer = channelKeyServer.getPubKey(); this.minConfirmationAnchor = minConfirmationAnchor; this.anchorTransaction = anchor.bitcoinSerialize(); this.revocationHash = revocationHash; + this.revocationHashNext = revocationHashNext; this.amountClient = clientAmount; this.amountServer = serverAmount; this.addressBytes = address.getHash160(); this.feePerByte = feePerByte; this.csvDelay = csvDelay; + + this.revocationHash.secret = null; + this.revocationHashNext.secret = null; } @Override @@ -42,14 +47,14 @@ public Channel saveToChannel (Channel channel) { try { Transaction newAnchorTx = new Transaction(Constants.getNetwork(), anchorTransaction); - if(channel.anchorTx != null) { - if(Tools.checkIfExistingInOutPutsAreEqual(channel.anchorTx, newAnchorTx)) { + if (channel.anchorTx != null) { + if (Tools.checkIfExistingInOutPutsAreEqual(channel.anchorTx, newAnchorTx)) { throw new LNEstablishException("Our in/outputs of the anchor has been changed.."); } } //Don't allow changing the values for now, symmetric messages are easy to implement though - if(channel.channelStatus.amountServer == 0 || channel.channelStatus.amountClient == 0) { + if (channel.channelStatus.amountServer == 0 || channel.channelStatus.amountClient == 0) { channel.channelStatus.amountClient = amountClient; channel.channelStatus.amountServer = amountServer; } @@ -57,12 +62,14 @@ public Channel saveToChannel (Channel channel) { channel.keyClient = ECKey.fromPublicOnly(channelKeyServer); channel.anchorTx = newAnchorTx; channel.channelStatus.addressClient = new Address(Constants.getNetwork(), addressBytes); - channel.channelStatus.revocationHashClient = revocationHash; + channel.channelStatus.revoHashClientCurrent = revocationHash; + channel.channelStatus.revoHashClientNext = revocationHashNext; channel.channelStatus.csvDelay = csvDelay; channel.channelStatus.feePerByte = feePerByte; channel.minConfirmationAnchor = minConfirmationAnchor; + channel.shaChainDepth = 0; return channel; - } catch(Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishMessageFactoryImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishMessageFactoryImpl.java index 385517e2..b831ae9c 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishMessageFactoryImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishMessageFactoryImpl.java @@ -12,7 +12,8 @@ public LNEstablishAMessage getEstablishMessageA (Channel channel) { LNEstablishAMessage message = new LNEstablishAMessage( channel.keyServer, channel.anchorTx, - channel.channelStatus.revocationHashServer, + channel.channelStatus.revoHashServerCurrent, + channel.channelStatus.revoHashServerNext, channel.channelStatus.amountClient, channel.channelStatus.amountServer, 0, //TODO add some reasonable minConfirmations From f6de0a361a3d622c8ee9d15d85fcc857ec445b77 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:22:24 +0100 Subject: [PATCH 04/21] Remove blur on the GUI to improve fluent flow --- .../src/main/java/wallettemplate/utils/GuiUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thunder-clientgui/src/main/java/wallettemplate/utils/GuiUtils.java b/thunder-clientgui/src/main/java/wallettemplate/utils/GuiUtils.java index 7db51b8f..499fcd84 100644 --- a/thunder-clientgui/src/main/java/wallettemplate/utils/GuiUtils.java +++ b/thunder-clientgui/src/main/java/wallettemplate/utils/GuiUtils.java @@ -119,7 +119,7 @@ public static void blurOut (Node node) { GaussianBlur blur = new GaussianBlur(0.0); node.setEffect(blur); Timeline timeline = new Timeline(); - KeyValue kv = new KeyValue(blur.radiusProperty(), 10.0); + KeyValue kv = new KeyValue(blur.radiusProperty(), 0.0); KeyFrame kf = new KeyFrame(Duration.millis(UI_ANIMATION_TIME_MSEC), kv); timeline.getKeyFrames().add(kf); timeline.play(); From 37788ea7775fd37699a106a1e21ee23aab9fbeac Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:23:27 +0100 Subject: [PATCH 05/21] Updating documentation in ScriptTools --- .../thunder/core/helper/ScriptTools.java | 90 +++++++++++++------ 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/helper/ScriptTools.java b/thunder-core/src/main/java/network/thunder/core/helper/ScriptTools.java index be940d11..d33c78a9 100644 --- a/thunder-core/src/main/java/network/thunder/core/helper/ScriptTools.java +++ b/thunder-core/src/main/java/network/thunder/core/helper/ScriptTools.java @@ -3,14 +3,12 @@ import com.google.common.primitives.UnsignedBytes; import com.google.gson.internal.LinkedTreeMap; import network.thunder.core.communication.layer.high.RevocationHash; -import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.high.payments.PaymentSecret; import network.thunder.core.etc.Tools; import org.bitcoinj.core.ECKey; import org.bitcoinj.script.Script; import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptChunk; -import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -65,9 +63,50 @@ public class ScriptTools { public static final byte[] COMMIT_INPUT_SCRIPT = Tools.hexStringToByteArray("00 FF FF FF"); - public static final byte[] CHANNEL_TX_OUTPUT_REVOCATION = Tools.hexStringToByteArray("A9 FF 87 63 FF 67 FF B3 75 FF 68 AC"); - public static final byte[] CHANNEL_TX_OUTPUT_PLAIN = Tools.hexStringToByteArray("FF AC"); + /* + * Script for the balance of the channel going to the owner of the channel tx. + * The counterparty can claim these funds using a revocation hash. + * + * Arguments: + * (1) revocation hash + * (2) public key of party that can claim funds using revocation + * (3) revocation delay + * (4) public key of owner of that output + */ + public static final byte[] CHANNEL_TX_OUTPUT_REVOCATION = scriptStringToByte( + "HASH160 FF EQUAL " + + "IF" + + "FF" + + "ELSE" + + "FF CSV DROP FF" + + "ENDIF" + + "CHECKSIG"); + + public static Script getChannelTxOutputRevocation (RevocationHash revocationHash, ECKey revocableKey, ECKey counterpartyKey, int revocationDelay) { + return produceScript( + CHANNEL_TX_OUTPUT_REVOCATION, + revocationHash.secretHash, + counterpartyKey.getPubKey(), + integerToByteArray(revocationDelay), + revocableKey.getPubKey()); + } + /* + * Script for the sending part of a payment + * The payment goes to a 2-of-2 if + * the CLTV timeout is over + * and to the counterparty if + * it can provide the revocation hash + * it can provide the payment secret + * + * Arguments: + * (1) revocation hash + * (2) payment hash + * (3) public key of receiving party + * (4) absolute payment timeout + * (5) public key of receiving party + * (6) public key of sending party + */ public static final byte[] CHANNEL_TX_OUTPUT_PAYMENT_SENDING_ONE = scriptStringToByte( "HASH160 DUP FF EQUAL SWAP FF EQUAL ADD " + @@ -77,13 +116,19 @@ public class ScriptTools { "FF CLTV DROP OP_2 FF FF OP_2 CHECKMULTISIG " + "ENDIF"); - public static Script getChannelTxOutputPaymentSending (ECKey keyServer, ECKey keyClient, + public static Script getChannelTxOutputPaymentSending (ECKey revocableKey, ECKey counterpartyKey, RevocationHash revocationHash, PaymentSecret secret, int refundTimeout) { - return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_SENDING_ONE, revocationHash.getSecretHash(), secret.hash, - keyClient.getPubKey(), integerToByteArray(refundTimeout), keyServer.getPubKey(), keyClient.getPubKey()); + return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_SENDING_ONE, revocationHash.secretHash, secret.hash, + counterpartyKey.getPubKey(), integerToByteArray(refundTimeout), revocableKey.getPubKey(), counterpartyKey.getPubKey()); } //TODO not very efficient yet with 3 times client pubkey... + //Script for the receiving part of a payment + //The payment goes to a 2-of-2 if + // the receiver can provide the payment secret + //and to the counterparty if + // it can provide the revocation hash + // the CLTV timeout is over public static final byte[] CHANNEL_TX_OUTPUT_PAYMENT_RECEIVING_ONE = scriptStringToByte( "HASH160 FF EQUAL " + @@ -98,10 +143,10 @@ public static Script getChannelTxOutputPaymentSending (ECKey keyServer, ECKey ke "ENDIF " + "ENDIF "); - public static Script getChannelTxOutputPaymentReceiving (ECKey keyServer, ECKey keyClient, + public static Script getChannelTxOutputPaymentReceiving (ECKey revocableKey, ECKey counterpartyKey, RevocationHash revocationHash, PaymentSecret secret, int refundTimeout) { - return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_RECEIVING_ONE, revocationHash.getSecretHash(), keyClient.getPubKey(), - secret.hash, keyServer.getPubKey(), keyClient.getPubKey(), integerToByteArray(refundTimeout), keyClient.getPubKey()); + return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_RECEIVING_ONE, revocationHash.secretHash, counterpartyKey.getPubKey(), + secret.hash, revocableKey.getPubKey(), counterpartyKey.getPubKey(), integerToByteArray(refundTimeout), counterpartyKey.getPubKey()); } public static final byte[] CHANNEL_TX_INPUT_PAYMENT_SENDING_TIMEOUT = scriptStringToByte("00 FF FF"); @@ -121,7 +166,7 @@ public static Script getChannelTxOutputPaymentReceiving (ECKey keyServer, ECKey "ENDIF"); public static Script getPaymentTxOutput (ECKey keyReceiver, ECKey keyRevocation, RevocationHash revocationHash, int revocationTimeout) { - return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_TWO, revocationHash.getSecretHash(), keyRevocation.getPubKey(), + return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_TWO, revocationHash.secretHash, keyRevocation.getPubKey(), integerToByteArray(revocationTimeout), keyReceiver.getPubKey()); } @@ -163,24 +208,11 @@ public static Script getCommitInputScript (byte[] signatureClient, byte[] signat } } - public static Script getChannelTxOutputRevocation (@NotNull RevocationHash revocationHash, @NotNull ECKey keyServer, ECKey keyClient, int revocationDelay) { - return produceScript(CHANNEL_TX_OUTPUT_REVOCATION, revocationHash.getSecretHash(), keyClient.getPubKey(), integerToByteArray(revocationDelay), - keyServer.getPubKey()); - } - - public static Script getChannelTxOutputPlain (ECKey keyClient) { - return produceScript(CHANNEL_TX_OUTPUT_PLAIN, keyClient.getPubKey()); - } - - public static Script getChannelTxOutputSending (RevocationHash revocationHash, PaymentData paymentData, ECKey keyServer, ECKey keyClient) { - return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_SENDING, paymentData.secret.hash, revocationHash.getSecretHash(), keyClient.getPubKey(), - integerToByteArray(paymentData.timestampRefund), integerToByteArray(paymentData.csvDelay), keyServer.getPubKey()); - } - - public static Script getChannelTxOutputReceiving (RevocationHash revocationHash, PaymentData paymentData, ECKey keyServer, ECKey keyClient) { - return produceScript(CHANNEL_TX_OUTPUT_PAYMENT_RECEIVING, paymentData.secret.hash, integerToByteArray(paymentData.csvDelay), - keyClient.getPubKey(), revocationHash.getSecretHash(), integerToByteArray(paymentData.timestampRefund), keyServer.getPubKey()); - } + //Script for the encumbered change payment of the commitment + //The payment goes to the counterparty if + // it can provide the revocation hash + //and to the owner of the commitment tx if + // the revocation delay passed public static Script produceScript (byte[] template, byte[]... parameters) { try { From 297a49d072e4b2ef2da6f46889b288b87a5643b8 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:26:54 +0100 Subject: [PATCH 06/21] Close connection when catching an exception --- .../core/communication/layer/ProcessorHandler.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/ProcessorHandler.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/ProcessorHandler.java index 9437a79b..5021597c 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/ProcessorHandler.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/ProcessorHandler.java @@ -3,7 +3,6 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; -import network.thunder.core.communication.processor.exceptions.LNException; public class ProcessorHandler extends ChannelDuplexHandler { @@ -49,14 +48,9 @@ public void channelRead (ChannelHandlerContext ctx, Object msg) throws Exception messageExecutor.sendMessageDownwards(message); } } - } catch (LNException e1) { - if (e1.shouldDisconnect()) { - ctx.close(); - } - throw e1; - } catch (Exception e) { - e.printStackTrace(); - throw e; + } catch (Exception e1) { + e1.printStackTrace(); + ctx.close(); } } @@ -79,7 +73,7 @@ public void write (ChannelHandlerContext ctx, Object msg, ChannelPromise promise } catch (Exception e) { e.printStackTrace(); - throw e; + ctx.close(); } } From 7f4487d93c505fa2c290d2fc680e7c96b5ab729e Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:28:23 +0100 Subject: [PATCH 07/21] Added AckProcessor to resend messages that did not get acknowledged within time --- .../layer/PipelineInitialiser.java | 6 +- .../layer/low/ack/AckProcessor.kt | 101 ++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/low/ack/AckProcessor.kt diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/PipelineInitialiser.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/PipelineInitialiser.java index 16e6a3d3..819db1fb 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/PipelineInitialiser.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/PipelineInitialiser.java @@ -6,9 +6,9 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.layer.low.ping.PingHandler; import network.thunder.core.communication.layer.low.serialisation.ByteToMessageObjectHandler; import network.thunder.core.communication.layer.low.serialisation.MessageObjectToByteHandler; -import network.thunder.core.communication.layer.low.ping.PingHandler; import network.thunder.core.communication.layer.low.serialisation.MessageSerializer; public class PipelineInitialiser extends ChannelInitializer { @@ -32,7 +32,7 @@ protected void initChannel (SocketChannel ch) throws Exception { if (serverMode) { node = new ClientObject(); node.isServer = true; - node.pubKeyClient = null; + node.nodeKey = null; } // ch.pipeline().addLast(new DumpHexHandler()); @@ -74,6 +74,8 @@ protected void initChannel (SocketChannel ch) throws Exception { Processor lnCloseProcessor = contextFactory.getLNCloseProcessor(node); ch.pipeline().addLast(new ProcessorHandler(lnCloseProcessor, "LNClose")); + ch.pipeline().addLast(new ProcessorHandler(contextFactory.getAckProcessor(node), "Ack")); + Processor lnPaymentProcessor = contextFactory.getLNPaymentProcessor(node); ch.pipeline().addLast(new ProcessorHandler(lnPaymentProcessor, "LNPayment")); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/low/ack/AckProcessor.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/low/ack/AckProcessor.kt new file mode 100644 index 00000000..debb7b07 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/low/ack/AckProcessor.kt @@ -0,0 +1,101 @@ +package network.thunder.core.communication.layer.low.ack + +import network.thunder.core.communication.ClientObject +import network.thunder.core.communication.ServerObject +import network.thunder.core.communication.layer.* +import network.thunder.core.communication.layer.high.AckMessage +import network.thunder.core.communication.layer.high.AckMessageImpl +import network.thunder.core.communication.layer.high.AckableMessage +import network.thunder.core.communication.layer.high.NumberedMessage +import network.thunder.core.database.DBHandler +import network.thunder.core.etc.Constants +import network.thunder.core.helper.events.LNEventHelper + +abstract class AckProcessor : Processor() { + +} + +class AckProcessorImpl( + contextFactory: ContextFactory, + var dbHandler: DBHandler, + var node: ClientObject) : AckProcessor() { + + lateinit var messageExecutor: MessageExecutor + + val eventHelper: LNEventHelper + val serverObject: ServerObject + + var connectionOpen = true + + + init { + this.eventHelper = contextFactory.eventHelper + this.serverObject = contextFactory.serverSettings + } + + override fun onLayerActive(messageExecutor: MessageExecutor?) { + this.messageExecutor = messageExecutor!! + startResendThread() + this.messageExecutor.sendNextLayerActive() + } + + override fun onInboundMessage(message: Message?) { + if (message is AckMessageImpl) { + //AckMessageImpl is the message type of this processor, it should not get passed further down the pipeline, we process it directly + dbHandler.setMessageAcked(node.nodeKey, message.messageNumberToAck) + dbHandler.saveMessage(node.nodeKey, message, DIRECTION.RECEIVED) + return + } + if (message is NumberedMessage) { + val lastMessage = dbHandler.lastProcessedMessaged(node.nodeKey) + + if (lastMessage >= message.messageNumber) { + val response = dbHandler.getMessageResponse(node.nodeKey, message.messageNumber) + if (response != null) { + //We have responded to that message already, send out the old response that we have on file + messageExecutor.sendMessageUpwards(response) + return + } else { + throw RuntimeException("Should have a response on file..") + } + } else if (message.messageNumber != 1L && message.messageNumber > lastMessage + 1) { + //We don't care about messages that are not immediate successor of the last message we processed + //In the future we could keep them and play them in when its their turn. + return + } + } + if (message is AckMessage && message.messageNumberToAck > 0) { + dbHandler.setMessageAcked(node.nodeKey, message.messageNumberToAck) + } + messageExecutor.sendMessageDownwards(message) + } + + + override fun onLayerClose() { + connectionOpen = false + } + + override fun consumesOutboundMessage(`object`: Any?): Boolean { + return false + } + + fun startResendThread() { + Thread(Runnable({ + while (connectionOpen) { + resendMessage() + Thread.sleep(Constants.MESSAGE_RESEND_TIME) + } + })).start() + } + + fun resendMessage() { + val messages = dbHandler.getUnackedMessageList(node.nodeKey) + if (messages != null) { + for (m in messages) { + if (m is AckableMessage) { + messageExecutor.sendMessageUpwards(m) + } + } + } + } +} \ No newline at end of file From 2494191d3d97a2e2f56c338aecc605aa72d1267b Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:40:56 +0100 Subject: [PATCH 08/21] Use NodeKey instead of ECKey in Channel class NodeKey is a wrapper around ECKey that does allow comparing the same ECKey. ECKey.equals is generally not reliable, as it also compares the createDate of the key. --- .../java/wallettemplate/MainController.java | 2 +- .../java/network/thunder/core/MainNode.java | 2 +- .../core/communication/ClientObject.java | 8 +- .../communication/ConnectionManagerImpl.java | 5 +- .../core/communication/ServerObject.java | 2 +- .../layer/ConnectionProcessor.java | 3 +- .../communication/layer/ContextFactory.java | 6 +- .../layer/ContextFactoryImpl.java | 21 +- .../communication/layer/high/Channel.java | 228 +++--------------- .../high/channel/ChannelManagerImpl.java | 4 +- .../layer/high/channel/ChannelSignatures.java | 7 + .../channel/close/LNCloseProcessorImpl.java | 31 ++- .../high/payments/messages/PeeledOnion.java | 6 +- .../AuthenticationProcessorImpl.java | 30 +-- .../network/thunder/core/etc/SeedNodes.java | 2 +- 15 files changed, 101 insertions(+), 256 deletions(-) diff --git a/thunder-clientgui/src/main/java/wallettemplate/MainController.java b/thunder-clientgui/src/main/java/wallettemplate/MainController.java index 485b7ba4..b891c598 100644 --- a/thunder-clientgui/src/main/java/wallettemplate/MainController.java +++ b/thunder-clientgui/src/main/java/wallettemplate/MainController.java @@ -259,7 +259,7 @@ public PaymentWrapper fromString (String string) { @Override public String toString (Channel channel) { return Coin.valueOf(channel.channelStatus.amountServer).toFriendlyString() + - " with " + model.getHostname(Main.dbHandler.getIPObjects(), channel.nodeKeyClient); + " with " + model.getHostname(Main.dbHandler.getIPObjects(), channel.nodeKeyClient.getPubKey()); } @Override diff --git a/thunder-core/src/main/java/network/thunder/core/MainNode.java b/thunder-core/src/main/java/network/thunder/core/MainNode.java index 9aeb195c..4780b050 100644 --- a/thunder-core/src/main/java/network/thunder/core/MainNode.java +++ b/thunder-core/src/main/java/network/thunder/core/MainNode.java @@ -96,7 +96,7 @@ public static void main (String[] args) throws Exception { buildChannelListener.await(30, TimeUnit.SECONDS); List openChannel = dbHandler.getOpenChannel(); for (Channel channel : openChannel) { - configuration.nodesToBuildChannelWith.add(Tools.bytesToHex(channel.nodeKeyClient)); + configuration.nodesToBuildChannelWith.add(channel.nodeKeyClient.getPubKeyHex()); } writeConfigurationFile(configuration); } else { diff --git a/thunder-core/src/main/java/network/thunder/core/communication/ClientObject.java b/thunder-core/src/main/java/network/thunder/core/communication/ClientObject.java index 71fdea3c..a11f94e4 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/ClientObject.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/ClientObject.java @@ -15,8 +15,7 @@ public class ClientObject { public boolean isServer; - //TODO transition to NodeKey.class - public ECKey pubKeyClient; + public NodeKey nodeKey; //Encryption keys public ECKey ephemeralKeyServer; @@ -50,7 +49,7 @@ public ClientObject (ClientObject node) { init(); this.port = node.port; this.host = node.host; - this.pubKeyClient = node.pubKeyClient; + this.nodeKey = node.nodeKey; this.isServer = node.isServer; this.intent = node.intent; this.name = node.name; @@ -64,12 +63,11 @@ public ClientObject (ServerObject node) { init(); this.host = node.hostServer; this.port = node.portServer; - this.pubKeyClient = node.pubKeyServer; + this.nodeKey = new NodeKey(node.pubKeyServer); this.isServer = false; } public void init () { - pubKeyClient = new ECKey(); ephemeralKeyServer = new ECKey(); } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/ConnectionManagerImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/ConnectionManagerImpl.java index f0d566b2..83176212 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/ConnectionManagerImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/ConnectionManagerImpl.java @@ -18,7 +18,6 @@ import network.thunder.core.helper.callback.SyncListener; import network.thunder.core.helper.callback.results.*; import network.thunder.core.helper.events.LNEventHelper; -import org.bitcoinj.core.ECKey; import org.eclipse.jetty.util.BlockingArrayQueue; import java.util.ArrayList; @@ -245,7 +244,7 @@ public void startBuildingRandomChannel (ResultCommand callback) { System.out.println("BUILD CHANNEL WITH: " + randomNode); ClientObject node = ipObjectToNode(randomNode, OPEN_CHANNEL); - channelManager.openChannel(new NodeKey(node.pubKeyClient), new ChannelOpenListener()); + channelManager.openChannel(node.nodeKey, new ChannelOpenListener()); alreadyTried.add(randomNode); @@ -313,7 +312,7 @@ private ClientObject ipObjectToNode (PubkeyIPObject ipObject, ConnectionIntent i ClientObject node = new ClientObject(); node.isServer = false; node.intent = intent; - node.pubKeyClient = ECKey.fromPublicOnly(ipObject.pubkey); + node.nodeKey = new NodeKey(ipObject.pubkey); node.host = ipObject.hostname; node.port = ipObject.port; return node; diff --git a/thunder-core/src/main/java/network/thunder/core/communication/ServerObject.java b/thunder-core/src/main/java/network/thunder/core/communication/ServerObject.java index fc597ff1..da721e79 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/ServerObject.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/ServerObject.java @@ -21,7 +21,7 @@ public ServerObject (ClientObject node) { init(); this.portServer = node.port; this.hostServer = node.host; - this.pubKeyServer = node.pubKeyClient; + this.pubKeyServer = node.nodeKey.nodeKey; } public ServerObject () { diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/ConnectionProcessor.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/ConnectionProcessor.java index 55d770cc..b4206b3c 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/ConnectionProcessor.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/ConnectionProcessor.java @@ -3,7 +3,6 @@ import network.thunder.core.communication.ClientObject; import network.thunder.core.communication.Connection; import network.thunder.core.communication.ConnectionRegistry; -import network.thunder.core.communication.NodeKey; import network.thunder.core.helper.callback.Command; public class ConnectionProcessor extends AuthenticatedProcessor implements Connection { @@ -21,7 +20,7 @@ public ConnectionProcessor (ContextFactory contextFactory, ClientObject clientOb @Override public void onLayerActive (MessageExecutor messageExecutor) { messageExecutor.sendNextLayerActive(); - setNode(new NodeKey(node.pubKeyClient)); + setNode(node.nodeKey); this.messageExecutor = messageExecutor; connectionRegistry.onConnected(getNode(), this); node.onConnectionComplete.stream().forEach(Command::execute); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactory.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactory.java index d9976258..0160ea22 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactory.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactory.java @@ -10,7 +10,7 @@ import network.thunder.core.communication.layer.high.channel.establish.LNEstablishProcessor; import network.thunder.core.communication.layer.high.channel.establish.messages.LNEstablishMessageFactory; import network.thunder.core.communication.layer.high.payments.*; -import network.thunder.core.communication.layer.high.payments.messages.LNPaymentMessageFactory; +import network.thunder.core.communication.layer.low.ack.AckProcessor; import network.thunder.core.communication.layer.low.authentication.AuthenticationProcessor; import network.thunder.core.communication.layer.low.authentication.messages.AuthenticationMessageFactory; import network.thunder.core.communication.layer.low.encryption.EncryptionProcessor; @@ -45,6 +45,8 @@ public interface ContextFactory { GossipProcessor getGossipProcessor (ClientObject node); + AckProcessor getAckProcessor (ClientObject node); + LNEstablishProcessor getLNEstablishProcessor (ClientObject node); LNPaymentProcessor getLNPaymentProcessor (ClientObject node); @@ -81,8 +83,6 @@ public interface ContextFactory { LNEstablishMessageFactory getLNEstablishMessageFactory (); - LNPaymentMessageFactory getLNPaymentMessageFactory (); - LNCloseMessageFactory getLNCloseMessageFactory (); LNCloseProcessor getLNCloseProcessor (ClientObject node); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactoryImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactoryImpl.java index 35bb0504..4b31f340 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactoryImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/ContextFactoryImpl.java @@ -11,8 +11,9 @@ import network.thunder.core.communication.layer.high.channel.establish.messages.LNEstablishMessageFactory; import network.thunder.core.communication.layer.high.channel.establish.messages.LNEstablishMessageFactoryImpl; import network.thunder.core.communication.layer.high.payments.*; -import network.thunder.core.communication.layer.high.payments.messages.LNPaymentMessageFactory; -import network.thunder.core.communication.layer.high.payments.messages.LNPaymentMessageFactoryImpl; + +import network.thunder.core.communication.layer.low.ack.AckProcessor; +import network.thunder.core.communication.layer.low.ack.AckProcessorImpl; import network.thunder.core.communication.layer.low.authentication.AuthenticationProcessor; import network.thunder.core.communication.layer.low.authentication.AuthenticationProcessorImpl; import network.thunder.core.communication.layer.low.authentication.messages.AuthenticationMessageFactory; @@ -57,6 +58,7 @@ public class ContextFactoryImpl implements ContextFactory { WalletHelper walletHelper; LNPaymentHelper paymentHelper; + LNPaymentLogic paymentLogic; LNOnionHelper onionHelper = new LNOnionHelperImpl(); @@ -74,6 +76,7 @@ public ContextFactoryImpl (ServerObject node, DBHandler dbHandler, Wallet wallet this.eventHelper = eventHelper; this.walletHelper = new WalletHelperImpl(wallet); + this.paymentLogic = new LNPaymentLogicImpl(); GossipSubjectImpl gossipSubject = new GossipSubjectImpl(dbHandler, eventHelper); this.gossipSubject = gossipSubject; this.broadcastHelper = gossipSubject; @@ -82,7 +85,7 @@ public ContextFactoryImpl (ServerObject node, DBHandler dbHandler, Wallet wallet this.paymentHelper = new LNPaymentHelperImpl(this, dbHandler); - if(Constants.USE_MOCK_BLOCKCHAIN) { + if (Constants.USE_MOCK_BLOCKCHAIN) { this.blockchainHelper = new MockBlockchainHelper(wallet); } else { this.blockchainHelper = new BlockchainHelperImpl(wallet); @@ -130,6 +133,11 @@ public GossipProcessor getGossipProcessor (ClientObject node) { return new GossipProcessorImpl(this, dbHandler, node); } + @Override + public AckProcessor getAckProcessor (ClientObject node) { + return new AckProcessorImpl(this, dbHandler, node); + } + @Override public LNEstablishProcessor getLNEstablishProcessor (ClientObject node) { return new LNEstablishProcessorImpl(this, dbHandler, node); @@ -182,7 +190,7 @@ public GossipSubject getGossipSubject () { @Override public LNPaymentLogic getLNPaymentLogic () { - return new LNPaymentLogicImpl(getLNPaymentMessageFactory(), dbHandler); + return paymentLogic; } @Override @@ -220,11 +228,6 @@ public LNEstablishMessageFactory getLNEstablishMessageFactory () { return new LNEstablishMessageFactoryImpl(); } - @Override - public LNPaymentMessageFactory getLNPaymentMessageFactory () { - return new LNPaymentMessageFactoryImpl(dbHandler); - } - @Override public LNCloseMessageFactory getLNCloseMessageFactory () { return new LNCloseMessageFactoryImpl(); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java index eafaa75a..165822cd 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java @@ -19,6 +19,7 @@ package network.thunder.core.communication.layer.high; import com.google.common.primitives.UnsignedBytes; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.channel.ChannelSignatures; import network.thunder.core.etc.Constants; import network.thunder.core.etc.Tools; @@ -36,7 +37,7 @@ public class Channel { public int id; - public byte[] nodeKeyClient; + public NodeKey nodeKeyClient; private Sha256Hash hash; /* * Pubkeys for the anchor transactions @@ -51,16 +52,10 @@ public class Channel { public byte[] masterPrivateKeyServer; /* * Keeping track of the revocation hashes we gave out. - * When we open the channel we set the depth to some high value and decrease it every X hours. - * Whenever we commit to a new version of the channel, we use a new child derived from the depth. + * When we open the channel we set the index to some high value and decrease it every X hours. + * Whenever we commit to a new version of the channel, we use a new child derived from the index. */ - public int serverChainDepth; - public int serverChainChild; - /* - * We keep track of the key chain of the other party. - * Doing so allows us to recreate and check old keys, as we know the depth of the current key we hold without poking around in the dark. - */ - public int clientChainDepth; + public int shaChainDepth; /* * Timestamps for the channel management. @@ -75,36 +70,41 @@ public class Channel { */ public Sha256Hash anchorTxHash; public Transaction anchorTx; - public List anchorSignature; public int minConfirmationAnchor; public ChannelStatus channelStatus; public ChannelSignatures channelSignatures = new ChannelSignatures(); - /* - * Upcounting version number to keep track which revocation-hash is used with which payments. - * We increase it, whenever we commit to a new channel. - */ - public int channelTxVersion; - /* * Enum to mark the different phases. * * These are necessary, as we save the state back to the database after each communication. */ public Phase phase; - /* - * Determines if the channel is ready to make/receive payments. - * We set this to true once the opening txs have enough confirmations. - * We set this to false if the channel is closed. - */ - public boolean isReady; - - public boolean requestedClose; public List closingSignatures; - public void setNodeKeyClient (byte[] nodeKeyClient) { - this.nodeKeyClient = nodeKeyClient; + public Channel copy () { + Channel channel = new Channel(); + channel.channelSignatures = this.channelSignatures.copy(); + channel.channelStatus = this.channelStatus.copy(); + + channel.timestampOpen = this.timestampOpen; + channel.masterPrivateKeyServer = this.masterPrivateKeyServer; + channel.masterPrivateKeyClient = this.masterPrivateKeyClient; + channel.phase = this.phase; + channel.anchorTx = new Transaction(Constants.getNetwork(), this.anchorTx.bitcoinSerialize()); + channel.anchorTxHash = this.anchorTxHash; + channel.closingSignatures = closingSignatures == null ? null : new ArrayList<>(this.closingSignatures); + channel.hash = this.hash; + channel.id = this.id; + channel.keyClient = this.keyClient; + channel.keyServer = this.keyServer; + channel.nodeKeyClient = this.nodeKeyClient; + channel.minConfirmationAnchor = this.minConfirmationAnchor; + channel.shaChainDepth = this.shaChainDepth; + channel.timestampForceClose = this.timestampForceClose; + channel.timestampOpen = this.timestampOpen; + return channel; } public Script getAnchorScript () { @@ -172,7 +172,7 @@ public void fillAnchorTransactionWithoutSignatures (WalletHelper walletHelper) { //region Script Getter public Script getAnchorScriptOutput () { - return ScriptTools.getAnchorOutputScriptP2SH(getKeyClient(), getKeyServer()); + return ScriptTools.getAnchorOutputScriptP2SH(keyClient, keyServer); } //endregion @@ -189,13 +189,14 @@ public Channel (byte[] nodeId, long amount) { this(); channelStatus.amountClient = amount; channelStatus.amountServer = amount; - nodeKeyClient = nodeId; - setIsReady(false); + nodeKeyClient = new NodeKey(nodeId); } public void retrieveDataFromOtherChannel (Channel channel) { keyClient = channel.keyServer; channelStatus.addressClient = channel.channelStatus.addressServer; + channelStatus.revoHashClientCurrent = channel.channelStatus.revoHashServerCurrent; + channelStatus.revoHashClientNext = channel.channelStatus.revoHashServerNext; anchorTx = channel.anchorTx; anchorTxHash = channel.anchorTxHash; @@ -209,174 +210,9 @@ public String toString () { '}'; } - //region Getter Setter - - public int getChannelTxVersion () { - return channelTxVersion; - } - - public void setChannelTxVersion (int channelTxVersion) { - this.channelTxVersion = channelTxVersion; - } - - public int getClientChainDepth () { - return clientChainDepth; - } - - public void setClientChainDepth (int clientChainDepth) { - this.clientChainDepth = clientChainDepth; - } - - public int getId () { - return id; - } - - public void setId (int id) { - this.id = id; - } - - public ECKey getKeyClient () { - return keyClient; - } - - public void setKeyClient (ECKey keyClient) { - this.keyClient = keyClient; - } - - public ECKey getKeyServer () { - return keyServer; - } - - public void setKeyServer (ECKey keyServer) { - this.keyServer = keyServer; - } - - public byte[] getMasterPrivateKeyClient () { - return masterPrivateKeyClient; - } - - public void setMasterPrivateKeyClient (byte[] masterPrivateKeyClient) { - this.masterPrivateKeyClient = masterPrivateKeyClient; - } - - public byte[] getMasterPrivateKeyServer () { - return masterPrivateKeyServer; - } - - public void setMasterPrivateKeyServer (byte[] masterPrivateKeyServer) { - this.masterPrivateKeyServer = masterPrivateKeyServer; - } - - public Phase getPhase () { - return phase; - } - - public void setPhase (Phase phase) { - this.phase = phase; - } - - public int getServerChainChild () { - return serverChainChild; - } - - public void setServerChainChild (int serverChainChild) { - this.serverChainChild = serverChainChild; - } - - public int getServerChainDepth () { - return serverChainDepth; - } - - public void setServerChainDepth (int serverChainDepth) { - this.serverChainDepth = serverChainDepth; - } - -// /** -// * New master key. -// * -// * @param masterKey the master key -// * @throws Exception the exception -// */ -// public void newMasterKey (RevocationHash masterKey) throws Exception { -// if (getMasterPrivateKeyClient() != null) { -// /* -// * Make sure the old masterPrivateKey is a child of this one.. -// */ - -// DeterministicKey key = DeterministicKey.deserializeB58(masterKey.privateKey, Constants.getNetwork()); -// DeterministicHierarchy hierachy = new DeterministicHierarchy(key); - -// List childList = HashDerivation.getChildList(getMasterChainDepth() - masterKey.depth); -// DeterministicKey keyDerived = hierachy.get(childList, true, true); - -// if (!HashDerivation.compareDeterministicKeys(keyDerived, getMasterPrivateKeyClient())) { -// throw new Exception("The new masterPrivateKey is not a parent of the one we have.."); -// } -// } - - /** - * Gets the timestamp force close. - * - * @return the timestamp force close - */ - public int getTimestampForceClose () { - return timestampForceClose; - } -// setMasterPrivateKeyClient(masterKey.getSecretAsString()); -// setMasterChainDepth(masterKey.getDepth()); -// } - - /** - * Sets the timestamp force close. - * - * @param timestampForceClose the new timestamp force close - */ - public void setTimestampForceClose (int timestampForceClose) { - this.timestampForceClose = timestampForceClose; - } - - /** - * Gets the timestamp open. - * - * @return the timestamp open - */ - public int getTimestampOpen () { - return timestampOpen; - } - - /** - * Sets the timestamp open. - * - * @param timestampOpen the new timestamp open - */ - public void setTimestampOpen (int timestampOpen) { - this.timestampOpen = timestampOpen; - } - - /** - * Checks if is ready. - * - * @return true, if is ready - */ - public boolean isReady () { - return isReady; - } - - /** - * Sets the ready. - * - * @param isReady the new ready - */ - public void setReady (boolean isReady) { - this.isReady = isReady; - } - - public void setIsReady (boolean isReady) { - this.isReady = isReady; - } - public enum Phase { NEUTRAL("0"), + OPEN("1"), ESTABLISH_REQUESTED("11"), ESTABLISH_WAITING_FOR_BLOCKCHAIN_CONFIRMATION("12"), PAYMENT_REQUESTED("21"), diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelManagerImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelManagerImpl.java index 413bf5b0..42fa4152 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelManagerImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelManagerImpl.java @@ -102,7 +102,7 @@ private void openChannelWithOpenConnection (NodeKey node, ChannelOpenListener ch @Override public void closeChannel (Channel channel, ResultCommand callback) { - NodeKey node = new NodeKey(channel.nodeKeyClient); + NodeKey node = channel.nodeKeyClient; ChannelCloser channelCloser = channelCloserMap.get(node); if (channelCloser != null) { @@ -145,7 +145,7 @@ private void maintainChannel () { //Do all kind of maintenance in here, like reconnecting to channels that disconnected.. List openChannel = dbHandler.getOpenChannel(); for (Channel channel : openChannel) { - NodeKey node = new NodeKey(channel.nodeKeyClient); + NodeKey node = channel.nodeKeyClient; if (!connectionRegistry.isConnected(node)) { connectionManager.connect(node, ConnectionIntent.MAINTAIN_CHANNEL, new ConnectionListener()); } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelSignatures.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelSignatures.java index 122fc37d..d96f5ef2 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelSignatures.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/ChannelSignatures.java @@ -16,4 +16,11 @@ public ChannelSignatures (List channelSignatures, List(this.channelSignatures); + c.paymentSignatures = new ArrayList<>(this.paymentSignatures); + return c; + } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/close/LNCloseProcessorImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/close/LNCloseProcessorImpl.java index 81ddba3d..de00b8da 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/close/LNCloseProcessorImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/close/LNCloseProcessorImpl.java @@ -1,7 +1,6 @@ package network.thunder.core.communication.layer.high.channel.close; import network.thunder.core.communication.ClientObject; -import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.ServerObject; import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.Message; @@ -33,7 +32,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import static network.thunder.core.communication.layer.high.Channel.Phase.CLOSE_REQUESTED_CLIENT; +import static network.thunder.core.communication.layer.high.Channel.Phase.CLOSED; import static network.thunder.core.communication.layer.high.Channel.Phase.CLOSE_REQUESTED_SERVER; public class LNCloseProcessorImpl extends LNCloseProcessor implements ChannelCloser { @@ -84,7 +83,7 @@ public static ChannelStatus getChannelStatus (ChannelStatus channelStatus) { //We just refund all open receiving payments and settle all open sent payments, even though this will probably cost us money. //Rather use a copy here to not run into concurrency issues somewhere else.. - ChannelStatus temp = channelStatus.getClone(); + ChannelStatus temp = channelStatus.copy(); long amountClient = temp.amountClient; long amountServer = temp.amountServer; @@ -132,8 +131,13 @@ public void closeChannel (Channel channel, ResultCommand callback) { isBlocked = true; weRequestedClose = true; channel.phase = CLOSE_REQUESTED_SERVER; - channel.isReady = false; - dbHandler.updateChannel(channel); + + //TODO need to rework this the same as LNEstablishImpl + dbHandler.updateChannelStatus( + getNode(), + channel.getHash(), + serverObject.pubKeyServer, + channel, null, null, null, null); calculateAndSendCloseMessage(); @@ -186,7 +190,7 @@ public void onLayerActive (MessageExecutor messageExecutor) { //TODO move obligation to save channel object in ChannelManagement //Need to also keep track of channels after reconnecting.. - setNode(new NodeKey(node.pubKeyClient)); + setNode(node.nodeKey); this.messageExecutor = messageExecutor; this.channelManager.addChannelCloser(getNode(), this); this.messageExecutor.sendNextLayerActive(); @@ -204,13 +208,18 @@ private void processChannelClose (LNCloseAMessage message) { List signatures = getTransactionSignatures(transaction); isBlocked = true; + boolean sendSignaturesBack = channel.phase != CLOSE_REQUESTED_SERVER; Channel channel = getChannel(); - channel.isReady = false; channel.closingSignatures = message.getSignatureList(); - dbHandler.updateChannel(channel); + channel.phase = CLOSED; + + dbHandler.updateChannelStatus( + getNode(), + channel.getHash(), + serverObject.pubKeyServer, + channel, null, null, null, null); - if (channel.phase != CLOSE_REQUESTED_SERVER) { - channel.phase = CLOSE_REQUESTED_CLIENT; + if (sendSignaturesBack) { sendCloseMessage(signatures); //Okay, so the other party sent us correct signatures to close down the channel.. } @@ -237,7 +246,7 @@ private void checkClosingMessage (LNCloseAMessage message) { } this.channel = getChannel(); //TODO fix working out the correct reverse strategy for channels that still have payments included - //ChannelStatus status = getChannelStatus(channel.channelStatus.getCloneReversed()).getCloneReversed(); + //ChannelStatus statusSender = getChannelStatus(channel.channelUpdate.reverse()).reverse(); ChannelStatus status = channel.channelStatus; Transaction transaction = getClosingTransaction(status, message.feePerByte); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/PeeledOnion.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/PeeledOnion.java index ee1d8245..0827de6b 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/PeeledOnion.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/PeeledOnion.java @@ -1,5 +1,6 @@ package network.thunder.core.communication.layer.high.payments.messages; +import network.thunder.core.communication.NodeKey; import org.bitcoinj.core.ECKey; import java.util.Arrays; @@ -17,17 +18,16 @@ void parseMessage (byte[] data) { byte[] emptyData = new byte[OnionObject.KEY_LENGTH]; if (Arrays.equals(emptyData, pubkeyOfNextHop)) { - System.out.println("We are the last hop.."); isLastHop = true; } else { - nextHop = ECKey.fromPublicOnly(pubkeyOfNextHop); + nextHop = new NodeKey(ECKey.fromPublicOnly(pubkeyOfNextHop)); } } public boolean isLastHop; public byte[] payload; - public ECKey nextHop; + public NodeKey nextHop; public long amount; public OnionObject onionObject; diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/low/authentication/AuthenticationProcessorImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/low/authentication/AuthenticationProcessorImpl.java index 4230267a..900d9fec 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/low/authentication/AuthenticationProcessorImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/low/authentication/AuthenticationProcessorImpl.java @@ -1,6 +1,7 @@ package network.thunder.core.communication.layer.low.authentication; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.ServerObject; import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.Message; @@ -50,7 +51,7 @@ public void onInboundMessage (Message message) { if (message instanceof Authentication) { processMessage(message); } else if (!authenticationExchangeFinished()) { - sendAuthenticatedErrorMessage("Not authenticated.."); + throw new RuntimeException("Not authenticated.."); } else { passMessageToNextLayer(message); } @@ -63,7 +64,6 @@ public void onOutboundMessage (Message message) { } else { throw new RuntimeException("Should not happen, which class is this? " + message); } - } public boolean shouldSendAuthenticationFirst () { @@ -79,7 +79,7 @@ public void sendAuthentication () { public void processMessage (Message message) { if (authenticationExchangeFinished()) { - messageExecutor.sendMessageUpwards(messageFactory.getFailureMessage("Already authenticated")); + throw new RuntimeException("Already authenticated"); } else { processAuthenticationMessage(message); @@ -103,7 +103,7 @@ public void processAuthenticationMessage (Message message) { } catch (Exception e) { e.printStackTrace(); - messageExecutor.sendMessageUpwards(messageFactory.getFailureMessage(e.getMessage())); + throw new RuntimeException(e); } } @@ -135,25 +135,23 @@ public void checkAuthenticationMessage (AuthenticationMessage authentication, Cl NoSuchAlgorithmException { ECKey ecKey = ECKey.fromPublicOnly(authentication.pubKeyServer); - if (node.pubKeyClient != null) { + if (node.nodeKey != null) { //Must be an outgoing connection, check if the nodeKey is what we expect it to be - if (!Arrays.equals(ecKey.getPubKey(), node.pubKeyClient.getPubKey())) { + if (!Arrays.equals(ecKey.getPubKey(), node.nodeKey.getPubKey())) { //We connected to the wrong node? - System.out.println("Connected to wrong node? Expected: " + node.pubKeyClient.getPublicKeyAsHex() + ". Is: " + ecKey.getPublicKeyAsHex()); + System.out.println("Connected to wrong node? Expected: " + node.nodeKey.getPubKeyHex() + ". Is: " + ecKey.getPublicKeyAsHex()); authenticationFailed(); } } - node.pubKeyClient = ecKey; - - ECKey pubKeyClient = node.pubKeyClient; + node.nodeKey = new NodeKey(ecKey); ECKey pubKeyTempServer = node.ephemeralKeyServer; - byte[] data = new byte[pubKeyClient.getPubKey().length + pubKeyTempServer.getPubKey().length]; - System.arraycopy(pubKeyClient.getPubKey(), 0, data, 0, pubKeyClient.getPubKey().length); - System.arraycopy(pubKeyTempServer.getPubKey(), 0, data, pubKeyClient.getPubKey().length, pubKeyTempServer.getPubKey().length); + byte[] data = new byte[ecKey.getPubKey().length + pubKeyTempServer.getPubKey().length]; + System.arraycopy(ecKey.getPubKey(), 0, data, 0, ecKey.getPubKey().length); + System.arraycopy(pubKeyTempServer.getPubKey(), 0, data, ecKey.getPubKey().length, pubKeyTempServer.getPubKey().length); - if (!CryptoTools.verifySignature(pubKeyClient, data, authentication.signature)) { + if (!CryptoTools.verifySignature(ecKey, data, authentication.signature)) { System.out.println("Node was not able to authenticate.."); authenticationFailed(); } @@ -168,10 +166,6 @@ private boolean authenticationExchangeFinished () { return authFinished; } - private void sendAuthenticatedErrorMessage (String error) { - messageExecutor.sendMessageUpwards(messageFactory.getFailureMessage(error)); - } - private void passMessageToNextLayer (Message message) { messageExecutor.sendMessageDownwards(message); } diff --git a/thunder-core/src/main/java/network/thunder/core/etc/SeedNodes.java b/thunder-core/src/main/java/network/thunder/core/etc/SeedNodes.java index 69536966..1c3d786d 100644 --- a/thunder-core/src/main/java/network/thunder/core/etc/SeedNodes.java +++ b/thunder-core/src/main/java/network/thunder/core/etc/SeedNodes.java @@ -48,7 +48,7 @@ public static void setToTestValues () { PubkeyIPObject seed1 = new PubkeyIPObject(); seed1.hostname = "localhost"; seed1.port = Constants.STANDARD_PORT; - seed1.pubkey = node.pubKeyClient.getPubKey(); + seed1.pubkey = node.nodeKey.getPubKey(); ipList.clear(); From a880a86d1bc23e3a5ef2e59409e2032b5c517f5b Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:42:41 +0100 Subject: [PATCH 09/21] Use ThunderContext.receive for creating and saving a PaymentSecret --- .../java/wallettemplate/ChannelInfoController.java | 2 +- .../ReceiveMoneyRequestController.java | 6 +++++- .../java/network/thunder/core/ThunderContext.java | 13 +++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/thunder-clientgui/src/main/java/wallettemplate/ChannelInfoController.java b/thunder-clientgui/src/main/java/wallettemplate/ChannelInfoController.java index 7772e6dc..63c0fe41 100644 --- a/thunder-clientgui/src/main/java/wallettemplate/ChannelInfoController.java +++ b/thunder-clientgui/src/main/java/wallettemplate/ChannelInfoController.java @@ -78,7 +78,7 @@ void initialize () throws SQLException { balanceClient.setText(Coin.valueOf(channel.channelStatus.amountClient).toFriendlyString()); balanceServer.setText(Coin.valueOf(channel.channelStatus.amountServer).toFriendlyString()); - labelOpen.setText(new Date(((long) channel.getTimestampOpen()) * 1000).toString()); + labelOpen.setText(new Date(((long) channel.timestampOpen) * 1000).toString()); txOpen.setText(Tools.bytesToHex(channel.anchorTx.bitcoinSerialize())); diff --git a/thunder-clientgui/src/main/java/wallettemplate/ReceiveMoneyRequestController.java b/thunder-clientgui/src/main/java/wallettemplate/ReceiveMoneyRequestController.java index de67dde8..3989d70d 100644 --- a/thunder-clientgui/src/main/java/wallettemplate/ReceiveMoneyRequestController.java +++ b/thunder-clientgui/src/main/java/wallettemplate/ReceiveMoneyRequestController.java @@ -75,9 +75,13 @@ void initialize () { public void update () { - PaymentRequest paymentRequest = Main.thunderContext.receivePayment(getAmount()); + if(amount.getText().equals("")) { + amount.setText("0"); + } + try { + PaymentRequest paymentRequest = Main.thunderContext.receivePayment(getAmount()); byte[] payload = paymentRequest.getPayload(); diff --git a/thunder-core/src/main/java/network/thunder/core/ThunderContext.java b/thunder-core/src/main/java/network/thunder/core/ThunderContext.java index fc89f317..6687e301 100644 --- a/thunder-core/src/main/java/network/thunder/core/ThunderContext.java +++ b/thunder-core/src/main/java/network/thunder/core/ThunderContext.java @@ -1,6 +1,8 @@ package network.thunder.core; -import network.thunder.core.communication.*; +import network.thunder.core.communication.LNConfiguration; +import network.thunder.core.communication.NodeKey; +import network.thunder.core.communication.ServerObject; import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.ContextFactoryImpl; import network.thunder.core.communication.layer.high.Channel; @@ -94,23 +96,26 @@ public void makePayment (byte[] receiver, long amount, PaymentSecret secret, Res if (Arrays.equals(receiver, node.pubKeyServer.getPubKey())) { throw new LNPaymentException("Can't send to yourself!"); } - LNPaymentHelper paymentHelper = contextFactory.getPaymentHelper(); + LNOnionHelper onionHelper = contextFactory.getOnionHelper(); LNRoutingHelper routingHelper = contextFactory.getLNRoutingHelper(); List route = routingHelper.getRoute(node.pubKeyServer.getPubKey(), receiver, 1000L, 1f, 1f, 1f); + if (route.size() == 0) { + throw new LNPaymentException("No route found.."); + } OnionObject object = onionHelper.createOnionObject(route, null); + LNPaymentHelper paymentHelper = contextFactory.getPaymentHelper(); + PaymentData paymentData = new PaymentData(); paymentData.amount = amount; paymentData.onionObject = object; - paymentData.sending = true; paymentData.secret = secret; paymentData.timestampOpen = Tools.currentTime(); paymentData.timestampRefund = Tools.currentTime() + route.size() * configuration.MAX_REFUND_DELAY * configuration.MAX_OVERLAY_REFUND; - paymentData.csvDelay = configuration.DEFAULT_REVOCATION_DELAY; paymentHelper.makePayment(paymentData); } catch (Exception e) { From d3347418410dd01bec69775631598e77db0cb1e5 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:49:12 +0100 Subject: [PATCH 10/21] Remove error messages, rather close the connection --- .../layer/MessageExecutorImpl.java | 1 - .../communication/layer/MessageFactory.java | 3 - .../layer/MesssageFactoryImpl.java | 8 --- .../encryption/EncryptionProcessorImpl.java | 70 +++++++++++++++++-- .../processor/exceptions/LNException.java | 2 +- 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageExecutorImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageExecutorImpl.java index aad19556..5f6c3135 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageExecutorImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageExecutorImpl.java @@ -25,7 +25,6 @@ public void sendNextLayerActive () { @Override public void sendMessageUpwards (Message message) { context.writeAndFlush(message); - } @Override diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageFactory.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageFactory.java index d90759c9..d674578f 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageFactory.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/MessageFactory.java @@ -1,8 +1,5 @@ package network.thunder.core.communication.layer; public interface MessageFactory { - FailureMessage getFailureMessage (String failure); - - Message parseMessage (Object object); } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/MesssageFactoryImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/MesssageFactoryImpl.java index b8a53287..73afdc09 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/MesssageFactoryImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/MesssageFactoryImpl.java @@ -1,13 +1,5 @@ package network.thunder.core.communication.layer; public abstract class MesssageFactoryImpl implements MessageFactory { - @Override - public FailureMessage getFailureMessage (String failure) { - return new FailureMessageImpl(failure); - } - @Override - public Message parseMessage (Object object) { - return null; - } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/low/encryption/EncryptionProcessorImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/low/encryption/EncryptionProcessorImpl.java index ce4c3ebd..b4d1ef74 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/low/encryption/EncryptionProcessorImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/low/encryption/EncryptionProcessorImpl.java @@ -4,14 +4,19 @@ import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.Message; import network.thunder.core.communication.layer.MessageExecutor; +import network.thunder.core.communication.layer.high.AckMessage; +import network.thunder.core.communication.layer.high.NumberedMessage; import network.thunder.core.communication.layer.low.encryption.messages.EncryptedMessage; import network.thunder.core.communication.layer.low.encryption.messages.EncryptionInitialMessage; import network.thunder.core.communication.layer.low.encryption.messages.EncryptionMessageFactory; +import network.thunder.core.communication.layer.middle.broadcasting.gossip.messages.Gossip; +import network.thunder.core.etc.Tools; import network.thunder.core.helper.crypto.ECDH; import org.bitcoinj.core.ECKey; public class EncryptionProcessorImpl extends EncryptionProcessor { - public static final boolean OUTPUT_MESSAGE = false; + public static final boolean OUTPUT_MESSAGE = true; + public static final boolean OUTPUT_GOSSIP = false; EncryptionMessageFactory messageFactory; MessageEncrypter messageEncrypter; ClientObject node; @@ -86,14 +91,24 @@ private void processEncryptedMessage (Message message) { private void processMessageToBeDecrypted (EncryptedMessage message) { Message decryptedMessage = messageEncrypter.decrypt(message, node.ecdhKeySet); if (OUTPUT_MESSAGE) { - System.out.println("I: " + node.host + " " + decryptedMessage); + if (decryptedMessage instanceof Gossip) { + if (OUTPUT_GOSSIP) { + System.out.println("I: " + getClientName() + " " + decryptedMessage); + } + } else { + System.out.println("I: " + + getMessageNumber(decryptedMessage) + " " + + getAckedMessageNumber(decryptedMessage) + " " + + getClientName() + " " + + decryptedMessage + "[" + (message.payload.length / 1024) + "]"); + } } executor.sendMessageDownwards(decryptedMessage); } private void processEncryptionInitialMessage (Message message) { if (!(message instanceof EncryptionInitialMessage)) { - executor.sendMessageUpwards(messageFactory.getFailureMessage("Expecting EncryptionInitial Message.. " + message)); + throw new RuntimeException("Expecting EncryptionInitial Message.. " + message); } else { EncryptionInitialMessage encryptionInitial = (EncryptionInitialMessage) message; @@ -113,9 +128,56 @@ private void onKeyExchangeFinished () { private void processMessageToBeEncrypted (Message message) { EncryptedMessage encryptedMessage = messageEncrypter.encrypt(message, node.ecdhKeySet); if (OUTPUT_MESSAGE) { - System.out.println("O: " + node.host + " " + message + "[" + (encryptedMessage.payload.length / 1024) + "]"); + if (message instanceof Gossip) { + if (OUTPUT_GOSSIP) { + System.out.println("O: " + getClientName() + " " + message + "[" + (encryptedMessage.payload.length / 1024) + "]"); + } + } else { + System.out.println("O: " + + System.currentTimeMillis() + " " + + getMessageNumber(message) + " " + + getAckedMessageNumber(message) + " " + + getClientName() + " " + + message + "[" + (encryptedMessage.payload.length / 1024) + "]"); + } } executor.sendMessageUpwards(encryptedMessage); } + private void logIncomingMessage(Message decryptedMessage, EncryptedMessage message) { + System.out.println("I: " + + getMessageNumber(decryptedMessage) + " " + + getAckedMessageNumber(decryptedMessage) + " " + + getClientName() + " " + + decryptedMessage + "[" + (message.payload.length / 1024) + "]"); + } + + private String getClientName () { + if (node.host == null) { + if (node.nodeKey != null) { + return Tools.bytesToHex(node.nodeKey.getPubKey()).substring(0, 8); + } else { + return null; + } + } else { + return node.host; + } + } + + private static long getMessageNumber (Message m) { + if (m instanceof NumberedMessage) { + return ((NumberedMessage) m).getMessageNumber(); + } else { + return -1; + } + } + + private static long getAckedMessageNumber (Message m) { + if (m instanceof AckMessage) { + return ((AckMessage) m).getMessageNumberToAck(); + } else { + return -1; + } + } + } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/processor/exceptions/LNException.java b/thunder-core/src/main/java/network/thunder/core/communication/processor/exceptions/LNException.java index e84dd58d..7474b407 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/processor/exceptions/LNException.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/processor/exceptions/LNException.java @@ -22,6 +22,6 @@ protected LNException (String message, Throwable cause, boolean enableSuppressio } public boolean shouldDisconnect () { - return false; + return true; } } From 7e546ce6288c06f41fa37e140fecd34f8a468a54 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:51:29 +0100 Subject: [PATCH 11/21] Test for null when copying ChannelStatus --- .../core/communication/layer/high/ChannelStatus.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java index 3c502645..e95a58cd 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java @@ -33,10 +33,10 @@ public ChannelStatus copy () { status.csvDelay = this.csvDelay; status.addressClient = this.addressClient; status.addressServer = this.addressServer; - status.revoHashClientCurrent = revoHashClientCurrent.copy(); - status.revoHashClientNext = revoHashClientNext.copy(); - status.revoHashServerCurrent = revoHashServerCurrent.copy(); - status.revoHashServerNext = revoHashServerNext.copy(); + status.revoHashClientCurrent = revoHashClientCurrent == null ? null : revoHashClientCurrent.copy(); + status.revoHashClientNext = revoHashClientNext == null ? null : revoHashClientNext.copy(); + status.revoHashServerCurrent = revoHashServerCurrent == null ? null : revoHashServerCurrent.copy(); + status.revoHashServerNext = revoHashServerNext == null ? null : revoHashServerNext.copy(); status.paymentList = clonePaymentList(this.paymentList); return status; From 3f0df0de3d5fc3e3d3ec0b4a5d9c9a6594873772 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 17:55:08 +0100 Subject: [PATCH 12/21] Don't use a different csvDelay for different payments --- .../layer/high/payments/PaymentData.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentData.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentData.java index 2ddff3e6..578a71eb 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentData.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentData.java @@ -23,19 +23,13 @@ public class PaymentData implements Cloneable { public boolean sending; public long amount; - public long fee; public PaymentSecret secret; public int timestampOpen; public int timestampRefund; //timestamp at which the other party will consider this payment refunded - public int csvDelay; //revocation delay for dual-tx public OnionObject onionObject; - /* - * TODO: We probably need further fields here, can't think of any now.. - */ - @Override public boolean equals (Object o) { if (this == o) { @@ -47,13 +41,18 @@ public boolean equals (Object o) { PaymentData that = (PaymentData) o; + if (amount != that.amount) { + return false; + } return secret != null ? secret.equals(that.secret) : that.secret == null; } @Override public int hashCode () { - return secret != null ? secret.hashCode() : 0; + int result = (int) (amount ^ (amount >>> 32)); + result = 31 * result + (secret != null ? secret.hashCode() : 0); + return result; } @Override @@ -61,6 +60,7 @@ public String toString () { return "PaymentData{" + "sending=" + sending + ", amount=" + amount + + ", onion=" + onionObject + '}'; } @@ -70,11 +70,9 @@ public Object clone () throws CloneNotSupportedException { p.onionObject = onionObject; p.sending = sending; p.amount = amount; - p.csvDelay = csvDelay; p.timestampOpen = timestampOpen; p.timestampRefund = timestampRefund; p.secret = secret; - p.fee = fee; return p; } From b5b7eba28a00e22ad8241d53764baa86b4a9c016 Mon Sep 17 00:00:00 2001 From: matsjj Date: Thu, 26 May 2016 18:05:22 +0100 Subject: [PATCH 13/21] Fixed tests --- .../java/network/thunder/core/MainClient.java | 2 +- .../ConnectionManagerImplTest.java | 90 ----- .../core/communication/FullCommTest.java | 3 +- .../layers/high/LNCloseHandlerTest.java | 24 +- .../layers/high/LNEstablishHandlerTest.java | 3 +- .../layers/high/LNPaymentHandlerTest.java | 202 ++++------ .../layers/high/LNPaymentLogicImplTest.java | 366 +++++++++++++----- .../layers/high/LNPaymentRoutingTest.java | 333 ---------------- .../layers/low/AckProcessorImplTest.kt | 116 ++++++ .../layers/low/AuthenticationHandlerTest.java | 35 +- .../layers/mid/SyncHandlerTest.java | 2 - .../core/etc/ConnectionManagerWrapper.java | 13 - .../thunder/core/etc/DBHandlerMock.java | 103 +++-- .../thunder/core/etc/HashDerivationTest.java | 67 ---- .../core/etc/LNPaymentDBHandlerMock.java | 134 +------ .../core/etc/LNPaymentMessageFactoryMock.java | 61 --- .../thunder/core/etc/MockLNEventHelper.java | 8 +- .../thunder/core/etc/MockLNPaymentHelper.java | 21 +- .../thunder/core/etc/MockLNPaymentLogic.java | 63 ++- .../thunder/core/etc/RevocationHashTest.java | 14 +- .../network/thunder/core/etc/TestTools.java | 44 +++ 21 files changed, 657 insertions(+), 1047 deletions(-) delete mode 100644 thunder-core/src/test/java/network/thunder/core/communication/ConnectionManagerImplTest.java delete mode 100644 thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentRoutingTest.java create mode 100644 thunder-core/src/test/java/network/thunder/core/communication/layers/low/AckProcessorImplTest.kt delete mode 100644 thunder-core/src/test/java/network/thunder/core/etc/ConnectionManagerWrapper.java delete mode 100644 thunder-core/src/test/java/network/thunder/core/etc/HashDerivationTest.java delete mode 100644 thunder-core/src/test/java/network/thunder/core/etc/LNPaymentMessageFactoryMock.java diff --git a/thunder-core/src/main/java/network/thunder/core/MainClient.java b/thunder-core/src/main/java/network/thunder/core/MainClient.java index 6c258bf5..2e60d377 100644 --- a/thunder-core/src/main/java/network/thunder/core/MainClient.java +++ b/thunder-core/src/main/java/network/thunder/core/MainClient.java @@ -98,7 +98,7 @@ public static void main (String[] args) throws Exception { paymentData.onionObject = onionObject; LNPaymentHelper paymentHelper = contextFactory.getPaymentHelper(); - paymentHelper.relayPayment(null, paymentData); + paymentHelper.makePayment(paymentData); while (true) { Thread.sleep(100000); diff --git a/thunder-core/src/test/java/network/thunder/core/communication/ConnectionManagerImplTest.java b/thunder-core/src/test/java/network/thunder/core/communication/ConnectionManagerImplTest.java deleted file mode 100644 index e5b7992f..00000000 --- a/thunder-core/src/test/java/network/thunder/core/communication/ConnectionManagerImplTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package network.thunder.core.communication; - -import network.thunder.core.communication.layer.ContextFactoryImpl; -import network.thunder.core.database.InMemoryDBHandler; -import network.thunder.core.etc.ConnectionManagerWrapper; -import network.thunder.core.etc.Constants; -import network.thunder.core.etc.SeedNodes; -import network.thunder.core.helper.callback.results.NullResultCommand; -import network.thunder.core.helper.events.LNEventHelperImpl; -import network.thunder.core.helper.wallet.MockWallet; -import org.junit.Before; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ConnectionManagerImplTest { - - final static int AMOUNT_OF_NODES = 100; - final static int PORT_START = 20000; - - ConnectionManagerWrapper connectionSeed; - - List clients = new ArrayList<>(); - - @Before - public void prepare () { - createSeedConnection(); - createNodes(); - } - - //Take out this test for now until we are done refactoring ConnectionManager - // @Test - public void test () throws Exception { - Logger.getLogger("io.netty").setLevel(Level.OFF); - - connectionSeed.connectionManager.startListening(new NullResultCommand()); - - for (int i = 6; i < 10; ++i) { - - System.out.println("------------------------------------------------------------------------------"); - System.out.println(i + " started up..."); - - clients.get(i).connectionManager.fetchNetworkIPs(new NullResultCommand()); - clients.get(i).connectionManager.startSyncing(new NullResultCommand()); - clients.get(i).connectionManager.startBuildingRandomChannel(new NullResultCommand()); - Thread.sleep(500); - } - - Thread.sleep(1000); - } - - private void createSeedConnection () { - SeedNodes.setToTestValues(); - ClientObject node = SeedNodes.nodeList.get(0); - ServerObject serverObject = new ServerObject(); - serverObject.portServer = node.port; - serverObject.hostServer = node.host; - serverObject.pubKeyServer = node.pubKeyClient; - - connectionSeed = getConnection(serverObject); - } - - private void createNodes () { - for (int i = 0; i < AMOUNT_OF_NODES; ++i) { - int port = PORT_START + i; - String host = "127.0.0.1"; - - ServerObject node = new ServerObject(); - node.init(); - node.portServer = port; - node.hostServer = host; - - ConnectionManagerWrapper wrapper = getConnection(node); - - clients.add(wrapper); - } - } - - private ConnectionManagerWrapper getConnection (ServerObject node) { - ConnectionManagerWrapper connection = new ConnectionManagerWrapper(); - connection.dbHandler = new InMemoryDBHandler(); - connection.wallet = new MockWallet(Constants.getNetwork()); - connection.contextFactory = new ContextFactoryImpl(node, connection.dbHandler, connection.wallet, new LNEventHelperImpl()); - connection.connectionManager = new ConnectionManagerImpl(connection.contextFactory, connection.dbHandler); - return connection; - } - -} \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/communication/FullCommTest.java b/thunder-core/src/test/java/network/thunder/core/communication/FullCommTest.java index f98afb35..a7d8bf2c 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/FullCommTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/FullCommTest.java @@ -79,10 +79,11 @@ private static void buildPaymentChannels (List nodeList) throws Int shouldConnected.stream() .map(ByteBuffer::array) .map(ECKey::fromPublicOnly) + .map(NodeKey::new) .forEach(pubkey -> actuallyConnected.addAll( nodeWrapper.dbHandler.getOpenChannel(pubkey). stream() - .map(p -> p.nodeKeyClient) + .map(p -> p.nodeKeyClient.getPubKey()) .map(ByteBuffer::wrap) .collect(Collectors.toList()))); diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNCloseHandlerTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNCloseHandlerTest.java index 53160bf2..77dd2397 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNCloseHandlerTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNCloseHandlerTest.java @@ -8,7 +8,6 @@ import network.thunder.core.communication.layer.Message; import network.thunder.core.communication.layer.ProcessorHandler; import network.thunder.core.communication.layer.high.Channel; -import network.thunder.core.communication.layer.high.channel.close.LNCloseException; import network.thunder.core.communication.layer.high.channel.close.LNCloseProcessorImpl; import network.thunder.core.communication.layer.high.channel.close.messages.LNCloseAMessage; import network.thunder.core.communication.layer.middle.broadcasting.gossip.BroadcastHelper; @@ -30,6 +29,7 @@ import java.beans.PropertyVetoException; import java.sql.SQLException; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -82,14 +82,14 @@ public void prepare () throws PropertyVetoException, SQLException { channel1.channelStatus.addressServer = new Address(Constants.getNetwork(), Tools.getRandomByte(20)); channel2.channelStatus.addressServer = new Address(Constants.getNetwork(), Tools.getRandomByte(20)); - channel1.nodeKeyClient = node1.pubKeyClient.getPubKey(); - channel2.nodeKeyClient = node2.pubKeyClient.getPubKey(); + channel1.nodeKeyClient = node1.nodeKey; + channel2.nodeKeyClient = node2.nodeKey; channel1.retrieveDataFromOtherChannel(channel2); channel2.retrieveDataFromOtherChannel(channel1); - dbHandler1.saveChannel(channel1); - dbHandler2.saveChannel(channel2); + dbHandler1.insertChannel(channel1); + dbHandler2.insertChannel(channel2); embeddedChannel1 = new EmbeddedChannel(new ProcessorHandler(processor1, "LNClose1")); embeddedChannel2 = new EmbeddedChannel(new ProcessorHandler(processor2, "LNClose2")); @@ -116,27 +116,26 @@ public void shouldSendMessage () { @Test public void shouldVerifyMessage () { -// channelManager.closeChannel(channel1, new NullResultCommand()); LNCloseAMessage message = (LNCloseAMessage) embeddedChannel1.readOutbound(); embeddedChannel2.writeInbound(message); after(); } - @Test(expected = LNCloseException.class) + @Test public void shouldFailSignatureOne () { -// channelManager.closeChannel(channel1, new NullResultCommand()); LNCloseAMessage message = (LNCloseAMessage) embeddedChannel1.readOutbound(); Tools.copyRandomByteInByteArray(message.signatureList.get(0), 30, 2); embeddedChannel2.writeInbound(message); + assertFalse(embeddedChannel2.isOpen()); after(); } - @Test(expected = LNCloseException.class) + @Test public void shouldFailSignatureTwo () { -// channelManager.closeChannel(channel1, new NullResultCommand()); LNCloseAMessage message = (LNCloseAMessage) embeddedChannel1.readOutbound(); Tools.copyRandomByteInByteArray(message.signatureList.get(0), 30, 2); embeddedChannel2.writeInbound(message); + assertFalse(embeddedChannel2.isOpen()); after(); } @@ -155,11 +154,6 @@ public BlockchainHelper getBlockchainHelper () { public BroadcastHelper getBroadcastHelper () { return broadcastHelper; } - -// @Override -// public ChannelManager getChannelManager () { -// return channelManager; -// } } } \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNEstablishHandlerTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNEstablishHandlerTest.java index e3b7d012..c5bf3a10 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNEstablishHandlerTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNEstablishHandlerTest.java @@ -3,7 +3,6 @@ import io.netty.channel.embedded.EmbeddedChannel; import network.thunder.core.communication.ClientObject; import network.thunder.core.communication.ConnectionManager; -import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.ServerObject; import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.ContextFactoryImpl; @@ -66,7 +65,7 @@ public void prepare () throws PropertyVetoException, SQLException { channel1 = new EmbeddedChannel(new ProcessorHandler(processor1, "LNEstablish1")); channel2 = new EmbeddedChannel(new ProcessorHandler(processor2, "LNEstablish2")); - contextFactory1.getChannelManager().openChannel(new NodeKey(node1.pubKeyClient), new ChannelOpenListener()); + contextFactory1.getChannelManager().openChannel(node1.nodeKey, new ChannelOpenListener()); Message m = (Message) channel2.readOutbound(); assertNull(m); diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java index adbd423e..9507af33 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java @@ -2,23 +2,23 @@ import io.netty.channel.embedded.EmbeddedChannel; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.LNConfiguration; +import network.thunder.core.communication.NodeKey; +import network.thunder.core.communication.ServerObject; +import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.Message; import network.thunder.core.communication.layer.ProcessorHandler; -import network.thunder.core.communication.layer.high.payments.PaymentData; +import network.thunder.core.communication.layer.high.AckMessageImpl; +import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.layer.high.payments.*; import network.thunder.core.communication.layer.high.payments.messages.LNPaymentAMessage; import network.thunder.core.communication.layer.high.payments.messages.LNPaymentBMessage; import network.thunder.core.communication.layer.high.payments.messages.LNPaymentCMessage; -import network.thunder.core.communication.layer.high.payments.messages.LNPaymentDMessage; -import network.thunder.core.communication.layer.ContextFactory; -import network.thunder.core.communication.layer.high.payments.messages.LNPaymentMessageFactory; -import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; -import network.thunder.core.communication.layer.high.payments.PaymentSecret; -import network.thunder.core.communication.layer.high.payments.LNPaymentProcessorImpl; -import network.thunder.core.communication.layer.high.payments.LNPaymentLogic; -import network.thunder.core.communication.layer.high.payments.LNPaymentProcessor; import network.thunder.core.database.DBHandler; -import network.thunder.core.etc.*; -import network.thunder.core.communication.ServerObject; +import network.thunder.core.database.InMemoryDBHandler; +import network.thunder.core.etc.MockContextFactory; +import network.thunder.core.etc.TestTools; +import network.thunder.core.etc.Tools; import org.junit.Before; import org.junit.Test; @@ -26,10 +26,13 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; -import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class LNPaymentHandlerTest { @@ -45,17 +48,33 @@ public class LNPaymentHandlerTest { LNPaymentProcessorImpl processor12; LNPaymentProcessorImpl processor21; - DBHandler dbHandler1 = new LNPaymentDBHandlerMock(); - DBHandler dbHandler2 = new LNPaymentDBHandlerMock(); + DBHandler dbHandler1 = new InMemoryDBHandler(); + DBHandler dbHandler2 = new InMemoryDBHandler(); - ContextFactory contextFactory12 = new MockLNPaymentContextFactory(serverObject1, dbHandler1); - ContextFactory contextFactory21 = new MockLNPaymentContextFactory(serverObject2, dbHandler2); + ContextFactory contextFactory12 = new MockContextFactory(serverObject1, dbHandler1); + ContextFactory contextFactory21 = new MockContextFactory(serverObject2, dbHandler2); + + LNPaymentHelper paymentHelper1 = contextFactory12.getPaymentHelper(); + + Channel channel1 = TestTools.getMockChannel(new LNConfiguration()); + Channel channel2 = TestTools.getMockChannel(new LNConfiguration()); @Before public void prepare () throws PropertyVetoException, SQLException { node1.isServer = false; node2.isServer = true; + channel1.nodeKeyClient = new NodeKey(serverObject2.pubKeyServer); + channel2.nodeKeyClient = new NodeKey(serverObject1.pubKeyServer); + + channel1.retrieveDataFromOtherChannel(channel2); + channel2.retrieveDataFromOtherChannel(channel1); + channel2.channelStatus.amountServer = channel1.channelStatus.amountClient; + channel2.channelStatus.amountClient = channel1.channelStatus.amountServer; + + dbHandler1.insertChannel(channel1); + dbHandler2.insertChannel(channel2); + this.node1.name = "LNPayment12"; this.node2.name = "LNPayment21"; @@ -76,131 +95,80 @@ public void after () { } @Test - public void fullExchangeWithNoDisturbanceWithinTimeframe () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - processor12.makePayment(getMockPaymentData()); - Thread.sleep(200); + public void fullExchange () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { + PaymentData paymentData = getMockPaymentData(); + dbHandler2.addPaymentSecret(paymentData.secret); + paymentHelper1.makePayment(paymentData); TestTools.exchangeMessages(channel12, channel21, LNPaymentAMessage.class); TestTools.exchangeMessages(channel21, channel12, LNPaymentBMessage.class); TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); + TestTools.exchangeMessages(channel21, channel12, AckMessageImpl.class); + + Thread.sleep(1000); + + TestTools.exchangeMessages(channel21, channel12, LNPaymentAMessage.class); + TestTools.exchangeMessages(channel12, channel21, LNPaymentBMessage.class); TestTools.exchangeMessages(channel21, channel12, LNPaymentCMessage.class); - TestTools.exchangeMessages(channel12, channel21, LNPaymentDMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentDMessage.class); + TestTools.exchangeMessages(channel12, channel21, AckMessageImpl.class); assertNull(channel12.readOutbound()); assertNull(channel21.readOutbound()); - after(); - } - - @Test - public void exchangeWithDelayShouldRestart () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - LNPaymentProcessor.TIMEOUT_NEGOTIATION = 500; - - processor12.makePayment(getMockPaymentData()); - Thread.sleep(100); - - TestTools.exchangeMessages(channel12, channel21); - TestTools.exchangeMessages(channel21, channel12); - Message message = (Message) channel12.readOutbound(); - - Thread.sleep((long) (LNPaymentProcessor.TIMEOUT_NEGOTIATION * 1.5)); - - System.out.println(message); + Channel channel1After = dbHandler1.getChannel(channel1.getHash()); + Channel channel2After = dbHandler2.getChannel(channel2.getHash()); - channel21.writeInbound(message); - assertNull(channel21.readOutbound()); - - TestTools.exchangeMessages(channel12, channel21, LNPaymentAMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentBMessage.class); + assertEquals(channel1.channelStatus.amountServer - paymentData.amount, channel1After.channelStatus.amountServer); + assertEquals(channel1.channelStatus.amountClient + paymentData.amount, channel1After.channelStatus.amountClient); + assertEquals(channel2.channelStatus.amountServer + paymentData.amount, channel2After.channelStatus.amountServer); + assertEquals(channel2.channelStatus.amountClient - paymentData.amount, channel2After.channelStatus.amountClient); after(); } @Test - public void exchangeWithOtherPartyStartingOwnExchange () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - processor12.makePayment(getMockPaymentData()); - Thread.sleep(200); + public void fullExchangeWithAnotherPaymentMidway () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { + paymentHelper1.makePayment(getMockPaymentData()); TestTools.exchangeMessages(channel12, channel21, LNPaymentAMessage.class); TestTools.exchangeMessages(channel21, channel12, LNPaymentBMessage.class); + paymentHelper1.makePayment(getMockPaymentData()); TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentCMessage.class); - - Message message = (Message) channel12.readOutbound(); - assertThat(message, instanceOf(LNPaymentDMessage.class)); - - System.out.println("abort.."); - Thread.sleep(200); - processor21.makePayment(getMockPaymentData()); - processor21.abortCurrentExchange(); - Thread.sleep(500); - - TestTools.exchangeMessages(channel21, channel12, LNPaymentAMessage.class); - //Other node should ignore this new exchange - assertNull(channel12.readOutbound()); + assertTrue(channel12.readOutbound() instanceof LNPaymentAMessage); after(); } @Test - public void exchangeConcurrentWithOneThrowingHigherDice () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - - processor12.makePayment(getMockPaymentData()); - processor21.makePayment(getMockPaymentData()); - Thread.sleep(200); - - LNPaymentAMessage message1 = (LNPaymentAMessage) channel12.readOutbound(); - assertThat(message1, instanceOf(LNPaymentAMessage.class)); - - LNPaymentAMessage message2 = (LNPaymentAMessage) channel21.readOutbound(); - assertThat(message2, instanceOf(LNPaymentAMessage.class)); - - int dice1 = message1.dice; - int dice2 = message2.dice; + public void sendWrongMessageShouldDisconnect () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { + paymentHelper1.makePayment(getMockPaymentData()); - channel12.writeInbound(message2); - channel21.writeInbound(message1); + LNPaymentAMessage messageA = (LNPaymentAMessage) channel12.readOutbound(); + channel21.writeInbound(messageA); + LNPaymentBMessage messageB = (LNPaymentBMessage) channel21.readOutbound(); + channel21.writeInbound(messageB); - if (dice2 > dice1) { - EmbeddedChannel channel3 = channel12; - channel12 = channel21; - channel21 = channel3; - } - - TestTools.exchangeMessages(channel21, channel12, LNPaymentBMessage.class); - TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentCMessage.class); - TestTools.exchangeMessages(channel12, channel21, LNPaymentDMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentDMessage.class); - - Thread.sleep(200); - - TestTools.exchangeMessages(channel21, channel12, LNPaymentAMessage.class); - TestTools.exchangeMessages(channel12, channel21, LNPaymentBMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentCMessage.class); - TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); - TestTools.exchangeMessages(channel21, channel12, LNPaymentDMessage.class); - TestTools.exchangeMessages(channel12, channel21, LNPaymentDMessage.class); + assertFalse(channel21.isOpen()); after(); } - public void exchangePayment (EmbeddedChannel from, EmbeddedChannel to) { - TestTools.exchangeMessages(from, to, LNPaymentAMessage.class); - TestTools.exchangeMessages(to, from, LNPaymentBMessage.class); - TestTools.exchangeMessages(from, to, LNPaymentCMessage.class); - TestTools.exchangeMessages(to, from, LNPaymentCMessage.class); - TestTools.exchangeMessages(from, to, LNPaymentDMessage.class); - TestTools.exchangeMessages(to, from, LNPaymentDMessage.class); - } - public PaymentData getMockPaymentData () { + LNConfiguration configuration = new LNConfiguration(); PaymentData paymentData = new PaymentData(); paymentData.sending = true; paymentData.amount = 10000; paymentData.secret = new PaymentSecret(Tools.getRandomByte(20)); + paymentData.timestampOpen = Tools.currentTime(); + paymentData.timestampRefund = paymentData.timestampOpen + configuration.DEFAULT_REFUND_DELAY * 10; + + LNOnionHelper onionHelper = new LNOnionHelperImpl(); + List route = new ArrayList<>(); + route.add(serverObject1.pubKeyServer.getPubKey()); + route.add(serverObject2.pubKeyServer.getPubKey()); + + paymentData.onionObject = onionHelper.createOnionObject(route, null); return paymentData; } @@ -222,26 +190,4 @@ public void run () { }).start(); } - class MockLNPaymentContextFactory extends MockContextFactory { - - public MockLNPaymentContextFactory (ServerObject node, DBHandler dbHandler) { - super(node, dbHandler); - } - - @Override - public LNPaymentLogic getLNPaymentLogic () { - return new MockLNPaymentLogic(getLNPaymentMessageFactory()); - } - - @Override - public LNPaymentMessageFactory getLNPaymentMessageFactory () { - return new LNPaymentMessageFactoryMock(); - } - - @Override - public LNPaymentHelper getPaymentHelper () { - return new MockLNPaymentHelper(); - } - } - } \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java index 55e83249..63343142 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java @@ -3,25 +3,28 @@ import network.thunder.core.communication.ClientObject; import network.thunder.core.communication.LNConfiguration; import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.layer.high.channel.ChannelSignatures; import network.thunder.core.communication.layer.high.payments.LNPaymentLogic; import network.thunder.core.communication.layer.high.payments.LNPaymentLogicImpl; import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.high.payments.PaymentSecret; -import network.thunder.core.communication.layer.high.payments.messages.*; -import network.thunder.core.communication.layer.high.payments.queue.QueueElementPayment; import network.thunder.core.communication.processor.exceptions.LNPaymentException; import network.thunder.core.etc.Constants; -import network.thunder.core.etc.LNPaymentDBHandlerMock; +import network.thunder.core.etc.TestTools; import network.thunder.core.etc.Tools; -import org.bitcoinj.core.Address; +import network.thunder.core.helper.ScriptTools; import org.bitcoinj.core.Context; import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutPoint; +import org.bitcoinj.crypto.TransactionSignature; import org.junit.Before; import org.junit.Test; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class LNPaymentLogicImplTest { @@ -31,17 +34,13 @@ public class LNPaymentLogicImplTest { ClientObject node1; ClientObject node2; - LNPaymentMessageFactory messageFactory1; - LNPaymentMessageFactory messageFactory2; - - LNPaymentLogic paymentLogic1; - LNPaymentLogic paymentLogic2; - - LNPaymentDBHandlerMock dbHandler1 = new LNPaymentDBHandlerMock(); - LNPaymentDBHandlerMock dbHandler2 = new LNPaymentDBHandlerMock(); + LNPaymentLogic paymentLogic; LNConfiguration configuration = new LNConfiguration(); + PaymentData sending = getPaymentData(true); + PaymentData receiving = getPaymentData(false); + @Before public void prepare () { Context.getOrCreate(Constants.getNetwork()); @@ -55,121 +54,288 @@ public void prepare () { node1.name = "LNPayment1"; node2.name = "LNPayment2"; - messageFactory1 = new LNPaymentMessageFactoryImpl(dbHandler1); - messageFactory2 = new LNPaymentMessageFactoryImpl(dbHandler2); - - channel1 = getMockChannel(); - channel2 = getMockChannel(); - -// channel1.channelStatus.applyConfiguration(configuration); -// channel2.channelStatus.applyConfiguration(configuration); + channel1 = TestTools.getMockChannel(configuration); + channel2 = TestTools.getMockChannel(configuration); channel1.retrieveDataFromOtherChannel(channel2); channel2.retrieveDataFromOtherChannel(channel1); - paymentLogic1 = new LNPaymentLogicImpl(messageFactory1, dbHandler1); - paymentLogic2 = new LNPaymentLogicImpl(messageFactory2, dbHandler2); - - paymentLogic1.initialise(channel1); - paymentLogic2.initialise(channel2); + paymentLogic = new LNPaymentLogicImpl(); } @Test - public void fullExchange () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - PaymentData paymentData = getMockPaymentData(); - QueueElementPayment elementPayment = new QueueElementPayment(paymentData); - - ChannelUpdate update = new ChannelUpdate(); - update.applyConfiguration(configuration); - update = elementPayment.produceNewChannelStatus(channel1.channelStatus, update, null); - - LNPayment messageA = paymentLogic1.getAMessage(update); - exchangeMessage(messageA, paymentLogic2); - - LNPayment messageB = paymentLogic2.getBMessage(); - exchangeMessage(messageB, paymentLogic1); - - LNPayment messageC1 = paymentLogic1.getCMessage(); - exchangeMessage(messageC1, paymentLogic2); + public void produceCorrectChannelTransactionNoPayments () { + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); + + assertEquals(2, channelTransaction.getOutputs().size()); + assertEquals(1, channelTransaction.getInputs().size()); + assertEquals(channel1.anchorTxHash, channelTransaction.getInput(0).getOutpoint().getHash()); + assertEquals(1, channelTransaction.getInput(0).getOutpoint().getIndex()); + + long expectedFee = channel1.channelStatus.feePerByte * (channelTransaction.getMessageSize() + 146) / 2; + + assertEquals(channel1.channelStatus.amountClient - channelTransaction.getOutput(0).getValue().value, expectedFee); + assertEquals(channel1.channelStatus.amountServer - channelTransaction.getOutput(1).getValue().value, expectedFee); + + assertEquals( + channelTransaction.getOutput(0).getScriptPubKey(), + ScriptTools.getChannelTxOutputRevocation( + channel1.channelStatus.revoHashServerCurrent, + channel1.keyServer, + channel1.keyClient, + channel1.channelStatus.csvDelay)); + + assertEquals( + channelTransaction.getOutput(0).getScriptPubKey(), + ScriptTools.getChannelTxOutputRevocation( + channel1.channelStatus.revoHashServerCurrent, + channel1.keyServer, + channel1.keyClient, + channel1.channelStatus.csvDelay)); + + assertEquals( + channelTransaction.getOutput(1).getAddressFromP2PKHScript(Constants.getNetwork()), + channel1.channelStatus.addressClient); + } - LNPayment messageC2 = paymentLogic2.getCMessage(); - exchangeMessage(messageC2, paymentLogic1); + @Test + public void produceCorrectChannelTransactionWithPayments () { + addPaymentsToChannel(); + + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); + + assertEquals(4, channelTransaction.getOutputs().size()); + + long expectedFee = channel1.channelStatus.feePerByte * (channelTransaction.getMessageSize() + 146) / 2; + + assertEquals(channel1.channelStatus.amountClient - channelTransaction.getOutput(0).getValue().value, expectedFee); + assertEquals(channel1.channelStatus.amountServer - channelTransaction.getOutput(1).getValue().value, expectedFee); + + assertEquals( + channelTransaction.getOutput(2).getScriptPubKey(), + ScriptTools.getChannelTxOutputPaymentSending( + channel1.keyServer, + channel1.keyClient, + channel1.channelStatus.revoHashServerCurrent, + sending.secret, + sending.timestampRefund)); + + assertEquals( + channelTransaction.getOutput(3).getScriptPubKey(), + ScriptTools.getChannelTxOutputPaymentReceiving( + channel1.keyServer, + channel1.keyClient, + channel1.channelStatus.revoHashServerCurrent, + receiving.secret, + receiving.timestampRefund)); + } - LNPayment messageD1 = paymentLogic1.getDMessage(); - exchangeMessage(messageD1, paymentLogic2); + @Test + public void produceCorrectPaymentTransactions () { + addPaymentsToChannel(); - LNPayment messageD2 = paymentLogic2.getDMessage(); - exchangeMessage(messageD2, paymentLogic1); - } + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); - @Test(expected = LNPaymentException.class) - public void partyASendsWrongSignatureOne () { - PaymentData paymentData = getMockPaymentData(); - QueueElementPayment elementPayment = new QueueElementPayment(paymentData); + List paymentTransactions = paymentLogic.getPaymentTransactions( + channelTransaction.getHash(), + channel1.channelStatus, + channel1.keyServer, + channel1.keyClient); - ChannelUpdate update = new ChannelUpdate(); - update.applyConfiguration(configuration); - update = elementPayment.produceNewChannelStatus(channel1.channelStatus, update, null); - LNPayment messageA = paymentLogic1.getAMessage(update); - exchangeMessage(messageA, paymentLogic2); + assertEquals(2, paymentTransactions.size()); - LNPaymentBMessage messageB = paymentLogic2.getBMessage(); - exchangeMessage(messageB, paymentLogic1); + assertEquals(channelTransaction.getHash(), paymentTransactions.get(0).getInput(0).getOutpoint().getHash()); + assertEquals(channelTransaction.getHash(), paymentTransactions.get(1).getInput(0).getOutpoint().getHash()); - LNPaymentCMessage messageC1 = paymentLogic1.getCMessage(); - messageC1.channelSignatures = Collections.singletonList(Tools.copyRandomByteInByteArray(messageC1.channelSignatures.get(0), 60, 2)); - exchangeMessage(messageC1, paymentLogic2); - } + assertEquals(2, paymentTransactions.get(0).getInput(0).getOutpoint().getIndex()); + assertEquals(3, paymentTransactions.get(1).getInput(0).getOutpoint().getIndex()); + assertEquals(1, paymentTransactions.get(0).getInputs().size()); + assertEquals(1, paymentTransactions.get(1).getInputs().size()); - @Test(expected = LNPaymentException.class) - public void partyBSendsWrongSignatureOne () { - PaymentData paymentData = getMockPaymentData(); - QueueElementPayment elementPayment = new QueueElementPayment(paymentData); + assertEquals(1, paymentTransactions.get(0).getOutputs().size()); + assertEquals(1, paymentTransactions.get(1).getOutputs().size()); - ChannelUpdate update = new ChannelUpdate(); - update.applyConfiguration(configuration); - update = elementPayment.produceNewChannelStatus(channel1.channelStatus, update, null); + assertEquals(sending.amount, paymentTransactions.get(0).getOutput(0).getValue().value); + assertEquals(receiving.amount, paymentTransactions.get(1).getOutput(0).getValue().value); - LNPayment messageA = paymentLogic1.getAMessage(update); - exchangeMessage(messageA, paymentLogic2); + assertEquals( + paymentTransactions.get(0).getOutput(0).getScriptPubKey(), + ScriptTools.getPaymentTxOutput( + channel1.keyServer, + channel1.keyClient, + channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.csvDelay)); - LNPaymentBMessage messageB = paymentLogic2.getBMessage(); - exchangeMessage(messageB, paymentLogic1); + assertEquals( + paymentTransactions.get(1).getOutput(0).getScriptPubKey(), + ScriptTools.getPaymentTxOutput( + channel1.keyServer, + channel1.keyClient, + channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.csvDelay)); - LNPaymentCMessage messageC1 = paymentLogic1.getCMessage(); - exchangeMessage(messageC1, paymentLogic2); - LNPaymentCMessage messageC2 = paymentLogic2.getCMessage(); - messageC2.channelSignatures = Collections.singletonList(Tools.copyRandomByteInByteArray(messageC2.channelSignatures.get(0), 60, 2)); - exchangeMessage(messageC1, paymentLogic1); } + @Test + public void produceCorrectSignatureObject () { + addPaymentsToChannel(); + + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); + + List paymentTransactions = paymentLogic.getPaymentTransactions( + channelTransaction.getHash(), + channel1.channelStatus, + channel1.keyServer, + channel1.keyClient); + + ChannelSignatures channelSignatures = paymentLogic.getSignatureObject(channel1, channelTransaction, paymentTransactions); + + assertEquals(1, channelSignatures.channelSignatures.size()); + assertEquals(2, channelSignatures.paymentSignatures.size()); + + Sha256Hash channelTxHashForSignature = channelTransaction.hashForSignature( + 0, + ScriptTools.getAnchorOutputScript(channel1.keyClient, channel1.keyServer), + Transaction.SigHash.ALL, + false); + + Sha256Hash paymentTxHashForSignature1 = paymentTransactions.get(0).hashForSignature( + 0, + channelTransaction.getOutput(2).getScriptBytes(), + Transaction.SigHash.ALL, + false); + + Sha256Hash paymentTxHashForSignature2 = paymentTransactions.get(1).hashForSignature( + 0, + channelTransaction.getOutput(3).getScriptBytes(), + Transaction.SigHash.ALL, + false); + + assertTrue(channel1.keyServer.verify(channelTxHashForSignature, channelSignatures.channelSignatures.get(0))); + assertTrue(channel1.keyServer.verify(paymentTxHashForSignature1, channelSignatures.paymentSignatures.get(0))); + assertTrue(channel1.keyServer.verify(paymentTxHashForSignature2, channelSignatures.paymentSignatures.get(1))); + } - private void exchangeMessage (LNPayment message, LNPaymentLogic receiver) { - receiver.checkMessageIncoming(message); + @Test + public void verifyCorrectSignatureObject () { + addPaymentsToChannel(); + + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); + + List paymentTransactions = paymentLogic.getPaymentTransactions( + channelTransaction.getHash(), + channel1.channelStatus, + channel1.keyServer, + channel1.keyClient); + + ChannelSignatures channelSignatures = paymentLogic.getSignatureObject(channel1, channelTransaction, paymentTransactions); + + paymentLogic.checkSignatures( + channel1.keyClient, + channel1.keyServer, + channelSignatures, + channelTransaction, + paymentTransactions, + channel1.channelStatus); } - private PaymentData getMockPaymentData () { - PaymentData paymentData = new PaymentData(); - paymentData.amount = 1; - paymentData.sending = true; - paymentData.secret = new PaymentSecret(Tools.getRandomByte(20)); - paymentData.timestampOpen = Tools.currentTime(); - paymentData.timestampRefund = Tools.currentTime() + 10 * configuration.DEFAULT_REFUND_DELAY * configuration.DEFAULT_OVERLAY_REFUND; - paymentData.csvDelay = configuration.DEFAULT_REVOCATION_DELAY; - return paymentData; + @Test(expected = LNPaymentException.class) + public void failIncorrectSignature1 () { + addPaymentsToChannel(); + + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); + + List paymentTransactions = paymentLogic.getPaymentTransactions( + channelTransaction.getHash(), + channel1.channelStatus, + channel1.keyServer, + channel1.keyClient); + + ChannelSignatures channelSignatures = paymentLogic.getSignatureObject(channel1, channelTransaction, paymentTransactions); + + TransactionSignature invalidSig = TestTools.corruptSignature( channelSignatures.channelSignatures.get(0)); + channelSignatures.channelSignatures.set(0, invalidSig); + + paymentLogic.checkSignatures( + channel1.keyClient, + channel1.keyServer, + channelSignatures, + channelTransaction, + paymentTransactions, + channel1.channelStatus); } - private static Channel getMockChannel() { - Channel channel = new Channel(); + @Test(expected = LNPaymentException.class) + public void failIncorrectSignature2 () { + addPaymentsToChannel(); + + Transaction channelTransaction = paymentLogic.getChannelTransaction( + new TransactionOutPoint(Constants.getNetwork(), 1, channel1.anchorTxHash), + channel1.channelStatus, + channel1.keyClient, + channel1.keyServer); + + List paymentTransactions = paymentLogic.getPaymentTransactions( + channelTransaction.getHash(), + channel1.channelStatus, + channel1.keyServer, + channel1.keyClient); + + ChannelSignatures channelSignatures = paymentLogic.getSignatureObject(channel1, channelTransaction, paymentTransactions); + + TransactionSignature invalidSig = TestTools.corruptSignature( channelSignatures.paymentSignatures.get(0)); + channelSignatures.paymentSignatures.set(0, invalidSig); + + paymentLogic.checkSignatures( + channel1.keyClient, + channel1.keyServer, + channelSignatures, + channelTransaction, + paymentTransactions, + channel1.channelStatus); + } - channel.anchorTxHash = Sha256Hash.wrap(Tools.getRandomByte(32)); - channel.channelStatus.addressServer = new Address(Constants.getNetwork(), Tools.getRandomByte(20)); - channel.channelStatus.amountClient = 10000; - channel.channelStatus.amountServer = 10000; + private static PaymentData getPaymentData (boolean sending) { + PaymentData payment = new PaymentData(); + payment.secret = new PaymentSecret(Tools.getRandomByte(20)); + payment.sending = sending; + payment.timestampOpen = Tools.currentTime(); + payment.amount = Tools.getRandom(900, 1100); + payment.timestampRefund = Tools.currentTime() + 60 * 60; + return payment; + } - return channel; + private void addPaymentsToChannel() { + channel1.channelStatus.paymentList.add(sending); + channel1.channelStatus.paymentList.add(receiving); + channel1.channelStatus.amountServer -= sending.amount; + channel1.channelStatus.amountClient -= receiving.amount; } } \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentRoutingTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentRoutingTest.java deleted file mode 100644 index 76fef776..00000000 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentRoutingTest.java +++ /dev/null @@ -1,333 +0,0 @@ -package network.thunder.core.communication.layers.high; - -import io.netty.channel.embedded.EmbeddedChannel; -import network.thunder.core.communication.layer.Message; -import network.thunder.core.communication.layer.ProcessorHandler; -import network.thunder.core.communication.layer.high.ChannelStatus; -import network.thunder.core.communication.layer.high.payments.PaymentData; -import network.thunder.core.communication.layer.high.payments.messages.OnionObject; -import network.thunder.core.communication.layer.ContextFactory; -import network.thunder.core.communication.layer.high.payments.messages.LNPaymentMessageFactory; -import network.thunder.core.communication.layer.high.payments.LNOnionHelper; -import network.thunder.core.communication.layer.high.payments.PaymentSecret; -import network.thunder.core.communication.layer.high.payments.LNPaymentProcessorImpl; -import network.thunder.core.communication.layer.high.payments.LNPaymentLogic; -import network.thunder.core.database.DBHandler; -import network.thunder.core.database.objects.PaymentWrapper; -import network.thunder.core.etc.*; -import network.thunder.core.communication.LNConfiguration; -import network.thunder.core.communication.ClientObject; -import network.thunder.core.communication.ServerObject; -import org.bitcoinj.core.ECKey; -import org.junit.Before; -import org.junit.Test; - -import java.beans.PropertyVetoException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import static junit.framework.TestCase.assertEquals; -import static org.junit.Assert.assertNull; - -public class LNPaymentRoutingTest { - - public static final int TIME_TO_WAIT_FOR_FULL_EXCHANGE = 600; - EmbeddedChannel channel12; - EmbeddedChannel channel21; - EmbeddedChannel channel23; - EmbeddedChannel channel32; - - ServerObject node1 = new ServerObject(); - ServerObject node2 = new ServerObject(); - ServerObject node3 = new ServerObject(); - - ClientObject node12 = new ClientObject(node1); - ClientObject node21 = new ClientObject(node2); - ClientObject node23 = new ClientObject(node2); - ClientObject node32 = new ClientObject(node3); - - LNPaymentDBHandlerMock dbHandler1 = new LNPaymentDBHandlerMock(); - LNPaymentDBHandlerMock dbHandler2 = new LNPaymentDBHandlerMock(); - LNPaymentDBHandlerMock dbHandler3 = new LNPaymentDBHandlerMock(); - - ContextFactory contextFactory1 = new MockLNPaymentContextFactory(node1, dbHandler1); - ContextFactory contextFactory2 = new MockLNPaymentContextFactory(node2, dbHandler2); - ContextFactory contextFactory3 = new MockLNPaymentContextFactory(node3, dbHandler3); - - LNPaymentProcessorImpl processor12; - LNPaymentProcessorImpl processor21; - LNPaymentProcessorImpl processor23; - LNPaymentProcessorImpl processor32; - - LNConfiguration configuration = new LNConfiguration(); - - @Before - public void prepare () throws PropertyVetoException, SQLException { - node12.name = "LNPayment12"; - node21.name = "LNPayment21"; - node23.name = "LNPayment23"; - node32.name = "LNPayment32"; - - node12.pubKeyClient = node2.pubKeyServer; - node21.pubKeyClient = node1.pubKeyServer; - node23.pubKeyClient = node3.pubKeyServer; - node32.pubKeyClient = node2.pubKeyServer; - - processor12 = new LNPaymentProcessorImpl(contextFactory1, dbHandler1, node12); - processor21 = new LNPaymentProcessorImpl(contextFactory2, dbHandler2, node21); - processor23 = new LNPaymentProcessorImpl(contextFactory2, dbHandler2, node23); - processor32 = new LNPaymentProcessorImpl(contextFactory3, dbHandler3, node32); - - channel12 = new EmbeddedChannel(new ProcessorHandler(processor12, "LNPayment12")); - channel21 = new EmbeddedChannel(new ProcessorHandler(processor21, "LNPayment21")); - channel23 = new EmbeddedChannel(new ProcessorHandler(processor23, "LNPayment23")); - channel32 = new EmbeddedChannel(new ProcessorHandler(processor32, "LNPayment32")); - - Message m = (Message) channel21.readOutbound(); - assertNull(m); - - } - - public void after () { - channel12.checkException(); - channel21.checkException(); - - channel21.checkException(); - channel23.checkException(); - - channel32.checkException(); - } - - @Test - public void exchangePaymentWithRouting () throws InterruptedException { - OnionObject onionObject = getOnionObject(contextFactory1.getOnionHelper()); - PaymentData paymentData = getMockPaymentData(); - PaymentWrapper wrapper = new PaymentWrapper(new byte[0], paymentData); - - dbHandler1.addPayment(wrapper); - dbHandler3.addPaymentSecret(paymentData.secret); - - paymentData.secret.secret = null; - - paymentData.onionObject = onionObject; - processor12.makePayment(paymentData); - - connectChannel(channel12, channel21); - connectChannel(channel23, channel32); - - Thread.sleep(TIME_TO_WAIT_FOR_FULL_EXCHANGE); - - ChannelStatus status12 = processor12.getStatusTemp(); - ChannelStatus status21 = processor21.getStatusTemp(); - ChannelStatus status23 = processor23.getStatusTemp(); - ChannelStatus status32 = processor21.getStatusTemp(); - - assertEquals(status12.amountClient, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL + paymentData.amount); - assertEquals(status12.amountServer, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL - paymentData.amount); - - assertEquals(status21.amountClient, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL - paymentData.amount); - assertEquals(status21.amountServer, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL + paymentData.amount); - - assertEquals(status23.amountClient, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL + paymentData.amount); - assertEquals(status23.amountServer, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL - paymentData.amount); - - assertEquals(status32.amountClient, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL - paymentData.amount); - assertEquals(status32.amountServer, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL + paymentData.amount); - after(); - } - - @Test - public void exchangePaymentWithRoutingRefund () throws InterruptedException { - OnionObject onionObject = getOnionObject(contextFactory1.getOnionHelper()); - PaymentData paymentData = getMockPaymentData(); - PaymentWrapper wrapper = new PaymentWrapper(new byte[0], paymentData); - - dbHandler1.addPayment(wrapper); - - paymentData.secret.secret = null; - - paymentData.onionObject = onionObject; - processor12.makePayment(paymentData); - - connectChannel(channel12, channel21); - connectChannel(channel23, channel32); - - Thread.sleep(TIME_TO_WAIT_FOR_FULL_EXCHANGE); - testUnchangedChannelAmounts(); - after(); - } - - @Test - public void exchangePaymentWithRoutingCorruptedOnionObject () throws InterruptedException { - OnionObject onionObject = getOnionObject(contextFactory1.getOnionHelper()); - - Tools.copyRandomByteInByteArray(onionObject.data, 100, 2); - - PaymentData paymentData = getMockPaymentData(); - PaymentWrapper wrapper = new PaymentWrapper(new byte[0], paymentData); - - dbHandler1.addPayment(wrapper); - - paymentData.secret.secret = null; - - paymentData.onionObject = onionObject; - processor12.makePayment(paymentData); - - connectChannel(channel12, channel21); - connectChannel(channel23, channel32); - - Thread.sleep(TIME_TO_WAIT_FOR_FULL_EXCHANGE); - testUnchangedChannelAmounts(); - after(); - } - - @Test - public void exchangePaymentWithRoutingToUnknownNode () throws InterruptedException { - OnionObject onionObject = getOnionObject(contextFactory1.getOnionHelper(), node12.pubKeyClient.getPubKey(), new ECKey().getPubKey()); - - PaymentData paymentData = getMockPaymentData(); - PaymentWrapper wrapper = new PaymentWrapper(new byte[0], paymentData); - - dbHandler1.addPayment(wrapper); - - paymentData.secret.secret = null; - - paymentData.onionObject = onionObject; - processor12.makePayment(paymentData); - - connectChannel(channel12, channel21); - connectChannel(channel23, channel32); - - Thread.sleep(TIME_TO_WAIT_FOR_FULL_EXCHANGE); - testUnchangedChannelAmounts(); - after(); - } - - @Test - public void exchangePaymentWithTooHighPayment () throws InterruptedException { - long normalAmount = LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL; - long doubleAmount = normalAmount * 2; - ChannelStatus status12 = processor12.getChannel().channelStatus; - status12.amountClient *= 2; - status12.amountServer *= 2; - - ChannelStatus status21 = processor21.getChannel().channelStatus; - status21.amountClient *= 2; - status21.amountServer *= 2; - - OnionObject onionObject = getOnionObject(contextFactory1.getOnionHelper()); - - PaymentData paymentData = getMockPaymentData(); - paymentData.amount = LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL + 1; - PaymentWrapper wrapper = new PaymentWrapper(new byte[0], paymentData); - - dbHandler1.addPayment(wrapper); - - paymentData.secret.secret = null; - - paymentData.onionObject = onionObject; - processor12.makePayment(paymentData); - - connectChannel(channel12, channel21); - connectChannel(channel23, channel32); - - Thread.sleep(TIME_TO_WAIT_FOR_FULL_EXCHANGE); - - testUnchangedChannelAmounts(doubleAmount, doubleAmount, normalAmount, normalAmount); - after(); - } - - public void testUnchangedChannelAmounts () { - testUnchangedChannelAmounts(LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL, - LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL, LNPaymentDBHandlerMock.INITIAL_AMOUNT_CHANNEL); - } - - public void testUnchangedChannelAmounts (long amount12, long amount21, long amount23, long amount32) { - ChannelStatus status12 = processor12.getStatusTemp(); - ChannelStatus status21 = processor21.getStatusTemp(); - ChannelStatus status23 = processor23.getStatusTemp(); - ChannelStatus status32 = processor32.getStatusTemp(); - - assertEquals(status12.amountClient, amount12); - assertEquals(status12.amountServer, amount12); - - assertEquals(status21.amountClient, amount21); - assertEquals(status21.amountServer, amount21); - - assertEquals(status23.amountClient, amount23); - assertEquals(status23.amountServer, amount23); - - assertEquals(status32.amountClient, amount32); - assertEquals(status32.amountServer, amount32); - } - - public static void exchangeMessages (EmbeddedChannel from, EmbeddedChannel to) { - Object message = from.readOutbound(); - if (message != null) { - to.writeInbound(message); - } - } - - public static void exchangeMessagesDuplex (EmbeddedChannel from, EmbeddedChannel to) { - exchangeMessages(from, to); - exchangeMessages(to, from); - } - - public OnionObject getOnionObject (LNOnionHelper onionHelper) { - return getOnionObject(onionHelper, node12.pubKeyClient.getPubKey(), node23.pubKeyClient.getPubKey()); - } - - public OnionObject getOnionObject (LNOnionHelper onionHelper, byte[] node2, byte[] node3) { - List route = new ArrayList<>(); - route.add(node2); - route.add(node3); - return onionHelper.createOnionObject(route, null); - } - - public PaymentData getMockPaymentData () { - PaymentData paymentData = new PaymentData(); - paymentData.sending = true; - paymentData.amount = 10000; - paymentData.secret = new PaymentSecret(Tools.getRandomByte(20)); - - paymentData.timestampOpen = Tools.currentTime(); - paymentData.timestampRefund = Tools.currentTime() + 3 - * configuration.MAX_REFUND_DELAY * configuration.MAX_OVERLAY_REFUND; - paymentData.csvDelay = configuration.DEFAULT_REVOCATION_DELAY; - - return paymentData; - } - - public void connectChannel (EmbeddedChannel from, EmbeddedChannel to) { - new Thread(() -> { - while (true) { - exchangeMessagesDuplex(from, to); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - } - }).start(); - } - - class MockLNPaymentContextFactory extends MockContextFactory { - - public MockLNPaymentContextFactory (ServerObject node, DBHandler dbHandler) { - super(node, dbHandler); - } - - @Override - public LNPaymentLogic getLNPaymentLogic () { - return new MockLNPaymentLogic(getLNPaymentMessageFactory()); - } - - @Override - public LNPaymentMessageFactory getLNPaymentMessageFactory () { - return new LNPaymentMessageFactoryMock(); - } - } - -} \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AckProcessorImplTest.kt b/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AckProcessorImplTest.kt new file mode 100644 index 00000000..b0e72aef --- /dev/null +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AckProcessorImplTest.kt @@ -0,0 +1,116 @@ +package network.thunder.core.communication.layers.low + +import io.netty.channel.embedded.EmbeddedChannel +import network.thunder.core.communication.ClientObject +import network.thunder.core.communication.ServerObject +import network.thunder.core.communication.layer.ContextFactoryImpl +import network.thunder.core.communication.layer.DIRECTION +import network.thunder.core.communication.layer.ProcessorHandler +import network.thunder.core.communication.layer.high.AckMessageImpl +import network.thunder.core.communication.layer.high.AckableMessage +import network.thunder.core.communication.layer.low.ack.AckProcessorImpl +import network.thunder.core.database.DBHandler +import network.thunder.core.database.InMemoryDBHandler +import network.thunder.core.etc.Constants +import network.thunder.core.helper.events.LNEventHelperImpl +import network.thunder.core.helper.wallet.MockWallet +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test + +class AckProcessorImplTest { + + val RESEND_TIME = 100L + + val serverObject1 = ServerObject() + val serverObject2 = ServerObject() + + val node1 = ClientObject(serverObject2) + val node2 = ClientObject(serverObject1) + + val dbHandler1: DBHandler = InMemoryDBHandler() + val dbHandler2: DBHandler = InMemoryDBHandler() + + val contextFactory1 = ContextFactoryImpl(serverObject1, dbHandler1, MockWallet(Constants.getNetwork()), LNEventHelperImpl()); + val contextFactory2 = ContextFactoryImpl(serverObject2, dbHandler2, MockWallet(Constants.getNetwork()), LNEventHelperImpl()); + + val processor1 = AckProcessorImpl(contextFactory1, dbHandler1, node2) + val processor2 = AckProcessorImpl(contextFactory2, dbHandler2, node1) + + lateinit var channel1: EmbeddedChannel + lateinit var channel2: EmbeddedChannel + + @Before + fun prepare() { + Constants.MESSAGE_RESEND_TIME = RESEND_TIME + + channel1 = EmbeddedChannel(ProcessorHandler(processor1, "AckProcessor1")) + channel2 = EmbeddedChannel(ProcessorHandler(processor2, "AckProcessor2")) + + assertNull(channel1.readOutbound()); + assertNull(channel2.readOutbound()); + } + + + @Test + fun shouldNotSendMessageBecauseTimeoutNotReachedYet() { + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(1), DIRECTION.SENT) + Thread.sleep((Constants.MESSAGE_RESEND_TIME / 2).toLong()) + assertNull(channel1.readOutbound()); + } + + @Test + fun shouldResendAllMessageAfterTimeout() { + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(1), DIRECTION.SENT) + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(2), DIRECTION.SENT) + Thread.sleep((Constants.MESSAGE_RESEND_TIME * 1.5).toLong()) + assertEquals(1L, (channel1.readOutbound() as AckableMessage).getMessageNumber()); + assertEquals(2L, (channel1.readOutbound() as AckableMessage).getMessageNumber()); + } + + @Test + fun shouldResendUnackedMessageAfterTimeout() { + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(1), DIRECTION.SENT) + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(2), DIRECTION.SENT) + channel1.writeInbound(AckMessageImpl(1)) + println(1) + println(" aa " + Constants.MESSAGE_RESEND_TIME) + Thread.sleep((Constants.MESSAGE_RESEND_TIME * 1.1).toLong()) + println(2) + assertEquals(2L, (channel1.readOutbound() as AckableMessage).getMessageNumber()); + assertNull(channel1.readOutbound()) + } + + @Test + fun shouldResendNoMessageAfterTimeout() { + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(1), DIRECTION.SENT) + dbHandler1.saveMessage(node2.nodeKey, AckableMessageMock(2), DIRECTION.SENT) + channel1.writeInbound(AckMessageImpl(1)) + channel1.writeInbound(AckMessageImpl(2)) + Thread.sleep((Constants.MESSAGE_RESEND_TIME * 1.5).toLong()) + assertNull(channel1.readOutbound()) + } + + @Test + fun shouldNotSendMessageBecauseNothingSentYet() { + Thread.sleep(200) + assertNull(channel1.readOutbound()); + } + +} + +class AckableMessageMock(var number: Long = 1L) : AckableMessage() { + + override fun verify() { + } + + override fun setMessageNumber(number: Long) { + this.number = number + } + + override fun getMessageNumber(): Long { + return number + } + +} \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AuthenticationHandlerTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AuthenticationHandlerTest.java index d8127ab0..4478c762 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AuthenticationHandlerTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/low/AuthenticationHandlerTest.java @@ -1,18 +1,15 @@ package network.thunder.core.communication.layers.low; import io.netty.channel.embedded.EmbeddedChannel; +import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.ServerObject; +import network.thunder.core.communication.layer.ContextFactory; import network.thunder.core.communication.layer.Message; import network.thunder.core.communication.layer.ProcessorHandler; import network.thunder.core.communication.layer.low.authentication.messages.AuthenticationMessage; -import network.thunder.core.communication.layer.ContextFactory; -import network.thunder.core.helper.events.LNEventHelper; -import network.thunder.core.communication.layer.FailureMessage; import network.thunder.core.etc.MockContextFactory; -import network.thunder.core.etc.MockLNEventHelper; import network.thunder.core.etc.RandomDataMessage; import network.thunder.core.helper.crypto.ECDH; -import network.thunder.core.communication.ClientObject; -import network.thunder.core.communication.ServerObject; import org.junit.Before; import org.junit.Test; @@ -39,8 +36,6 @@ public class AuthenticationHandlerTest { ContextFactory contextFactory1; ContextFactory contextFactory2; - LNEventHelper eventHelper = new MockLNEventHelper(); - @Before public void prepare () throws PropertyVetoException, SQLException { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); @@ -116,7 +111,7 @@ public void testAuthenticationHandshake () throws NoSuchProviderException, NoSuc } @Test - public void shouldNotAllowMessagePassthrough () throws NoSuchProviderException, NoSuchAlgorithmException { + public void shouldNotAllowMessagePassthroughIn () throws NoSuchProviderException, NoSuchAlgorithmException { channel1.readOutbound(); assertNull(channel1.readOutbound()); @@ -129,14 +124,28 @@ public void shouldNotAllowMessagePassthrough () throws NoSuchProviderException, channel1.writeInbound(randomDataMessage1); channel2.writeInbound(randomDataMessage2); - assertNull(channel1.readInbound()); - assertNull(channel2.readInbound()); + assertFalse(channel1.isOpen()); + assertFalse(channel2.isOpen()); + + after(); + } + + @Test + public void shouldNotAllowMessagePassthroughOut () throws NoSuchProviderException, NoSuchAlgorithmException { + channel1.readOutbound(); + + assertNull(channel1.readOutbound()); + assertNull(channel2.readOutbound()); + RandomDataMessage randomDataMessage1 = new RandomDataMessage(); + RandomDataMessage randomDataMessage2 = new RandomDataMessage(); + + //Should allow both directions now.. channel1.writeOutbound(randomDataMessage1); channel2.writeOutbound(randomDataMessage2); - assertTrue(channel1.readOutbound() instanceof FailureMessage); - assertTrue(channel2.readOutbound() instanceof FailureMessage); + assertFalse(channel1.isOpen()); + assertFalse(channel2.isOpen()); after(); } diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/mid/SyncHandlerTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/mid/SyncHandlerTest.java index 8c83cbba..7c0de04d 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/mid/SyncHandlerTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/mid/SyncHandlerTest.java @@ -86,8 +86,6 @@ public void testSyncingDataObjects () throws InterruptedException { assertNull(channel1.readOutbound()); assertNull(channel2.readOutbound()); -// syncStructure1.saveFullSyncToDatabase(); - assertTrue(checkListsEqual(dbHandler1.channelStatusObjectArrayList, dbHandler2.channelStatusObjectArrayList)); assertTrue(checkListsEqual(dbHandler1.pubkeyChannelObjectArrayList, dbHandler2.pubkeyChannelObjectArrayList)); assertTrue(checkListsEqual(dbHandler1.pubkeyIPObjectArrayList, dbHandler2.pubkeyIPObjectArrayList)); diff --git a/thunder-core/src/test/java/network/thunder/core/etc/ConnectionManagerWrapper.java b/thunder-core/src/test/java/network/thunder/core/etc/ConnectionManagerWrapper.java deleted file mode 100644 index fb0387e5..00000000 --- a/thunder-core/src/test/java/network/thunder/core/etc/ConnectionManagerWrapper.java +++ /dev/null @@ -1,13 +0,0 @@ -package network.thunder.core.etc; - -import network.thunder.core.communication.ConnectionManager; -import network.thunder.core.communication.layer.ContextFactory; -import network.thunder.core.database.DBHandler; -import org.bitcoinj.core.Wallet; - -public class ConnectionManagerWrapper { - public ContextFactory contextFactory; - public Wallet wallet; - public ConnectionManager connectionManager; - public DBHandler dbHandler; -} diff --git a/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java b/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java index 7ee6fb5d..dd8e3821 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java @@ -1,8 +1,15 @@ package network.thunder.core.etc; +import network.thunder.core.communication.NodeKey; +import network.thunder.core.communication.layer.DIRECTION; +import network.thunder.core.communication.layer.MessageWrapper; +import network.thunder.core.communication.layer.high.AckableMessage; import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.layer.high.NumberedMessage; import network.thunder.core.communication.layer.high.RevocationHash; +import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.high.payments.PaymentSecret; +import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; import network.thunder.core.communication.layer.middle.broadcasting.types.ChannelStatusObject; import network.thunder.core.communication.layer.middle.broadcasting.types.P2PDataObject; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; @@ -10,70 +17,94 @@ import network.thunder.core.database.objects.PaymentWrapper; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; +import org.jetbrains.annotations.NotNull; import java.util.List; public class DBHandlerMock implements DBHandler { @Override - public List getSyncDataByFragmentIndex (int fragmentIndex) { + public List getMessageList (NodeKey nodeKey, Sha256Hash channelHash, Class c) { return null; } @Override - public List getSyncDataIPObjects () { + public List getUnackedMessageList (NodeKey nodeKey) { return null; } @Override - public void insertIPObjects (List ipList) { + public AckableMessage getMessageResponse (NodeKey nodeKey, long messageIdReceived) { + return null; + } + + @Override + public void setMessageAcked (NodeKey nodeKey, long messageId) { } @Override - public List getIPObjects () { - return null; + public void setMessageProcessed (NodeKey nodeKey, NumberedMessage message) { + } + @Override - public P2PDataObject getP2PDataObjectByHash (byte[] hash) { - return null; + public long lastProcessedMessaged (NodeKey nodeKey) { + return 0; } @Override - public PubkeyIPObject getIPObject (byte[] nodeKey) { - return null; + public long saveMessage (NodeKey nodeKey, NumberedMessage message, DIRECTION direction) { + return 0; } + @Override - public void invalidateP2PObject (P2PDataObject ipObject) { + public void linkResponse (NodeKey nodeKey, long messageRequest, long messageResponse) { } @Override - public void syncDatalist (List dataList) { + public List getSyncDataByFragmentIndex (int fragmentIndex) { + return null; + } + @Override + public List getSyncDataIPObjects () { + return null; } @Override - public void insertRevocationHash (RevocationHash hash) { + public void insertIPObjects (List ipList) { } @Override - public RevocationHash createRevocationHash (Channel channel) { + public List getIPObjects () { return null; } @Override - public List getOldRevocationHashes (Channel channel) { + public P2PDataObject getP2PDataObjectByHash (byte[] hash) { return null; } @Override - public boolean checkOldRevocationHashes (List revocationHashList) { - return true; + public PubkeyIPObject getIPObject (byte[] nodeKey) { + return null; + } + + @Override + public void invalidateP2PObject (P2PDataObject ipObject) { + } + @Override + public void syncDatalist (List dataList) { + + } + + @Override public Channel getChannel (int id) { return null; @@ -85,30 +116,31 @@ public Channel getChannel (Sha256Hash hash) { } @Override - public List getChannel (ECKey nodeKey) { + public List getChannel (NodeKey nodeKey) { return null; } @Override - public List getOpenChannel (ECKey nodeKey) { + public List getOpenChannel (NodeKey nodeKey) { return null; } @Override - public int saveChannel (Channel channel) { - return 0; + public List getOpenChannel () { + return null; } @Override - public void updateChannel (Channel channel) { + public void insertChannel (Channel channel) { } @Override - public List getOpenChannel () { - return null; + public void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, Channel channel, ChannelUpdate update, RevocationHash revocationHash, NumberedMessage request, NumberedMessage response) { + } + @Override public List getIPObjectsWithActiveChannel () { return null; @@ -120,37 +152,42 @@ public List getTopology () { } @Override - public byte[] getSenderOfPayment (PaymentSecret paymentSecret) { - return new byte[0]; + public List lockPaymentsToBeRefunded (NodeKey nodeKey) { + return null; } @Override - public byte[] getReceiverOfPayment (PaymentSecret paymentSecret) { - return new byte[0]; + public List lockPaymentsToBeMade (NodeKey nodeKey) { + return null; } @Override - public void addPayment (PaymentWrapper paymentWrapper) { - + public List lockPaymentsToBeRedeemed (NodeKey nodeKey) { + return null; } @Override - public void updatePayment (PaymentWrapper paymentWrapper) { + public void checkPaymentsList () { } @Override - public void updatePaymentSender (PaymentWrapper paymentWrapper) { + public void unlockPayments (NodeKey nodeKey, List paymentList) { } @Override - public void updatePaymentReceiver (PaymentWrapper paymentWrapper) { + public NodeKey getSenderOfPayment (PaymentSecret paymentSecret) { + return null; + } + + @Override + public void addPayment (NodeKey firstHop, PaymentData paymentWrapper) { } @Override - public void updatePaymentAddReceiverAddress (PaymentSecret secret, byte[] receiver) { + public void updatePayment (PaymentWrapper paymentWrapper) { } diff --git a/thunder-core/src/test/java/network/thunder/core/etc/HashDerivationTest.java b/thunder-core/src/test/java/network/thunder/core/etc/HashDerivationTest.java deleted file mode 100644 index 701f9372..00000000 --- a/thunder-core/src/test/java/network/thunder/core/etc/HashDerivationTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package network.thunder.core.etc; - -import network.thunder.core.communication.layer.high.RevocationHash; -import network.thunder.core.helper.HashDerivation; -import org.junit.Test; - -import java.util.Arrays; - -import static org.junit.Assert.*; - -public class HashDerivationTest { - - @Test - public void shouldCalculateCorrectMasterSeed () { - //Let's start with a given seed - //Values below are calculated using http://www.fileformat.info/tool/hash.htm?hex=010649653115246623551233652115421336a8b6 - byte[] seed = Tools.hexStringToByteArray("010649653115246623551233652115421336a8b6"); - - RevocationHash revocationHash = HashDerivation.calculateRevocationHash(seed, 1, 0); - - assertEquals(revocationHash.getChild(), 0); - assertEquals(revocationHash.getDepth(), 1); - assertTrue(Arrays.equals(revocationHash.getSecret(), Tools.hexStringToByteArray("087d3e3f876d41fe243ca517809aa4a662eccba2"))); - assertNull(revocationHash.getSecretHash()); - } - - @Test - public void shouldCalculateCorrectRevocationHash () { - //Let's start with a given seed - //Values below are calculated using http://www.fileformat.info/tool/hash.htm?hex=010649653115246623551233652115421336a8b6 - byte[] seed = Tools.hexStringToByteArray("010649653115246623551233652115421336a8b6"); - - RevocationHash revocationHash = HashDerivation.calculateRevocationHash(seed, 1, 1); - - assertEquals(revocationHash.getChild(), 1); - assertEquals(revocationHash.getDepth(), 1); - assertTrue(Arrays.equals(revocationHash.getSecret(), Tools.hexStringToByteArray("7f66cee7aea711cc30f0ea9fd0612d8e63c53beb"))); - assertTrue(Arrays.equals(revocationHash.getSecretHash(), Tools.hexStringToByteArray("e7e24faa81ef3ae383464031cba965a05f6f4aa6"))); - - } - - @Test - public void shouldFindCorrectSecretWithBruteForce () { - byte[] seed = Tools.hexStringToByteArray("010649653115246623551233652115421336a8b6"); - - RevocationHash masterHash = HashDerivation.calculateRevocationHash(seed, 0, 0); - - RevocationHash targetHash = HashDerivation.calculateRevocationHash(seed, 250, 250); - - RevocationHash foundHash = HashDerivation.bruteForceHash(masterHash.getSecret(), targetHash.getSecretHash(), 500, 500); - - assertEquals(foundHash, targetHash); - } - - @Test - public void shouldTimeoutWhileTryingToBruteForce () { - byte[] seed = Tools.hexStringToByteArray("010649653115246623551233652115421336a8b6"); - - RevocationHash masterHash = HashDerivation.calculateRevocationHash(seed, 0, 0); - - RevocationHash targetHash = HashDerivation.calculateRevocationHash(seed, 250, 250); - - RevocationHash foundHash = HashDerivation.bruteForceHash(masterHash.getSecret(), targetHash.getSecretHash(), 100, 100); - - assertNull(foundHash); - } -} \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentDBHandlerMock.java b/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentDBHandlerMock.java index 7827c62c..5859af2d 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentDBHandlerMock.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentDBHandlerMock.java @@ -1,20 +1,18 @@ package network.thunder.core.etc; -import network.thunder.core.communication.layer.high.ChannelStatus; +import network.thunder.core.communication.LNConfiguration; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.Channel; -import network.thunder.core.communication.layer.high.RevocationHash; -import network.thunder.core.communication.layer.high.payments.PaymentSecret; -import network.thunder.core.database.objects.PaymentWrapper; -import org.bitcoinj.core.ECKey; +import network.thunder.core.communication.layer.high.ChannelStatus; +import network.thunder.core.database.InMemoryDBHandler; import java.util.ArrayList; import java.util.List; -import java.util.Random; -public class LNPaymentDBHandlerMock extends DBHandlerMock { +public class LNPaymentDBHandlerMock extends InMemoryDBHandler { public static final long INITIAL_AMOUNT_CHANNEL = 10000000; - List payments = new ArrayList<>(); - List secrets = new ArrayList<>(); + + LNConfiguration configuration = new LNConfiguration(); @Override public Channel getChannel (int id) { @@ -27,128 +25,16 @@ public Channel getChannel (int id) { } @Override - public List getChannel (ECKey nodeKey) { + public List getChannel (NodeKey nodeKey) { List list = new ArrayList<>(); Channel c = getChannel(1); - c.nodeKeyClient = nodeKey.getPubKey(); + c.nodeKeyClient = nodeKey; list.add(c); return list; } @Override - public List getOpenChannel (ECKey nodeKey) { + public List getOpenChannel (NodeKey nodeKey) { return getChannel(nodeKey); } - - @Override - public void addPayment (PaymentWrapper paymentWrapper) { - if (payments.contains(paymentWrapper)) { - throw new RuntimeException("Double payment added?"); - } - payments.add(paymentWrapper); - } - - @Override - public void updatePayment (PaymentWrapper paymentWrapper) { - for (PaymentWrapper p : payments) { - if (p.equals(paymentWrapper)) { - p.paymentData = paymentWrapper.paymentData; - p.receiver = paymentWrapper.receiver; - p.sender = paymentWrapper.sender; - p.statusReceiver = paymentWrapper.statusReceiver; - p.statusSender = paymentWrapper.statusSender; - } - } - } - - @Override - public void updatePaymentSender (PaymentWrapper paymentWrapper) { - for (PaymentWrapper p : payments) { - if (p.equals(paymentWrapper)) { - p.paymentData = paymentWrapper.paymentData; - p.statusSender = paymentWrapper.statusSender; - } - } - } - - @Override - public void updatePaymentReceiver (PaymentWrapper paymentWrapper) { - for (PaymentWrapper p : payments) { - if (p.equals(paymentWrapper)) { - p.paymentData = paymentWrapper.paymentData; - p.statusReceiver = paymentWrapper.statusReceiver; - } - } - } - - @Override - public void updatePaymentAddReceiverAddress (PaymentSecret secret, byte[] receiver) { - for (PaymentWrapper p : payments) { - if (p.paymentData.secret.equals(secret)) { - p.receiver = receiver; - } - } - } - - @Override - public PaymentWrapper getPayment (PaymentSecret paymentSecret) { - for (PaymentWrapper payment : payments) { - if (payment.paymentData.secret.equals(paymentSecret)) { - return payment; - } - } - return null; - } - - @Override - public void addPaymentSecret (PaymentSecret secret) { - if (secrets.contains(secret)) { - PaymentSecret oldSecret = secrets.get(secrets.indexOf(secret)); - oldSecret.secret = secret.secret; - } else { - secrets.add(secret); - } - } - - @Override - public PaymentSecret getPaymentSecret (PaymentSecret secret) { - if (!secrets.contains(secret)) { - return null; - } - return secrets.get(secrets.indexOf(secret)); - } - - @Override - public byte[] getSenderOfPayment (PaymentSecret paymentSecret) { - for (PaymentWrapper payment : payments) { - if (payment.paymentData.secret.equals(paymentSecret)) { - return payment.sender; - } - } - return null; - } - - @Override - public byte[] getReceiverOfPayment (PaymentSecret paymentSecret) { - for (PaymentWrapper payment : payments) { - if (payment.paymentData.secret.equals(paymentSecret)) { - return payment.receiver; - } - } - return null; - } - - @Override - public RevocationHash createRevocationHash (Channel channel) { - byte[] secret = new byte[20]; - new Random().nextBytes(secret); - byte[] secretHash = Tools.hashSecret(secret); - RevocationHash hash = new RevocationHash(1, 1, secret, secretHash); - return hash; - } - - @Override - public List getOldRevocationHashes (Channel channel) { - return new ArrayList<>(); - } } diff --git a/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentMessageFactoryMock.java b/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentMessageFactoryMock.java deleted file mode 100644 index fb8b02fe..00000000 --- a/thunder-core/src/test/java/network/thunder/core/etc/LNPaymentMessageFactoryMock.java +++ /dev/null @@ -1,61 +0,0 @@ -package network.thunder.core.etc; - -import network.thunder.core.communication.layer.MesssageFactoryImpl; -import network.thunder.core.communication.layer.high.Channel; -import network.thunder.core.communication.layer.high.RevocationHash; -import network.thunder.core.communication.layer.high.channel.ChannelSignatures; -import network.thunder.core.communication.layer.high.payments.messages.*; -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.crypto.TransactionSignature; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Random; - -public class LNPaymentMessageFactoryMock extends MesssageFactoryImpl implements LNPaymentMessageFactory { - Random random = new Random(); - - @Override - public LNPaymentAMessage getMessageA (Channel channel, ChannelUpdate statusTemp) { - if (statusTemp == null) { - statusTemp = new ChannelUpdate(); - } - return new LNPaymentAMessage(statusTemp, getMockRevocationHash()); - } - - @Override - public LNPaymentBMessage getMessageB (Channel channel) { - return new LNPaymentBMessage(getMockRevocationHash()); - } - - @Override - public LNPaymentCMessage getMessageC (Channel channel, List channelSignatures, List paymentSignatures) { - return new LNPaymentCMessage(new ChannelSignatures(getMockSig(), getMockSig())); - } - - @Override - public LNPaymentDMessage getMessageD (Channel channel) { - return new LNPaymentDMessage(new ArrayList<>()); - } - - private RevocationHash getMockRevocationHash () { - byte[] secret = new byte[20]; - random.nextBytes(secret); - byte[] hash = Tools.hashSecret(secret); - RevocationHash revocationHash = new RevocationHash(1, 1, secret, hash); - return revocationHash; - } - - private List getMockSig () { - ECKey k = new ECKey(); - Transaction t = new Transaction(Constants.getNetwork()); - t.addInput(Sha256Hash.ZERO_HASH, 0, Tools.getDummyScript()); - t.addOutput(Coin.ZERO, Tools.getDummyScript()); - - return Collections.singletonList(Tools.getSignature(t, 0, t.getOutput(0),k)); - } -} diff --git a/thunder-core/src/test/java/network/thunder/core/etc/MockLNEventHelper.java b/thunder-core/src/test/java/network/thunder/core/etc/MockLNEventHelper.java index 10ce77e4..980c577d 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/MockLNEventHelper.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/MockLNEventHelper.java @@ -1,6 +1,7 @@ package network.thunder.core.etc; import network.thunder.core.communication.ClientObject; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.middle.broadcasting.types.PubkeyIPObject; @@ -55,7 +56,12 @@ public void onPaymentRefunded (PaymentData payment) { } @Override - public void onPaymentCompleted (PaymentData payment) { + public void onPaymentRedeemed (PaymentData payment) { + + } + + @Override + public void onPaymentAdded (NodeKey nodeKey, PaymentData payment) { } diff --git a/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentHelper.java b/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentHelper.java index 5e099e02..b29622ab 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentHelper.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentHelper.java @@ -1,23 +1,19 @@ package network.thunder.core.etc; -import network.thunder.core.communication.layer.high.payments.PaymentData; +import network.thunder.core.communication.NodeKey; import network.thunder.core.communication.layer.high.payments.LNPaymentHelper; import network.thunder.core.communication.layer.high.payments.LNPaymentProcessor; -import network.thunder.core.communication.layer.high.payments.PaymentSecret; +import network.thunder.core.communication.layer.high.payments.PaymentData; public class MockLNPaymentHelper implements LNPaymentHelper { - @Override - public void addProcessor (LNPaymentProcessor processor) { - - } @Override - public void removeProcessor (LNPaymentProcessor processor) { + public void addProcessor (NodeKey nodeKey, LNPaymentProcessor processor) { } @Override - public void relayPayment (LNPaymentProcessor paymentProcessor, PaymentData paymentData) { + public void removeProcessor (NodeKey nodeKey) { } @@ -26,13 +22,4 @@ public void makePayment (PaymentData paymentData) { } - @Override - public void paymentRedeemed (PaymentSecret paymentSecret) { - - } - - @Override - public void paymentRefunded (PaymentData paymentSecret) { - - } } diff --git a/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentLogic.java b/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentLogic.java index d5dcc078..83e96e6e 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentLogic.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/MockLNPaymentLogic.java @@ -1,11 +1,13 @@ package network.thunder.core.etc; +import network.thunder.core.communication.LNConfiguration; import network.thunder.core.communication.layer.high.Channel; import network.thunder.core.communication.layer.high.ChannelStatus; import network.thunder.core.communication.layer.high.channel.ChannelSignatures; import network.thunder.core.communication.layer.high.payments.LNPaymentLogic; import network.thunder.core.communication.layer.high.payments.messages.*; import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionOutPoint; import org.bitcoinj.crypto.TransactionSignature; @@ -23,15 +25,7 @@ public MockLNPaymentLogic (LNPaymentMessageFactory messageFactory) { this.messageFactory = messageFactory; } - @Override - public ChannelSignatures getSignatureObject (Channel channel, Transaction channelTransaction) { - return null; - } - @Override - public void initialise (Channel channel) { - this.channel = channel; - } public List getChannelSignatures () { return null; @@ -41,59 +35,60 @@ public List getPaymentSignatures () { return null; } - @Override - public void checkMessageIncoming (LNPayment message) { - if (message instanceof LNPaymentAMessage) { - update = ((LNPaymentAMessage) message).channelStatus.getCloneReversed(); - } - } @Override public Transaction getChannelTransaction (TransactionOutPoint anchor, ChannelStatus channelStatus, ECKey client, ECKey server) { return null; } - @Override - public void checkSignatures (ECKey keyServer, ECKey keyClient, ChannelSignatures channelSignatures, Transaction channelTransaction, ChannelStatus status) { - + public ChannelSignatures getSignatureObject (Channel channel, Transaction channelTransaction, List paymentTransactions) { + return null; } - public void readMessageOutbound (LNPayment message) { - if (message instanceof LNPaymentAMessage) { - update = ((LNPaymentAMessage) message).channelStatus; - } + @Override + public List getPaymentTransactions (Sha256Hash parentTransactionHash, ChannelStatus channelStatus, ECKey keyServer, ECKey keyClient) { + return null; } @Override - public Channel updateChannel (Channel channel) { - return channel; + public void checkUpdate (LNConfiguration configuration, Channel channel, ChannelUpdate channelUpdate) { + } @Override - public ChannelUpdate getChannelUpdate () { - return update; + public void checkSignatures (ECKey keyServer, ECKey keyClient, ChannelSignatures channelSignatures, Transaction channelTransaction, List + paymentTransactions, ChannelStatus status) { + } @Override - public LNPaymentAMessage getAMessage (ChannelUpdate update) { - this.update = update; - return messageFactory.getMessageA(channel, update); + public Transaction getChannelTransaction (Channel channel, SIDE side) { + return null; } @Override - public LNPaymentBMessage getBMessage () { - return messageFactory.getMessageB(channel); + public ChannelSignatures getSignatureObject (Channel channel) { + return null; } @Override - public LNPaymentCMessage getCMessage () { - return messageFactory.getMessageC(channel, null, null); + public List getPaymentTransactions (Channel channel, SIDE side) { + return null; } @Override - public LNPaymentDMessage getDMessage () { - return messageFactory.getMessageD(channel); + public void checkSignatures (Channel channel, ChannelSignatures channelSignatures, SIDE side) { + } + public void readMessageOutbound (LNPayment message) { + if (message instanceof LNPaymentAMessage) { + update = ((LNPaymentAMessage) message).channelUpdate; + } + } + + + + } diff --git a/thunder-core/src/test/java/network/thunder/core/etc/RevocationHashTest.java b/thunder-core/src/test/java/network/thunder/core/etc/RevocationHashTest.java index 06e6e224..8fe43ab1 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/RevocationHashTest.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/RevocationHashTest.java @@ -16,7 +16,7 @@ public void shouldFailBecauseOfWrongSecret () throws Exception { Random r = new Random(); r.nextBytes(secret); - RevocationHash revocationHash = new RevocationHash(10, 10, secret, secret); + RevocationHash revocationHash = new RevocationHash(10, secret, secret); assertFalse(revocationHash.check()); } @@ -27,17 +27,7 @@ public void shouldPassBecauseCorrectSecret () throws Exception { r.nextBytes(secret); byte[] hash = Tools.hashSecret(secret); - RevocationHash revocationHash = new RevocationHash(10, 10, secret, hash); - assertTrue(revocationHash.check()); - } - - @Test - public void shouldPassBecauseOfNewMaster () throws Exception { - byte[] secret = new byte[20]; - Random r = new Random(); - r.nextBytes(secret); - - RevocationHash revocationHash = new RevocationHash(0, 0, secret, secret); + RevocationHash revocationHash = new RevocationHash(10, secret, hash); assertTrue(revocationHash.check()); } } \ No newline at end of file diff --git a/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java b/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java index ed7312be..7ba4ce67 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java @@ -1,11 +1,25 @@ package network.thunder.core.etc; import io.netty.channel.embedded.EmbeddedChannel; +import network.thunder.core.communication.LNConfiguration; +import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.layer.high.RevocationHash; +import org.bitcoinj.core.Address; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.crypto.TransactionSignature; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertThat; public class TestTools { + public static TransactionSignature corruptSignature (TransactionSignature signature) { + byte[] sig = signature.encodeToBitcoin(); + Tools.copyRandomByteInByteArray(sig, 40, 4); + return TransactionSignature.decodeFromBitcoin(sig, true); + } + public static void exchangeMessages (EmbeddedChannel from, EmbeddedChannel to) { Object message = from.readOutbound(); System.out.println(message); @@ -26,4 +40,34 @@ public static void exchangeMessagesDuplex (EmbeddedChannel from, EmbeddedChannel exchangeMessages(from, to); exchangeMessages(to, from); } + + public static Channel getMockChannel (LNConfiguration configuration) { + Channel channel = new Channel(); + + Transaction anchor = new Transaction(Constants.getNetwork()); + anchor.addInput(Sha256Hash.wrap(Tools.getRandomByte(32)), 0, Tools.getDummyScript()); + anchor.addInput(Sha256Hash.wrap(Tools.getRandomByte(32)), 1, Tools.getDummyScript()); + + anchor.addOutput(Coin.valueOf(2000000), Tools.getDummyScript()); + + channel.anchorTx = anchor; + channel.anchorTxHash = anchor.getHash(); + channel.channelStatus.amountClient = 1100000; + channel.channelStatus.amountServer = 900000; + + channel.channelStatus.csvDelay = configuration.DEFAULT_REVOCATION_DELAY; + channel.channelStatus.feePerByte = configuration.DEFAULT_FEE_PER_BYTE; + channel.masterPrivateKeyServer = Tools.getRandomByte(20); + channel.shaChainDepth = 1; + channel.channelStatus.revoHashServerCurrent = new RevocationHash(1, channel.masterPrivateKeyServer); + channel.channelStatus.revoHashClientCurrent = new RevocationHash(1, Tools.getRandomByte(20)); + + channel.channelStatus.revoHashServerNext = new RevocationHash(2, channel.masterPrivateKeyServer); + channel.channelStatus.revoHashClientNext = new RevocationHash(2, Tools.getRandomByte(20)); + + channel.channelStatus.addressClient = new Address(Constants.getNetwork(), Tools.getRandomByte(20)); + channel.channelStatus.addressServer = new Address(Constants.getNetwork(), Tools.getRandomByte(20)); + + return channel; + } } From d648b2ae587f5266e8aa32fe237f9184e6c8d950 Mon Sep 17 00:00:00 2001 From: matsjj Date: Mon, 30 May 2016 08:44:39 +0100 Subject: [PATCH 14/21] Updated payment procedure to handle conflicts properly When both parties send a request to update the channel concurrently, there was a problem that could have led to loss of funds. We now send a fresh revocation hash to be used for the next exchange at the beginning of the exchange (with the A message), such that in case of a conflict, we can use that unused revocation hash directly to construct the new status. --- .../communication/layer/high/Channel.java | 4 +- .../layer/high/ChannelStatus.java | 39 +++ .../layer/high/RevocationHash.java | 25 +- .../messages/LNEstablishAMessage.java | 2 +- .../high/payments/LNPaymentLogicImpl.java | 11 +- .../high/payments/LNPaymentProcessorImpl.kt | 299 +++++++++++++----- .../layer/high/payments/PaymentResponse.kt | 2 +- .../payments/messages/LNPaymentAMessage.java | 29 +- .../payments/messages/LNPaymentBMessage.java | 41 ++- .../payments/messages/LNPaymentCMessage.java | 19 +- .../payments/messages/LNRevokeNewMessage.java | 9 + .../payments/messages/LNRevokeOldMessage.java | 11 + .../payments/messages/LNSignatureMessage.java | 9 + .../thunder/core/database/DBHandler.java | 2 +- .../core/database/InMemoryDBHandler.java | 5 +- .../network/thunder/core/etc/Constants.java | 2 +- .../java/network/thunder/core/etc/Tools.java | 25 +- .../layers/high/LNPaymentHandlerTest.java | 59 +++- .../layers/high/LNPaymentLogicImplTest.java | 12 +- .../thunder/core/etc/DBHandlerMock.java | 3 +- .../network/thunder/core/etc/TestTools.java | 2 +- 21 files changed, 450 insertions(+), 160 deletions(-) create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeNewMessage.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeOldMessage.java create mode 100644 thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNSignatureMessage.java diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java index 165822cd..d556d7b4 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/Channel.java @@ -55,7 +55,7 @@ public class Channel { * When we open the channel we set the index to some high value and decrease it every X hours. * Whenever we commit to a new version of the channel, we use a new child derived from the index. */ - public int shaChainDepth; + public int shaChainDepthCurrent; /* * Timestamps for the channel management. @@ -101,7 +101,7 @@ public Channel copy () { channel.keyServer = this.keyServer; channel.nodeKeyClient = this.nodeKeyClient; channel.minConfirmationAnchor = this.minConfirmationAnchor; - channel.shaChainDepth = this.shaChainDepth; + channel.shaChainDepthCurrent = this.shaChainDepthCurrent; channel.timestampForceClose = this.timestampForceClose; channel.timestampOpen = this.timestampOpen; return channel; diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java index e95a58cd..4cf99880 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/ChannelStatus.java @@ -1,5 +1,6 @@ package network.thunder.core.communication.layer.high; +import com.google.common.base.Preconditions; import network.thunder.core.communication.layer.high.payments.PaymentData; import network.thunder.core.communication.layer.high.payments.messages.ChannelUpdate; import org.bitcoinj.core.Address; @@ -17,11 +18,20 @@ public class ChannelStatus { public int feePerByte; public int csvDelay; + //Various revocation hashes are stored here. They get swapped downwards after an exchange (Next->Current; NextNext->Next) + //Current revocation hash is the one that we have a current valid channel transaction with public RevocationHash revoHashClientCurrent; public RevocationHash revoHashServerCurrent; + + //Next revocation hash is the hash used when creating a new channel transaction public RevocationHash revoHashClientNext; public RevocationHash revoHashServerNext; + //NextNext is the new hash exchanged on the begin of an exchange + //For now there is no need to store it in the database + transient public RevocationHash revoHashClientNextNext; + transient public RevocationHash revoHashServerNextNext; + public Address addressClient; public Address addressServer; @@ -35,8 +45,10 @@ public ChannelStatus copy () { status.addressServer = this.addressServer; status.revoHashClientCurrent = revoHashClientCurrent == null ? null : revoHashClientCurrent.copy(); status.revoHashClientNext = revoHashClientNext == null ? null : revoHashClientNext.copy(); + status.revoHashClientNextNext = revoHashClientNextNext == null ? null : revoHashClientNextNext.copy(); status.revoHashServerCurrent = revoHashServerCurrent == null ? null : revoHashServerCurrent.copy(); status.revoHashServerNext = revoHashServerNext == null ? null : revoHashServerNext.copy(); + status.revoHashServerNextNext = revoHashServerNextNext == null ? null : revoHashServerNextNext.copy(); status.paymentList = clonePaymentList(this.paymentList); return status; @@ -53,6 +65,14 @@ public ChannelStatus reverse () { status.revoHashServerCurrent = status.revoHashClientCurrent; status.revoHashClientCurrent = tempRevocationHash; + RevocationHash tempRevocationHashNext = status.revoHashServerNext; + status.revoHashServerNext = status.revoHashClientNext; + status.revoHashClientNext = tempRevocationHashNext; + + RevocationHash tempRevocationHashNextNext = status.revoHashServerNextNext; + status.revoHashServerNextNext = status.revoHashClientNextNext; + status.revoHashClientNextNext = tempRevocationHashNextNext; + Address tempAddress = status.addressServer; status.addressServer = status.addressClient; status.addressClient = tempAddress; @@ -105,8 +125,25 @@ public void applyUpdate (ChannelUpdate update) { } public void applyNextRevoHash () { + Preconditions.checkNotNull(this.revoHashClientNext); + Preconditions.checkNotNull(this.revoHashServerNext); + this.revoHashClientCurrent = this.revoHashClientNext; this.revoHashServerCurrent = this.revoHashServerNext; + + this.revoHashClientNext = null; + this.revoHashServerNext = null; + } + + public void applyNextNextRevoHash () { + Preconditions.checkNotNull(this.revoHashClientNextNext); + Preconditions.checkNotNull(this.revoHashServerNextNext); + + this.revoHashClientNext = this.revoHashClientNextNext; + this.revoHashServerNext = this.revoHashServerNextNext; + + this.revoHashClientNextNext = null; + this.revoHashServerNextNext = null; } private List reverseSending (List paymentDataList) { @@ -136,6 +173,8 @@ public String toString () { ", amountServer=" + amountServer + ", revoServer=" + revoHashServerCurrent + ", revoClient=" + revoHashClientCurrent + + ", revoServerNext=" + revoHashServerNext + + ", revoClientNext=" + revoHashClientNext + ", paymentList=" + paymentList.size() + '}'; } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java index 9affe6df..b9512b96 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/RevocationHash.java @@ -74,12 +74,15 @@ public boolean check () { return secret != null && Arrays.equals(secretHash, hashSecret(secret)); } + public RevocationHash (byte[] secretHash) { + this.secretHash = secretHash; + } + @Override - public int hashCode () { - int result = index; - result = 31 * result + (secret != null ? Arrays.hashCode(secret) : 0); - result = 31 * result + (secretHash != null ? Arrays.hashCode(secretHash) : 0); - return result; + public String toString () { + return "R{" + index + ": " + + "<" + Tools.bytesToHex(secretHash).substring(0, 5)+">" + + '}'; } @Override @@ -93,20 +96,12 @@ public boolean equals (Object o) { RevocationHash that = (RevocationHash) o; - if (index != that.index) { - return false; - } - if (!Arrays.equals(secret, that.secret)) { - return false; - } return Arrays.equals(secretHash, that.secretHash); } @Override - public String toString () { - return "RevocationHash{" + index + ": " + - "" + Tools.bytesToHex(secretHash).substring(0, 6) + ".." + - '}'; + public int hashCode () { + return Arrays.hashCode(secretHash); } } \ No newline at end of file diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java index 229ee5cc..39728680 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/channel/establish/messages/LNEstablishAMessage.java @@ -67,7 +67,7 @@ public Channel saveToChannel (Channel channel) { channel.channelStatus.csvDelay = csvDelay; channel.channelStatus.feePerByte = feePerByte; channel.minConfirmationAnchor = minConfirmationAnchor; - channel.shaChainDepth = 0; + channel.shaChainDepthCurrent = 0; return channel; } catch (Exception e) { throw new RuntimeException(e); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java index 6e4ca151..4b810a96 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentLogicImpl.java @@ -30,13 +30,13 @@ public Transaction getChannelTransaction (TransactionOutPoint anchor, ChannelSta transaction.addInput(anchor.getHash(), anchor.getIndex(), Tools.getDummyScript()); transaction.addOutput(Coin.valueOf(0), ScriptTools.getChannelTxOutputRevocation( - channelStatus.revoHashServerCurrent, + channelStatus.revoHashServerNext, server, client, channelStatus.csvDelay)); transaction.addOutput(Coin.valueOf(0), channelStatus.addressClient); - transaction = addPayments(transaction, channelStatus, channelStatus.revoHashServerCurrent, server, client); + transaction = addPayments(transaction, channelStatus, channelStatus.revoHashServerNext, server, client); //Missing two signatures, max 146B long fee = (long) Math.ceil((transaction.getMessageSize() + SIGNATURE_SIZE) * channelStatus.feePerByte / 2); @@ -94,7 +94,7 @@ public List getPaymentTransactions (Sha256Hash parentTransactionHas transaction.addInput(parentTransactionHash, index, Tools.getDummyScript()); Coin value = Coin.valueOf(payment.amount); - Script script = ScriptTools.getPaymentTxOutput(keyServer, keyClient, channelStatus.revoHashServerCurrent, channelStatus.csvDelay); + Script script = ScriptTools.getPaymentTxOutput(keyServer, keyClient, channelStatus.revoHashServerNext, channelStatus.csvDelay); transaction.addOutput(value, script); transactions.add(transaction); @@ -110,11 +110,8 @@ public void checkUpdate (LNConfiguration configuration, Channel channel, Channel ChannelStatus oldStatus = channel.channelStatus; ChannelStatus newStatus = oldStatus.copy(); newStatus.applyUpdate(channelUpdate); - newStatus.applyNextRevoHash(); - System.out.println("Receiving update:"); - System.out.println("Old statusSender: " + oldStatus); - System.out.println("New statusSender: " + newStatus); + //Check if the update is allowed.. checkPaymentsInNewStatus(oldStatus, channelUpdate); diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt index 8e18310c..d4d88239 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/LNPaymentProcessorImpl.kt @@ -7,9 +7,8 @@ import network.thunder.core.communication.layer.DIRECTION.RECEIVED import network.thunder.core.communication.layer.DIRECTION.SENT import network.thunder.core.communication.layer.Message import network.thunder.core.communication.layer.MessageExecutor -import network.thunder.core.communication.layer.high.AckMessageImpl -import network.thunder.core.communication.layer.high.NumberedMessage -import network.thunder.core.communication.layer.high.RevocationHash +import network.thunder.core.communication.layer.MessageWrapper +import network.thunder.core.communication.layer.high.* import network.thunder.core.communication.layer.high.payments.LNPaymentLogic.SIDE.SERVER import network.thunder.core.communication.layer.high.payments.messages.* import network.thunder.core.communication.processor.exceptions.LNPaymentException @@ -17,10 +16,37 @@ import network.thunder.core.database.DBHandler import network.thunder.core.etc.Constants import network.thunder.core.etc.Tools import network.thunder.core.helper.events.LNEventHelper -import java.util.* import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantLock +/** + * Processor for negotiating new payment status. + * + * There are currently two allowed message exchanges. + * A -> + * <- B + * C -> + * + * or + * + * A -> + * <- A + * A -> + * <- B + * C -> + * + * in case of concurrent payment requests. In case of the conflict, the dice in the message decides which party may + * continue with his exchange, with the other party putting its own updates on hold. + * + * TODO Violation of this strict protocol (for example by not honoring the dice or sending another A message) will + * lead directly to channel closure. + * + * Message A contains the new revocation hash that is to be used with the exchange after the one right now. + * This is important for the conflict case, as it means we can directly start a new exchange with new hashes. + * When starting another exchange with the same hashes, the other party holds multiple valid states + * that are all signed by us, which means it can choose the most profitable one. More clearly, any payment + * cannot be considered final without it being the *only* valid state right now. + */ class LNPaymentProcessorImpl( contextFactory: ContextFactory, var dbHandler: DBHandler, @@ -83,7 +109,7 @@ class LNPaymentProcessorImpl( val channel = dbHandler.getChannel(message.channelHash).copy() val state = PaymentState(channel, messages) - var (newChannel, newUpdate, oldRevocation, newMessage) = + val (newChannel, newUpdate, oldRevocation, newMessage) = if (message is LNPaymentAMessage) { readMessageA(state, message) } else if (message is LNPaymentBMessage) { @@ -92,7 +118,6 @@ class LNPaymentProcessorImpl( readMessageC(state, message as LNPaymentCMessage) } - var response: NumberedMessage? = null; if (newMessage != null) { newMessage.messageNumberToAck = message.messageNumber @@ -101,6 +126,10 @@ class LNPaymentProcessorImpl( response = AckMessageImpl(message.messageNumber) } + if (response is ChannelSpecificMessage) { + response.channelHash = message.channelHash + } + //One atomic transaction here dbHandler.updateChannelStatus( node.nodeKey, @@ -127,7 +156,6 @@ class LNPaymentProcessorImpl( } } - } else { throw LNPaymentException("Wrong message? " + message); } @@ -208,18 +236,21 @@ class LNPaymentProcessorImpl( } } val status = channel.channelStatus.copy() + val statusT = channel.channelStatus.copy() status.applyUpdate(update) - status.applyNextRevoHash() + statusT.applyUpdate(update) + statusT.applyNextRevoHash() println("Sending update: ") println("Old statusSender: " + state.channel.channelStatus) - println("New statusSender: " + status) + println("New statusSender: " + statusT) channel.channelStatus = status - val signatures = paymentLogic.getSignatureObject(channel) + val message = LNPaymentAMessage(update) + addSignatureToMessage(channel, message) + addNewRevocationToMessage(channel, message) - val message = LNPaymentAMessage(update, signatures) message.channelHash = channel.hash return message @@ -229,124 +260,224 @@ class LNPaymentProcessorImpl( fun readMessageA(state: PaymentState, message: LNPaymentAMessage): PaymentResponse { val lastMessage = state.messages.lastOrNull() + val channel = applyUpdates(state, listOf(MessageWrapper(message, direction = RECEIVED))) + if (lastMessage != null && lastMessage.message is LNPaymentAMessage) { if (lastMessage.direction == SENT) { if (message.dice < lastMessage.message.dice) { - //Our dice was higher, we just ignore their message.. - return PaymentResponse(null, null, null, null) + //Our dice was higher, it's our turn to send yet another update with the new revocation hash received here + //TODO don't use the old update object, rather reconstruct it again from the database + val response = LNPaymentAMessage(lastMessage.message.channelUpdate) + addSignatureToMessage(channel, response) + addNewRevocationToMessage(channel, response) + return PaymentResponse(null, null, null, response) } else { + //Our dice was lower. We will wait for the other party to send another updated A message + return PaymentResponse(null, null, null, null) } - } else { - throw LNPaymentException("Wrong message received in readMessageA"); } } + paymentLogic.checkUpdate(serverObject.configuration, state.channel, message.channelUpdate.cloneReversed) - //Only process the message if we completed the last exchange or if concurrent A exchange - if (lastMessage == null || lastMessage.message is LNPaymentAMessage || lastMessage.message is LNPaymentCMessage) { - val channel = state.channel - paymentLogic.checkUpdate(serverObject.configuration, channel, message.channelUpdate.cloneReversed) + println("Receiving update:") + println("Old statusSender: " + state.channel.channelStatus) + println("New statusSender: " + channel.channelStatus) - channel.channelStatus.applyUpdate(message.channelUpdate.cloneReversed) - channel.channelStatus.applyNextRevoHash(); + checkNewRevocationMessage(channel, message) + checkSignatureMessage(channel, message) - paymentLogic.checkSignatures(channel, message.getChannelSignatures(), SERVER) + val outboundMessage = LNPaymentBMessage() + addNewRevocationToMessage(channel, outboundMessage) + addOldRevocationToMessage(channel, outboundMessage) + addSignatureToMessage(channel, outboundMessage) - val signatures = paymentLogic.getSignatureObject(channel) + return PaymentResponse(null, null, null, outboundMessage) + } - val oldRevocation = RevocationHash(channel.shaChainDepth, channel.masterPrivateKeyServer) - val newRevocation = RevocationHash(channel.shaChainDepth + 2, channel.masterPrivateKeyServer) - newRevocation.secret = null //IMPORTANT: remove your secret before sending it + fun readMessageB(state: PaymentState, message: LNPaymentBMessage): PaymentResponse { + val messageWrapper = state.messages.last { it.direction == SENT && it.message is LNPaymentAMessage } + val messageA = messageWrapper.message as LNPaymentAMessage + var channel = applyUpdates(state, listOf(MessageWrapper(message, direction = RECEIVED))) - val outboundMessage = LNPaymentBMessage(signatures, oldRevocation, newRevocation) - outboundMessage.channelHash = message.channelHash + checkOldRevocationMessage(channel, message, state.channel) + checkNewRevocationMessage(channel, message) + checkSignatureMessage(channel, message) - return PaymentResponse(null, null, null, outboundMessage) - } else { - throw LNPaymentException("Wrong message received in readMessageA: " + lastMessage); - } + val outboundMessage = LNPaymentCMessage() + addOldRevocationToMessage(channel, outboundMessage) + + channel = applyUpdates(state, listOf(MessageWrapper(message, direction = RECEIVED), MessageWrapper(outboundMessage, direction = SENT))) + + channel.channelStatus.applyNextRevoHash() + channel.channelStatus.applyNextNextRevoHash() + return PaymentResponse(channel, messageA.channelUpdate, message.oldRevocation, outboundMessage) } + fun readMessageC(state: PaymentState, message: LNPaymentCMessage): PaymentResponse { + val messageA = state.messages.last { it.direction == RECEIVED && it.message is LNPaymentAMessage }.message as LNPaymentAMessage + val messageB = state.messages.last { it.direction == SENT && it.message is LNPaymentBMessage } //Not used, but good as extra type safety - fun readMessageB(state: PaymentState, message: LNPaymentBMessage): PaymentResponse { - val messageA = state.messages.last { it.direction == SENT && it.message is LNPaymentAMessage } + var channel = applyUpdates(state, listOf(MessageWrapper(message, direction = RECEIVED))) - if (messageA.message !is LNPaymentAMessage || messageA.direction != SENT) { - throw LNPaymentException("Wrong message received in readMessageB"); - } - val channel = state.channel - var status = channel.channelStatus + checkOldRevocationMessage(channel, message, state.channel) - if (!message.oldRevocation.check() || !Arrays.equals(state.channel.channelStatus.revoHashClientCurrent.secretHash, message.oldRevocation.secretHash)) { - throw LNPaymentException("Old revocation check failed. " + - "Old one on file: ${channel.channelStatus.revoHashClientCurrent}. Received: ${message.oldRevocation}") - } + channel.channelStatus.applyNextRevoHash() + channel.channelStatus.applyNextNextRevoHash() + + return PaymentResponse(channel, messageA.channelUpdate.cloneReversed, message.oldRevocation, null) + } + + fun addNewRevocationToMessage(channel: Channel, message: LNRevokeNewMessage) { + val newRevocation = RevocationHash(channel.channelStatus.revoHashServerNext.index + 1, channel.masterPrivateKeyServer) + newRevocation.secret = null //IMPORTANT: remove your secret before sending it + message.newRevocationHash = newRevocation + } - if (message.newRevocation.index != channel.channelStatus.revoHashClientNext.index + 1) { - throw LNPaymentException("New revocation hash does not append chain") + fun checkNewRevocationMessage(channel: Channel, message: LNRevokeNewMessage) { + val current = channel.channelStatus.revoHashServerNext.index + if (message.newRevocationHash.index != current + 1) { + throw LNPaymentException("New revocation hash does not append chain. New: ${message.newRevocationHash.index}. Current: ${current}") } + } - status.applyUpdate(messageA.message.channelUpdate) - status.applyNextRevoHash() - paymentLogic.checkSignatures(channel, message.getChannelSignatures(), SERVER) + fun addOldRevocationToMessage(channel: Channel, message: LNRevokeOldMessage) { + message.oldRevocationHash = (channel.shaChainDepthCurrent..channel.channelStatus.revoHashServerNext.index - 1). + map { RevocationHash(it, channel.masterPrivateKeyServer) } + } - val oldRevocation = RevocationHash(channel.shaChainDepth, channel.masterPrivateKeyServer) - val newRevocation = RevocationHash(channel.shaChainDepth + 2, channel.masterPrivateKeyServer) - newRevocation.secret = null //IMPORTANT: remove secret before sending it + fun checkOldRevocationMessage(channel: Channel, message: LNRevokeOldMessage, oldChannel: Channel) { + val currentRevocation = channel.channelStatus.revoHashClientCurrent + val currentRevocationReceived = message.oldRevocationHash.first() + if (!currentRevocationReceived.check() || currentRevocation != currentRevocationReceived) { + throw LNPaymentException("Old revocation check failed. ${currentRevocation} != ${currentRevocationReceived}") + } - channel.channelSignatures = message.getChannelSignatures() - channel.channelStatus = status - channel.shaChainDepth++ - channel.channelStatus.revoHashClientNext = message.newRevocation - channel.channelStatus.revoHashServerNext = newRevocation //TODO - channel.timestampForceClose = status.paymentList.map { it.timestampRefund }.min() ?: 0 + if (message.oldRevocationHash.size > 1) { + val intermediateRevocation = oldChannel.channelStatus.revoHashClientNext + val intermediateRevocationReceived = message.oldRevocationHash.last() + if (!intermediateRevocationReceived.check() || intermediateRevocation != intermediateRevocationReceived) { + throw LNPaymentException("Old revocation check failed") + } + } + } - val outboundMessage = LNPaymentCMessage(oldRevocation, newRevocation) - outboundMessage.channelHash = message.channelHash + fun addSignatureToMessage(channel: Channel, message: LNSignatureMessage) { + message.channelSignatures = paymentLogic.getSignatureObject(channel) + } - return PaymentResponse(channel, messageA.message.channelUpdate, message.oldRevocation, outboundMessage) + fun checkSignatureMessage(channel: Channel, message: LNSignatureMessage) { + paymentLogic.checkSignatures(channel, message.getChannelSignatures(), SERVER) } - fun readMessageC(state: PaymentState, message: LNPaymentCMessage): PaymentResponse { - val messageA = state.messages.last { it.direction == RECEIVED && it.message is LNPaymentAMessage }.message as LNPaymentAMessage - val messageB = state.messages.last { it.direction == SENT && it.message is LNPaymentBMessage } + fun sendMessage(message: Message) { + println("${node.nodeKey} O message = ${message}") + messageExecutor.sendMessageUpwards(message) + } + fun applyUpdates(state: PaymentState, newMessageWrapper: List): Channel { val channel = state.channel - if (messageB.message !is LNPaymentBMessage || messageB.direction != SENT) { - throw LNPaymentException("Wrong message received in readMessageC"); + var messageList = state.messages + val indexThisExchangeStarts = messageList.indexOfLast { it.message is LNPaymentCMessage } + if (indexThisExchangeStarts != -1) { + messageList = messageList.subList(indexThisExchangeStarts + 1, messageList.size); } - if (!message.oldRevocation.check() || !Arrays.equals(channel.channelStatus.revoHashClientCurrent.secretHash, message.oldRevocation.secretHash)) { - throw LNPaymentException("Old revocation check failed") - } + messageList = messageList.toMutableList() + messageList.addAll(newMessageWrapper) - if (message.newRevocation.index != channel.channelStatus.revoHashClientNext.index + 1) { - throw LNPaymentException("New revocation hash does not append chain") - } + checkMessageOrder(messageList) - var status = state.channel.channelStatus.copy() - status.applyUpdate(messageA.channelUpdate.cloneReversed) - status.applyNextRevoHash() + var countA = 0 + for (messageWrapper in messageList) { + val message = messageWrapper.message + if (message is LNPaymentAMessage) countA++ + if (message is LNSignatureMessage) { + if (messageWrapper.direction == RECEIVED) { + channel.channelSignatures = message.getChannelSignatures() + } + } - channel.channelSignatures = messageA.getChannelSignatures() - channel.channelStatus = status - channel.shaChainDepth++ - channel.channelStatus.revoHashClientNext = message.newRevocation - channel.channelStatus.revoHashServerNext = messageB.message.newRevocation - channel.timestampForceClose = status.paymentList.map { it.timestampRefund }.min() ?: 0 + if (message is LNRevokeNewMessage) { + if (messageWrapper.direction == RECEIVED) { + channel.channelStatus.revoHashClientNextNext = message.newRevocationHash + } else { + channel.channelStatus.revoHashServerNextNext = message.newRevocationHash + } + } + if (countA == 2) { + channel.channelStatus.applyNextNextRevoHash() + } - return PaymentResponse(channel, messageA.channelUpdate.cloneReversed, message.oldRevocation, null) - } + if (message is LNRevokeOldMessage && messageWrapper.direction == SENT) { + channel.shaChainDepthCurrent = message.oldRevocationHash.map { it.index }.max()!! + 1 + } + } + + val messageA: MessageWrapper + if (messageList.count { it.message is LNPaymentAMessage } == 2) { + messageA = messageList.get(0) + } else { + messageA = messageList.last { it.message is LNPaymentAMessage } + } + + if (messageA.message is LNPaymentAMessage) { + if (messageA.direction == RECEIVED) { + channel.channelStatus.applyUpdate(messageA.message.channelUpdate.cloneReversed) + } else { + channel.channelStatus.applyUpdate(messageA.message.channelUpdate) + } + } + + channel.timestampForceClose = channel.channelStatus.paymentList.map { it.timestampRefund }.min() ?: 0 - fun sendMessage(m: Message) { - messageExecutor.sendMessageUpwards(m) + return channel } + fun checkMessageOrder(messageList: List) { + //There are only 2 possiblities allowed + //A -> A -> A -> B -> C + // or + //A -> B -> C + val conflictWon: Boolean + val weStartedExchange: Boolean + + val messageA = messageList.get(0) + if (messageA.message !is LNPaymentAMessage) throw LNPaymentException("Wrong message order") + weStartedExchange = messageA.direction == SENT + + if (messageList.size == 1) return + if (messageList.get(1).message is LNPaymentAMessage) { + //Conflicting concurrent updates.. + val messageA1 = messageList.get(1) + if (messageA1.message !is LNPaymentAMessage) throw LNPaymentException("Wrong message order") + + conflictWon = messageA.message.dice > messageA1.message.dice + + if (!conflictWon) { + Tools.testMessageWrapper(messageList, LNPaymentAMessage::class.java, 2, RECEIVED, weStartedExchange) + Tools.testMessageWrapper(messageList, LNPaymentBMessage::class.java, 3, SENT, weStartedExchange) + Tools.testMessageWrapper(messageList, LNPaymentCMessage::class.java, 4, RECEIVED, weStartedExchange) + } else { + Tools.testMessageWrapper(messageList, LNPaymentAMessage::class.java, 2, SENT, weStartedExchange) + Tools.testMessageWrapper(messageList, LNPaymentBMessage::class.java, 3, RECEIVED, weStartedExchange) + Tools.testMessageWrapper(messageList, LNPaymentCMessage::class.java, 4, SENT, weStartedExchange) + } + if (messageList.size > 5) { + throw LNPaymentException("Message after complete exchange..") + } + } else { + //Normal case + Tools.testMessageWrapper(messageList, LNPaymentBMessage::class.java, 1, RECEIVED, weStartedExchange) + Tools.testMessageWrapper(messageList, LNPaymentCMessage::class.java, 2, SENT, weStartedExchange) + if (messageList.size > 3) throw LNPaymentException("Message after complete exchange..") + } + } override fun onLayerActive(messageExecutor: MessageExecutor) { this.messageExecutor = messageExecutor diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt index 87f7b889..8bfc91b8 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/PaymentResponse.kt @@ -8,6 +8,6 @@ import network.thunder.core.communication.layer.high.payments.messages.LNPayment data class PaymentResponse( val channel: Channel?, val update: ChannelUpdate?, - val revocationHash: RevocationHash?, + val revocationHash: List?, val messages: LNPayment?) diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java index 68d92b96..20debc4d 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentAMessage.java @@ -1,6 +1,7 @@ package network.thunder.core.communication.layer.high.payments.messages; import com.google.common.base.Preconditions; +import network.thunder.core.communication.layer.high.RevocationHash; import network.thunder.core.communication.layer.high.channel.ChannelSignatures; import org.bitcoinj.crypto.TransactionSignature; @@ -9,7 +10,7 @@ import java.util.Random; import java.util.stream.Collectors; -public class LNPaymentAMessage extends LNPayment { +public class LNPaymentAMessage extends LNPayment implements LNRevokeNewMessage, LNSignatureMessage { public int dice; public ChannelUpdate channelUpdate; @@ -17,14 +18,14 @@ public class LNPaymentAMessage extends LNPayment { public List channelSignatures = new ArrayList<>(); public List paymentSignatures = new ArrayList<>(); - public LNPaymentAMessage (ChannelUpdate channelUpdate, ChannelSignatures channelSignatures) { + public RevocationHash newRevocation; + + public LNPaymentAMessage (ChannelUpdate channelUpdate) { this.dice = new Random().nextInt(Integer.MAX_VALUE); this.channelUpdate = channelUpdate; - - this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); - this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); } + @Override public ChannelSignatures getChannelSignatures () { ChannelSignatures signatures = new ChannelSignatures(); signatures.paymentSignatures = paymentSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); @@ -32,6 +33,22 @@ public ChannelSignatures getChannelSignatures () { return signatures; } + @Override + public void setChannelSignatures (ChannelSignatures channelSignatures) { + this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + } + + @Override + public RevocationHash getNewRevocationHash () { + return newRevocation; + } + + @Override + public void setNewRevocationHash (RevocationHash newRevocationHash) { + this.newRevocation = newRevocationHash; + } + @Override public void verify () { Preconditions.checkNotNull(channelUpdate); @@ -46,6 +63,6 @@ public String toString () { + (channelUpdate.newPayments.size() + " " + channelUpdate.redeemedPayments.size() + " " + channelUpdate.refundedPayments.size()) + - '}'; + ", revo="+this.newRevocation+"}"; } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java index 27c5b11f..09f12d68 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentBMessage.java @@ -9,22 +9,15 @@ import java.util.List; import java.util.stream.Collectors; -public class LNPaymentBMessage extends LNPayment { +public class LNPaymentBMessage extends LNPayment implements LNRevokeNewMessage, LNRevokeOldMessage, LNSignatureMessage { - public RevocationHash oldRevocation; public RevocationHash newRevocation; + public List oldRevocation; public List channelSignatures = new ArrayList<>(); public List paymentSignatures = new ArrayList<>(); - public LNPaymentBMessage (ChannelSignatures channelSignatures, RevocationHash oldRevocation, RevocationHash newRevocation) { - this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); - this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); - - this.oldRevocation = oldRevocation; - this.newRevocation = newRevocation; - } - + @Override public ChannelSignatures getChannelSignatures () { ChannelSignatures signatures = new ChannelSignatures(); signatures.paymentSignatures = paymentSignatures.stream().map(o -> TransactionSignature.decodeFromBitcoin(o, true)).collect(Collectors.toList()); @@ -32,6 +25,32 @@ public ChannelSignatures getChannelSignatures () { return signatures; } + @Override + public void setChannelSignatures (ChannelSignatures channelSignatures) { + this.channelSignatures = channelSignatures.channelSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + this.paymentSignatures = channelSignatures.paymentSignatures.stream().map(TransactionSignature::encodeToBitcoin).collect(Collectors.toList()); + } + + @Override + public RevocationHash getNewRevocationHash () { + return newRevocation; + } + + @Override + public List getOldRevocationHash () { + return oldRevocation; + } + + @Override + public void setOldRevocationHash (List oldRevocationHash) { + this.oldRevocation = oldRevocationHash; + } + + @Override + public void setNewRevocationHash (RevocationHash newRevocationHash) { + this.newRevocation = newRevocationHash; + } + @Override public void verify () { Preconditions.checkNotNull(oldRevocation); @@ -42,6 +61,6 @@ public void verify () { @Override public String toString () { - return "LNPaymentBMessage{}"; + return "LNPaymentBMessage{"+newRevocation+"}"; } } diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java index 7eaf551d..e2d43f5f 100644 --- a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNPaymentCMessage.java @@ -3,20 +3,25 @@ import com.google.common.base.Preconditions; import network.thunder.core.communication.layer.high.RevocationHash; -public class LNPaymentCMessage extends LNPayment { +import java.util.List; - public RevocationHash oldRevocation; - public RevocationHash newRevocation; +public class LNPaymentCMessage extends LNPayment implements LNRevokeOldMessage { - public LNPaymentCMessage (RevocationHash oldRevocation, RevocationHash newRevocation) { - this.oldRevocation = oldRevocation; - this.newRevocation = newRevocation; + public List oldRevocation; + + @Override + public List getOldRevocationHash () { + return oldRevocation; + } + + @Override + public void setOldRevocationHash (List oldRevocationHash) { + this.oldRevocation = oldRevocationHash; } @Override public void verify () { Preconditions.checkNotNull(oldRevocation); - Preconditions.checkNotNull(newRevocation); } @Override diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeNewMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeNewMessage.java new file mode 100644 index 00000000..ff0ed725 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeNewMessage.java @@ -0,0 +1,9 @@ +package network.thunder.core.communication.layer.high.payments.messages; + +import network.thunder.core.communication.layer.high.RevocationHash; + +public interface LNRevokeNewMessage { + RevocationHash getNewRevocationHash (); + void setNewRevocationHash (RevocationHash newRevocationHash); +} + diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeOldMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeOldMessage.java new file mode 100644 index 00000000..683d01c7 --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNRevokeOldMessage.java @@ -0,0 +1,11 @@ +package network.thunder.core.communication.layer.high.payments.messages; + +import network.thunder.core.communication.layer.high.RevocationHash; + +import java.util.List; + +public interface LNRevokeOldMessage { + List getOldRevocationHash (); + void setOldRevocationHash (List oldRevocationHash); +} + diff --git a/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNSignatureMessage.java b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNSignatureMessage.java new file mode 100644 index 00000000..bdbba16d --- /dev/null +++ b/thunder-core/src/main/java/network/thunder/core/communication/layer/high/payments/messages/LNSignatureMessage.java @@ -0,0 +1,9 @@ +package network.thunder.core.communication.layer.high.payments.messages; + +import network.thunder.core.communication.layer.high.channel.ChannelSignatures; + +public interface LNSignatureMessage { + ChannelSignatures getChannelSignatures (); + void setChannelSignatures(ChannelSignatures channelSignatures); +} + diff --git a/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java b/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java index ede314af..258049d2 100644 --- a/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java +++ b/thunder-core/src/main/java/network/thunder/core/database/DBHandler.java @@ -52,7 +52,7 @@ public interface DBHandler { void insertChannel (Channel channel); void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, - Channel channel, ChannelUpdate update, RevocationHash revocationHash, NumberedMessage request, NumberedMessage response); + Channel channel, ChannelUpdate update, List revocationHash, NumberedMessage request, NumberedMessage response); List getIPObjectsWithActiveChannel (); List getTopology (); diff --git a/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java b/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java index 88d20f2d..13f47871 100644 --- a/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java +++ b/thunder-core/src/main/java/network/thunder/core/database/InMemoryDBHandler.java @@ -256,7 +256,8 @@ public void insertChannel (Channel channel) { @Override public void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, - Channel channel, ChannelUpdate update, RevocationHash revocationHash, NumberedMessage request, NumberedMessage response) { + Channel channel, ChannelUpdate update, List revocationHash, + NumberedMessage request, NumberedMessage response) { if (request != null) { setMessageProcessed(nodeKey, request); @@ -274,7 +275,7 @@ public void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash c revocationList = new ArrayList<>(); revocationHashListTheir.put(channelHash, revocationList); } - revocationList.add(revocationHash); + revocationList.addAll(revocationHash); } if (channel != null) { synchronized (channelList) { diff --git a/thunder-core/src/main/java/network/thunder/core/etc/Constants.java b/thunder-core/src/main/java/network/thunder/core/etc/Constants.java index ea98e192..47e0b787 100644 --- a/thunder-core/src/main/java/network/thunder/core/etc/Constants.java +++ b/thunder-core/src/main/java/network/thunder/core/etc/Constants.java @@ -29,7 +29,7 @@ public static NetworkParameters getNetwork () { public static boolean USE_MOCK_BLOCKCHAIN = true; public static int PAYMENT_TIMEOUT = 5; - public static long MESSAGE_RESEND_TIME = 15000; + public static long MESSAGE_RESEND_TIME = 5000; public static int MAX_HTLC_PER_CHANNEL = 250; public static final int STANDARD_PORT = 2204; diff --git a/thunder-core/src/main/java/network/thunder/core/etc/Tools.java b/thunder-core/src/main/java/network/thunder/core/etc/Tools.java index 107dc52b..ef8834bb 100644 --- a/thunder-core/src/main/java/network/thunder/core/etc/Tools.java +++ b/thunder-core/src/main/java/network/thunder/core/etc/Tools.java @@ -22,7 +22,10 @@ import com.google.gson.Gson; import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; import com.sun.org.apache.xml.internal.security.utils.Base64; +import network.thunder.core.communication.layer.DIRECTION; +import network.thunder.core.communication.layer.MessageWrapper; import network.thunder.core.communication.layer.high.Channel; +import network.thunder.core.communication.processor.exceptions.LNPaymentException; import network.thunder.core.helper.ScriptTools; import org.bitcoinj.core.*; import org.bitcoinj.core.ECKey.ECDSASignature; @@ -50,10 +53,10 @@ public static int getRandom (int min, int max) { } /** - * Get the method name for a depth in call stack.
+ * Get the method name for a index in call stack.
* Utility function * - * @param depth depth in the call stack (0 means current method, 1 means call method, ...) + * @param depth index in the call stack (0 means current method, 1 means call method, ...) * @return method name */ public static String getMethodName (final int depth) { @@ -146,6 +149,24 @@ public static String InputStreamToString (InputStream in) { return qry; } + public static void testMessageWrapper (List messageList, Class c, int index, DIRECTION direction, boolean weStartedExchange) { + if (index >= messageList.size()) { + return; + } + MessageWrapper message = messageList.get(index); + + if (!message.getMessage().getClass().equals(c)) { + throw new LNPaymentException("Wrong message type"); + } + if (message.getDirection() == direction && weStartedExchange) { + return; + } + if (message.getDirection() != direction && !weStartedExchange) { + return; + } + throw new LNPaymentException("Wrong message order"); + } + public static boolean arrayListContainsByteArray (ArrayList arrayList, byte[] bytes) { //TODO: This is a probably slow hack, better way would be to use a helper class, as we can make use of hashCode then.. /* Example wrapper class to have more efficient contains(..) diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java index 9507af33..4cf58575 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentHandlerTest.java @@ -19,6 +19,7 @@ import network.thunder.core.etc.MockContextFactory; import network.thunder.core.etc.TestTools; import network.thunder.core.etc.Tools; +import org.bitcoinj.core.ECKey; import org.junit.Before; import org.junit.Test; @@ -30,9 +31,7 @@ import java.util.List; import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class LNPaymentHandlerTest { @@ -55,6 +54,7 @@ public class LNPaymentHandlerTest { ContextFactory contextFactory21 = new MockContextFactory(serverObject2, dbHandler2); LNPaymentHelper paymentHelper1 = contextFactory12.getPaymentHelper(); + LNPaymentHelper paymentHelper2 = contextFactory21.getPaymentHelper(); Channel channel1 = TestTools.getMockChannel(new LNConfiguration()); Channel channel2 = TestTools.getMockChannel(new LNConfiguration()); @@ -96,7 +96,7 @@ public void after () { @Test public void fullExchange () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - PaymentData paymentData = getMockPaymentData(); + PaymentData paymentData = getMockPaymentData(serverObject1.pubKeyServer, serverObject2.pubKeyServer); dbHandler2.addPaymentSecret(paymentData.secret); paymentHelper1.makePayment(paymentData); @@ -105,8 +105,6 @@ public void fullExchange () throws NoSuchProviderException, NoSuchAlgorithmExcep TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); TestTools.exchangeMessages(channel21, channel12, AckMessageImpl.class); - Thread.sleep(1000); - TestTools.exchangeMessages(channel21, channel12, LNPaymentAMessage.class); TestTools.exchangeMessages(channel12, channel21, LNPaymentBMessage.class); TestTools.exchangeMessages(channel21, channel12, LNPaymentCMessage.class); @@ -128,11 +126,11 @@ public void fullExchange () throws NoSuchProviderException, NoSuchAlgorithmExcep @Test public void fullExchangeWithAnotherPaymentMidway () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - paymentHelper1.makePayment(getMockPaymentData()); + paymentHelper1.makePayment(getMockPaymentData(serverObject1.pubKeyServer, serverObject2.pubKeyServer)); TestTools.exchangeMessages(channel12, channel21, LNPaymentAMessage.class); TestTools.exchangeMessages(channel21, channel12, LNPaymentBMessage.class); - paymentHelper1.makePayment(getMockPaymentData()); + paymentHelper1.makePayment(getMockPaymentData(serverObject1.pubKeyServer, serverObject2.pubKeyServer)); TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); assertTrue(channel12.readOutbound() instanceof LNPaymentAMessage); @@ -140,9 +138,46 @@ public void fullExchangeWithAnotherPaymentMidway () throws NoSuchProviderExcepti after(); } + @Test + public void fullExchangeConflict () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { + PaymentData payment1 = getMockPaymentData(serverObject1.pubKeyServer, serverObject2.pubKeyServer); + PaymentData payment2 = getMockPaymentData(serverObject2.pubKeyServer, serverObject1.pubKeyServer); + + paymentHelper1.makePayment(payment1); + paymentHelper2.makePayment(payment2); + + dbHandler1.addPaymentSecret(payment2.secret); + dbHandler2.addPaymentSecret(payment1.secret); + + LNPaymentAMessage messageA1 = (LNPaymentAMessage) channel12.readOutbound(); + LNPaymentAMessage messageA2 = (LNPaymentAMessage) channel21.readOutbound(); + + channel21.writeInbound(messageA1); + channel12.writeInbound(messageA2); + + if(messageA1.dice > messageA2.dice) { + EmbeddedChannel temp = channel12; + channel12 = channel21; + channel21 = temp; + } + + TestTools.exchangeMessages(channel12, channel21, AckMessageImpl.class); + TestTools.exchangeMessages(channel21, channel12, LNPaymentAMessage.class); + TestTools.exchangeMessages(channel12, channel21, LNPaymentBMessage.class); + TestTools.exchangeMessages(channel21, channel12, LNPaymentCMessage.class); + + TestTools.exchangeMessages(channel12, channel21, AckMessageImpl.class); + TestTools.exchangeMessages(channel12, channel21, LNPaymentAMessage.class); + TestTools.exchangeMessages(channel21, channel12, LNPaymentBMessage.class); + TestTools.exchangeMessages(channel12, channel21, LNPaymentCMessage.class); + + + after(); + } + @Test public void sendWrongMessageShouldDisconnect () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException { - paymentHelper1.makePayment(getMockPaymentData()); + paymentHelper1.makePayment(getMockPaymentData(serverObject1.pubKeyServer, serverObject2.pubKeyServer)); LNPaymentAMessage messageA = (LNPaymentAMessage) channel12.readOutbound(); channel21.writeInbound(messageA); @@ -154,7 +189,7 @@ public void sendWrongMessageShouldDisconnect () throws NoSuchProviderException, after(); } - public PaymentData getMockPaymentData () { + public PaymentData getMockPaymentData (ECKey key1, ECKey key2) { LNConfiguration configuration = new LNConfiguration(); PaymentData paymentData = new PaymentData(); paymentData.sending = true; @@ -165,8 +200,8 @@ public PaymentData getMockPaymentData () { LNOnionHelper onionHelper = new LNOnionHelperImpl(); List route = new ArrayList<>(); - route.add(serverObject1.pubKeyServer.getPubKey()); - route.add(serverObject2.pubKeyServer.getPubKey()); + route.add(key1.getPubKey()); + route.add(key2.getPubKey()); paymentData.onionObject = onionHelper.createOnionObject(route, null); diff --git a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java index 63343142..7e54adb9 100644 --- a/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java +++ b/thunder-core/src/test/java/network/thunder/core/communication/layers/high/LNPaymentLogicImplTest.java @@ -84,7 +84,7 @@ public void produceCorrectChannelTransactionNoPayments () { assertEquals( channelTransaction.getOutput(0).getScriptPubKey(), ScriptTools.getChannelTxOutputRevocation( - channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.revoHashServerNext, channel1.keyServer, channel1.keyClient, channel1.channelStatus.csvDelay)); @@ -92,7 +92,7 @@ public void produceCorrectChannelTransactionNoPayments () { assertEquals( channelTransaction.getOutput(0).getScriptPubKey(), ScriptTools.getChannelTxOutputRevocation( - channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.revoHashServerNext, channel1.keyServer, channel1.keyClient, channel1.channelStatus.csvDelay)); @@ -124,7 +124,7 @@ public void produceCorrectChannelTransactionWithPayments () { ScriptTools.getChannelTxOutputPaymentSending( channel1.keyServer, channel1.keyClient, - channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.revoHashServerNext, sending.secret, sending.timestampRefund)); @@ -133,7 +133,7 @@ public void produceCorrectChannelTransactionWithPayments () { ScriptTools.getChannelTxOutputPaymentReceiving( channel1.keyServer, channel1.keyClient, - channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.revoHashServerNext, receiving.secret, receiving.timestampRefund)); } @@ -177,7 +177,7 @@ public void produceCorrectPaymentTransactions () { ScriptTools.getPaymentTxOutput( channel1.keyServer, channel1.keyClient, - channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.revoHashServerNext, channel1.channelStatus.csvDelay)); assertEquals( @@ -185,7 +185,7 @@ public void produceCorrectPaymentTransactions () { ScriptTools.getPaymentTxOutput( channel1.keyServer, channel1.keyClient, - channel1.channelStatus.revoHashServerCurrent, + channel1.channelStatus.revoHashServerNext, channel1.channelStatus.csvDelay)); diff --git a/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java b/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java index dd8e3821..9ebf72c4 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/DBHandlerMock.java @@ -136,11 +136,12 @@ public void insertChannel (Channel channel) { } @Override - public void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, Channel channel, ChannelUpdate update, RevocationHash revocationHash, NumberedMessage request, NumberedMessage response) { + public void updateChannelStatus (@NotNull NodeKey nodeKey, @NotNull Sha256Hash channelHash, @NotNull ECKey keyServer, Channel channel, ChannelUpdate update, List revocationHash, NumberedMessage request, NumberedMessage response) { } + @Override public List getIPObjectsWithActiveChannel () { return null; diff --git a/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java b/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java index 7ba4ce67..1427caa9 100644 --- a/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java +++ b/thunder-core/src/test/java/network/thunder/core/etc/TestTools.java @@ -58,7 +58,7 @@ public static Channel getMockChannel (LNConfiguration configuration) { channel.channelStatus.csvDelay = configuration.DEFAULT_REVOCATION_DELAY; channel.channelStatus.feePerByte = configuration.DEFAULT_FEE_PER_BYTE; channel.masterPrivateKeyServer = Tools.getRandomByte(20); - channel.shaChainDepth = 1; + channel.shaChainDepthCurrent = 1; channel.channelStatus.revoHashServerCurrent = new RevocationHash(1, channel.masterPrivateKeyServer); channel.channelStatus.revoHashClientCurrent = new RevocationHash(1, Tools.getRandomByte(20)); From 19ba2df7ed2c6f321ae83ff26f2cfda2c2c878fc Mon Sep 17 00:00:00 2001 From: Jean-Pierre Rupp Date: Wed, 1 Jun 2016 17:12:45 +0100 Subject: [PATCH 15/21] Fixed fast catchup time for bitcoinj --- .../thunder/core/helper/blockchain/BlockchainHelperImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thunder-core/src/main/java/network/thunder/core/helper/blockchain/BlockchainHelperImpl.java b/thunder-core/src/main/java/network/thunder/core/helper/blockchain/BlockchainHelperImpl.java index 75e89ea1..571b8238 100644 --- a/thunder-core/src/main/java/network/thunder/core/helper/blockchain/BlockchainHelperImpl.java +++ b/thunder-core/src/main/java/network/thunder/core/helper/blockchain/BlockchainHelperImpl.java @@ -107,8 +107,8 @@ public void init () { peerGroup.addPeerDiscovery(new DnsDiscovery(Constants.getNetwork())); peerGroup.setDownloadTxDependencies(false); peerGroup.setBloomFilteringEnabled(false); - - peerGroup.setFastCatchupTimeSecs(System.currentTimeMillis()); + // Setting to less than now - 1 block according to bitcoinj documentation + peerGroup.setFastCatchupTimeSecs(System.currentTimeMillis() / 1000 - 7200); peerGroup.start(); peerGroup.addEventListener(new EventListener(), Threading.SAME_THREAD); From 18613e4ab614cd21036a611d3c8cee23b3885ded Mon Sep 17 00:00:00 2001 From: Jean-Pierre Rupp Date: Wed, 1 Jun 2016 18:25:02 +0100 Subject: [PATCH 16/21] Update bitcoinj with functional interfaces --- thunder-clientgui/pom.xml | 2 +- .../src/main/java/wallettemplate/Main.java | 52 ++------- .../java/wallettemplate/MainController.java | 2 +- .../SendMoneyBlockchainController.java | 8 +- .../wallettemplate/SendMoneyController.java | 2 +- .../wallettemplate/utils/BitcoinUIModel.java | 42 +------ thunder-core/pom.xml | 2 +- .../java/network/thunder/core/MainClient.java | 2 +- .../java/network/thunder/core/MainNode.java | 57 +++------- .../network/thunder/core/ThunderContext.java | 2 +- .../layer/ContextFactoryImpl.java | 2 +- .../blockchain/BlockchainHelperImpl.java | 104 ++++++------------ .../blockchain/MockBlockchainHelper.java | 2 +- .../core/helper/wallet/MockWallet.java | 3 +- .../core/helper/wallet/WalletHelperImpl.java | 1 + .../thunder/core/etc/MockContextFactory.java | 2 +- .../network/thunder/core/etc/NodeWrapper.java | 2 +- 17 files changed, 83 insertions(+), 204 deletions(-) diff --git a/thunder-clientgui/pom.xml b/thunder-clientgui/pom.xml index 9a7608ec..73cfb7cd 100644 --- a/thunder-clientgui/pom.xml +++ b/thunder-clientgui/pom.xml @@ -60,7 +60,7 @@ org.bitcoinj bitcoinj-core - 0.13.1 + 0.14.2 com.google.guava diff --git a/thunder-clientgui/src/main/java/wallettemplate/Main.java b/thunder-clientgui/src/main/java/wallettemplate/Main.java index 28877136..96f02d07 100644 --- a/thunder-clientgui/src/main/java/wallettemplate/Main.java +++ b/thunder-clientgui/src/main/java/wallettemplate/Main.java @@ -17,6 +17,7 @@ import org.bitcoinj.core.*; import org.bitcoinj.kits.WalletAppKit; import org.bitcoinj.script.Script; +import org.bitcoinj.wallet.Wallet; import wallettemplate.controls.NotificationBarPane; import wallettemplate.utils.GuiUtils; import wallettemplate.utils.TextFieldValidator; @@ -84,46 +85,17 @@ private void realStart (Stage mainWindow) throws IOException { wallet = walletAppKit.wallet(); wallet.allowSpendingUnconfirmedTransactions(); wallet.reset(); - wallet.addEventListener(new WalletEventListener() { - @Override - public void onCoinsReceived (Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - System.out.println("wallet = " + wallet); - System.out.println("tx = " + tx); - System.out.println("prevBalance = " + prevBalance); - System.out.println("newBalance = " + newBalance); - } - - @Override - public void onCoinsSent (Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { - System.out.println("wallet = " + wallet); - System.out.println("tx = " + tx); - System.out.println("prevBalance = " + prevBalance); - System.out.println("newBalance = " + newBalance); - } - - @Override - public void onReorganize (Wallet wallet) { - - } - - @Override - public void onTransactionConfidenceChanged (Wallet wallet, Transaction tx) { - - } - - @Override - public void onWalletChanged (Wallet wallet) { - } - - @Override - public void onScriptsChanged (Wallet wallet, List