package network.thunder.core.communication.nio.handler.mid;
import io.netty.channel.embedded.EmbeddedChannel;
import network.thunder.core.communication.Message;
import network.thunder.core.communication.nio.handler.ProcessorHandler;
import network.thunder.core.communication.objects.lightning.subobjects.PaymentData;
import network.thunder.core.communication.objects.messages.impl.message.lnpayment.LNPaymentAMessage;
import network.thunder.core.communication.objects.messages.impl.message.lnpayment.LNPaymentBMessage;
import network.thunder.core.communication.objects.messages.impl.message.lnpayment.LNPaymentCMessage;
import network.thunder.core.communication.objects.messages.impl.message.lnpayment.LNPaymentDMessage;
import network.thunder.core.communication.objects.messages.interfaces.factories.ContextFactory;
import network.thunder.core.communication.objects.messages.interfaces.factories.LNPaymentMessageFactory;
import network.thunder.core.communication.objects.messages.interfaces.helper.LNPaymentHelper;
import network.thunder.core.communication.objects.subobjects.PaymentSecret;
import network.thunder.core.communication.processor.implementations.lnpayment.LNPaymentProcessorImpl;
import network.thunder.core.communication.processor.interfaces.lnpayment.LNPaymentLogic;
import network.thunder.core.communication.processor.interfaces.lnpayment.LNPaymentProcessor;
import network.thunder.core.database.DBHandler;
import network.thunder.core.etc.*;
import network.thunder.core.mesh.NodeClient;
import network.thunder.core.mesh.NodeServer;
import org.junit.Before;
import org.junit.Test;
import java.beans.PropertyVetoException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.sql.SQLException;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
/**
* Created by matsjerratsch on 02/11/2015.
*/
public class LNPaymentHandlerTest {
EmbeddedChannel channel12;
EmbeddedChannel channel21;
NodeServer nodeServer1 = new NodeServer();
NodeServer nodeServer2 = new NodeServer();
NodeClient node1 = new NodeClient(nodeServer2);
NodeClient node2 = new NodeClient(nodeServer1);
LNPaymentProcessorImpl processor12;
LNPaymentProcessorImpl processor21;
DBHandler dbHandler1 = new LNPaymentDBHandlerMock();
DBHandler dbHandler2 = new LNPaymentDBHandlerMock();
ContextFactory contextFactory12 = new MockLNPaymentContextFactory(nodeServer1, dbHandler1);
ContextFactory contextFactory21 = new MockLNPaymentContextFactory(nodeServer2, dbHandler2);
@Before
public void prepare () throws PropertyVetoException, SQLException {
node1.isServer = false;
node2.isServer = true;
this.node1.name = "LNPayment12";
this.node2.name = "LNPayment21";
processor12 = new LNPaymentProcessorImpl(contextFactory12, dbHandler1, this.node1);
processor21 = new LNPaymentProcessorImpl(contextFactory21, dbHandler2, this.node2);
channel12 = new EmbeddedChannel(new ProcessorHandler(processor12, "LNPayment12"));
channel21 = new EmbeddedChannel(new ProcessorHandler(processor21, "LNPayment21"));
Message m = (Message) channel21.readOutbound();
assertNull(m);
}
public void after () {
channel12.checkException();
channel21.checkException();
}
@Test
public void fullExchangeWithNoDisturbanceWithinTimeframe () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException {
processor12.makePayment(getMockPaymentData());
Thread.sleep(200);
exchangeMessages(channel12, channel21, LNPaymentAMessage.class);
exchangeMessages(channel21, channel12, LNPaymentBMessage.class);
exchangeMessages(channel12, channel21, LNPaymentCMessage.class);
exchangeMessages(channel21, channel12, LNPaymentCMessage.class);
exchangeMessages(channel12, channel21, LNPaymentDMessage.class);
exchangeMessages(channel21, channel12, LNPaymentDMessage.class);
assertNull(channel12.readOutbound());
assertNull(channel21.readOutbound());
after();
}
@Test
public void exchangeWithDelayShouldRestart () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException {
processor12.makePayment(getMockPaymentData());
Thread.sleep(2000);
exchangeMessages(channel12, channel21);
exchangeMessages(channel21, channel12);
Message message = (Message) channel12.readOutbound();
Thread.sleep(LNPaymentProcessor.TIMEOUT_NEGOTIATION + 5000);
System.out.println(message);
channel21.writeInbound(message);
assertNull(channel21.readOutbound());
exchangeMessages(channel12, channel21, LNPaymentAMessage.class);
exchangeMessages(channel21, channel12, LNPaymentBMessage.class);
after();
}
@Test
public void exchangeWithOtherPartyStartingOwnExchange () throws NoSuchProviderException, NoSuchAlgorithmException, InterruptedException {
processor12.makePayment(getMockPaymentData());
Thread.sleep(200);
exchangeMessages(channel12, channel21, LNPaymentAMessage.class);
exchangeMessages(channel21, channel12, LNPaymentBMessage.class);
exchangeMessages(channel12, channel21, LNPaymentCMessage.class);
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);
exchangeMessages(channel21, channel12, LNPaymentAMessage.class);
exchangeMessages(channel12, channel21, LNPaymentBMessage.class);
exchangeMessages(channel21, channel12, LNPaymentCMessage.class);
exchangeMessages(channel12, channel21, LNPaymentCMessage.class);
exchangeMessages(channel21, channel12, LNPaymentDMessage.class);
exchangeMessages(channel12, channel21, LNPaymentDMessage.class);
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;
channel12.writeInbound(message2);
channel21.writeInbound(message1);
if (dice2 > dice1) {
EmbeddedChannel channel3 = channel12;
channel12 = channel21;
channel21 = channel3;
}
exchangeMessages(channel21, channel12, LNPaymentBMessage.class);
exchangeMessages(channel12, channel21, LNPaymentCMessage.class);
exchangeMessages(channel21, channel12, LNPaymentCMessage.class);
exchangeMessages(channel12, channel21, LNPaymentDMessage.class);
exchangeMessages(channel21, channel12, LNPaymentDMessage.class);
Thread.sleep(200);
exchangeMessages(channel21, channel12, LNPaymentAMessage.class);
exchangeMessages(channel12, channel21, LNPaymentBMessage.class);
exchangeMessages(channel21, channel12, LNPaymentCMessage.class);
exchangeMessages(channel12, channel21, LNPaymentCMessage.class);
exchangeMessages(channel21, channel12, LNPaymentDMessage.class);
exchangeMessages(channel12, channel21, LNPaymentDMessage.class);
after();
}
public void exchangePayment (EmbeddedChannel from, EmbeddedChannel to) {
exchangeMessages(from, to, LNPaymentAMessage.class);
exchangeMessages(to, from, LNPaymentBMessage.class);
exchangeMessages(from, to, LNPaymentCMessage.class);
exchangeMessages(to, from, LNPaymentCMessage.class);
exchangeMessages(from, to, LNPaymentDMessage.class);
exchangeMessages(to, from, LNPaymentDMessage.class);
}
public static void exchangeMessages (EmbeddedChannel from, EmbeddedChannel to) {
Object message = from.readOutbound();
System.out.println(message);
if (message != null) {
to.writeInbound(message);
}
}
public static void exchangeMessages (EmbeddedChannel from, EmbeddedChannel to, Class expectedMessage) {
Object message = from.readOutbound();
assertThat(message, instanceOf(expectedMessage));
if (message != null) {
to.writeInbound(message);
}
}
public static void exchangeMessagesDuplex (EmbeddedChannel from, EmbeddedChannel to) {
exchangeMessages(from, to);
exchangeMessages(to, from);
}
public PaymentData getMockPaymentData () {
PaymentData paymentData = new PaymentData();
paymentData.sending = true;
paymentData.amount = 10000;
paymentData.secret = new PaymentSecret(Tools.getRandomByte(20));
return paymentData;
}
public void connectChannel (EmbeddedChannel from, EmbeddedChannel to) {
new Thread(new Runnable() {
@Override
public void run () {
while (true) {
exchangeMessagesDuplex(from, to);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
class MockLNPaymentContextFactory extends MockContextFactory {
public MockLNPaymentContextFactory (NodeServer 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();
}
}
}