package network.thunder.core.communication.processor.implementations.management; import network.thunder.core.communication.objects.messages.interfaces.helper.BlockchainHelper; import network.thunder.core.communication.objects.messages.interfaces.helper.ChannelManager; import network.thunder.core.communication.objects.messages.interfaces.helper.etc.OnBlockCommand; import network.thunder.core.communication.objects.messages.interfaces.helper.etc.OnTxCommand; import network.thunder.core.communication.processor.implementations.LNEstablishProcessorImpl; import network.thunder.core.communication.processor.interfaces.LNEstablishProcessor; import network.thunder.core.database.objects.Channel; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionInput; public class ChannelBlockchainWatcher extends BlockchainWatcher { private Channel channel; private ChannelManager channelManager; private boolean started; private boolean stopped; boolean seen = false; boolean confirmed = false; boolean anchorDone = false; int confirmations = 0; int blockSince = 0; OnTxCommand anchorSuccess; OnTxCommand anchorEscape; OnBlockCommand blockCommand; public ChannelBlockchainWatcher (BlockchainHelper blockchainHelper, ChannelManager channelManager, Channel channel) { super(blockchainHelper); this.channel = channel; this.channelManager = channelManager; } @Override public void start () { Transaction tx = blockchainHelper.getTransaction(channel.anchorTxHashClient); if (tx != null) { int depth = tx.getConfidence().getDepthInBlocks(); if (depth > 0) { confirmed = true; confirmations = depth; } } anchorSuccess = new OnTxCommand() { @Override public boolean compare (Transaction tx) { if (stopped) { return true; } else { return channel.getAnchorTxHashClient().equals(tx.getHash()); } } @Override public void execute (Transaction tx) { if (stopped) { return; } else if (LNEstablishProcessorImpl.MIN_CONFIRMATIONS == 0) { channelManager.onAnchorDone(channel); } } }; anchorEscape = new OnTxCommand() { @Override public boolean compare (Transaction tx) { if (stopped) { return true; } for (TransactionInput input : tx.getInputs()) { if (input.getOutpoint().getHash().equals(channel.anchorTxHashClient) || input.getOutpoint().getHash().equals(channel.anchorTxHashServer)) { return true; } } return false; } @Override public void execute (Transaction tx) { if (stopped) { return; } else { channelManager.onAnchorFailure(channel, new AnchorSpentChannelFailure(channel)); } } }; blockCommand = block -> { if (stopped) { return true; } if (!confirmed) { if (block.getTransactions().contains(tx)) { confirmed = true; } } if (confirmed) { confirmations++; } else { blockSince++; } if (confirmations >= LNEstablishProcessorImpl.MIN_CONFIRMATIONS) { channelManager.onAnchorDone(channel); return true; } else { if ((blockSince > LNEstablishProcessor.MAX_WAIT_FOR_OTHER_TX_IF_SEEN && seen) || (blockSince > LNEstablishProcessor.MAX_WAIT_FOR_OTHER_TX && !seen)) { channelManager.onAnchorFailure(channel, new AnchorNotFoundChannelFailure(channel)); } } return false; }; blockchainHelper.addBlockListener(this.blockCommand); blockchainHelper.addTxListener(this.anchorEscape); blockchainHelper.addTxListener(this.anchorSuccess); } @Override public void stop () { } }