forked from Qortal/qortal
Added trade-bot websocket
This commit is contained in:
parent
d507383487
commit
cac68ccc14
@ -43,6 +43,7 @@ import org.qortal.api.websocket.ActiveChatsWebSocket;
|
||||
import org.qortal.api.websocket.AdminStatusWebSocket;
|
||||
import org.qortal.api.websocket.BlocksWebSocket;
|
||||
import org.qortal.api.websocket.ChatMessagesWebSocket;
|
||||
import org.qortal.api.websocket.TradeBotWebSocket;
|
||||
import org.qortal.api.websocket.TradeOffersWebSocket;
|
||||
import org.qortal.settings.Settings;
|
||||
|
||||
@ -199,6 +200,7 @@ public class ApiService {
|
||||
context.addServlet(ActiveChatsWebSocket.class, "/websockets/chat/active/*");
|
||||
context.addServlet(ChatMessagesWebSocket.class, "/websockets/chat/messages");
|
||||
context.addServlet(TradeOffersWebSocket.class, "/websockets/crosschain/tradeoffers");
|
||||
context.addServlet(TradeBotWebSocket.class, "/websockets/crosschain/tradebot");
|
||||
|
||||
// Start server
|
||||
this.server.start();
|
||||
|
119
src/main/java/org/qortal/api/websocket/TradeBotWebSocket.java
Normal file
119
src/main/java/org/qortal/api/websocket/TradeBotWebSocket.java
Normal file
@ -0,0 +1,119 @@
|
||||
package org.qortal.api.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.qortal.controller.TradeBot;
|
||||
import org.qortal.data.crosschain.TradeBotData;
|
||||
import org.qortal.event.Event;
|
||||
import org.qortal.event.EventBus;
|
||||
import org.qortal.event.Listener;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.utils.Base58;
|
||||
|
||||
@WebSocket
|
||||
@SuppressWarnings("serial")
|
||||
public class TradeBotWebSocket extends ApiWebSocket implements Listener {
|
||||
|
||||
/** Cache of trade-bot entry states, keyed by trade-bot entry's "trade private key" (base58) */
|
||||
private static final Map<String, TradeBotData.State> PREVIOUS_STATES = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory) {
|
||||
factory.register(TradeBotWebSocket.class);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<TradeBotData> tradeBotEntries = repository.getCrossChainRepository().getAllTradeBotData();
|
||||
if (tradeBotEntries == null)
|
||||
// How do we properly fail here?
|
||||
return;
|
||||
|
||||
PREVIOUS_STATES.putAll(tradeBotEntries.stream().collect(Collectors.toMap(entry -> Base58.encode(entry.getTradePrivateKey()), TradeBotData::getState)));
|
||||
} catch (DataException e) {
|
||||
// No output this time
|
||||
}
|
||||
|
||||
EventBus.INSTANCE.addListener(this::listen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listen(Event event) {
|
||||
if (!(event instanceof TradeBot.StateChangeEvent))
|
||||
return;
|
||||
|
||||
TradeBotData tradeBotData = ((TradeBot.StateChangeEvent) event).getTradeBotData();
|
||||
String tradePrivateKey58 = Base58.encode(tradeBotData.getTradePrivateKey());
|
||||
|
||||
synchronized (PREVIOUS_STATES) {
|
||||
if (PREVIOUS_STATES.get(tradePrivateKey58) == tradeBotData.getState())
|
||||
// Not changed
|
||||
return;
|
||||
|
||||
PREVIOUS_STATES.put(tradePrivateKey58, tradeBotData.getState());
|
||||
}
|
||||
|
||||
List<TradeBotData> tradeBotEntries = Collections.singletonList(tradeBotData);
|
||||
for (Session session : getSessions())
|
||||
sendEntries(session, tradeBotEntries);
|
||||
}
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onWebSocketConnect(Session session) {
|
||||
// Send all known trade-bot entries
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<TradeBotData> tradeBotEntries = repository.getCrossChainRepository().getAllTradeBotData();
|
||||
if (tradeBotEntries == null) {
|
||||
session.close(4001, "repository issue fetching trade-bot entries");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sendEntries(session, tradeBotEntries)) {
|
||||
session.close(4002, "websocket issue");
|
||||
return;
|
||||
}
|
||||
} catch (DataException e) {
|
||||
// No output this time
|
||||
}
|
||||
|
||||
super.onWebSocketConnect(session);
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onWebSocketClose(Session session, int statusCode, String reason) {
|
||||
super.onWebSocketClose(session, statusCode, reason);
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onWebSocketMessage(Session session, String message) {
|
||||
/* ignored */
|
||||
}
|
||||
|
||||
private boolean sendEntries(Session session, List<TradeBotData> tradeBotEntries) {
|
||||
try {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
marshall(stringWriter, tradeBotEntries);
|
||||
|
||||
String output = stringWriter.toString();
|
||||
session.getRemote().sendStringByFuture(output);
|
||||
} catch (IOException e) {
|
||||
// No output this time?
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -30,6 +30,8 @@ import org.qortal.data.crosschain.TradeBotData;
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||
import org.qortal.data.transaction.MessageTransactionData;
|
||||
import org.qortal.event.Event;
|
||||
import org.qortal.event.EventBus;
|
||||
import org.qortal.group.Group;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
@ -47,6 +49,18 @@ public class TradeBot {
|
||||
|
||||
public enum ResponseResult { OK, INSUFFICIENT_FUNDS, BTC_BALANCE_ISSUE, BTC_NETWORK_ISSUE }
|
||||
|
||||
public static class StateChangeEvent implements Event {
|
||||
private final TradeBotData tradeBotData;
|
||||
|
||||
public StateChangeEvent(TradeBotData tradeBotData) {
|
||||
this.tradeBotData = tradeBotData;
|
||||
}
|
||||
|
||||
public TradeBotData getTradeBotData() {
|
||||
return this.tradeBotData;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(TradeBot.class);
|
||||
private static final Random RANDOM = new SecureRandom();
|
||||
private static final long FEE_AMOUNT = 1000L;
|
||||
@ -158,6 +172,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("Built AT %s. Waiting for deployment", atAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
// Return to user for signing and broadcast as we don't have their Qortal private key
|
||||
try {
|
||||
@ -258,6 +273,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("Funding P2SH-A %s. Waiting for confirmation", p2shAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return ResponseResult.OK;
|
||||
}
|
||||
@ -368,6 +384,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("AT %s confirmed ready. Waiting for trade message", tradeBotData.getAtAddress()));
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,6 +422,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("AT %s cancelled. Refunding P2SH-A %s - aborting trade", tradeBotData.getAtAddress(), p2shAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -443,6 +461,7 @@ public class TradeBot {
|
||||
|
||||
LOGGER.info(() -> String.format("P2SH-A %s funding confirmed. Messaged %s. Waiting for AT %s to lock to us",
|
||||
p2shAddress, crossChainTradeData.qortalCreatorTradeAddress, tradeBotData.getAtAddress()));
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -478,6 +497,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("AT %s cancelled - trading aborted", tradeBotData.getAtAddress()));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -550,6 +570,7 @@ public class TradeBot {
|
||||
String p2shBAddress = BTC.getInstance().deriveP2shAddress(redeemScriptBytes);
|
||||
|
||||
LOGGER.info(() -> String.format("Locked AT %s to %s. Waiting for P2SH-B %s", tradeBotData.getAtAddress(), aliceNativeAddress, p2shBAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -558,6 +579,7 @@ public class TradeBot {
|
||||
if (tradeBotData.getLastTransactionSignature() != originalLastTransactionSignature) {
|
||||
repository.getCrossChainRepository().save(tradeBotData);
|
||||
repository.saveChanges();
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +619,8 @@ public class TradeBot {
|
||||
else
|
||||
LOGGER.info(() -> String.format("LockTime-A reached, refunding P2SH-A %s - aborting trade", p2shAddress));
|
||||
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -622,6 +646,8 @@ public class TradeBot {
|
||||
repository.getCrossChainRepository().save(tradeBotData);
|
||||
repository.saveChanges();
|
||||
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -679,6 +705,8 @@ public class TradeBot {
|
||||
|
||||
LOGGER.info(() -> String.format("AT %s locked to us (%s). P2SH-B %s funded. Watching P2SH-B for secret-B",
|
||||
tradeBotData.getAtAddress(), tradeBotData.getTradeNativeAddress(), p2shAddress));
|
||||
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -706,6 +734,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("AT %s has auto-refunded - trade aborted", tradeBotData.getAtAddress()));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -746,6 +775,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("P2SH-B %s redeemed (exposing secret-B). Watching AT %s for secret-A", p2shAddress, tradeBotData.getAtAddress()));
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -782,6 +812,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("LockTime-B reached, refunding P2SH-B %s - aborting trade", p2shAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -825,6 +856,8 @@ public class TradeBot {
|
||||
|
||||
LOGGER.info(() -> String.format("P2SH-B %s redeemed, using secrets to redeem AT %s. Funds should arrive at %s",
|
||||
p2shAddress, tradeBotData.getAtAddress(), receivingAddress));
|
||||
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -867,6 +900,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("AT %s has auto-refunded - trade aborted", tradeBotData.getAtAddress()));
|
||||
notifyStateChange(tradeBotData);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -902,6 +936,7 @@ public class TradeBot {
|
||||
String receivingAddress = BTC.getInstance().pkhToAddress(receivingAccountInfo);
|
||||
|
||||
LOGGER.info(() -> String.format("P2SH-A %s redeemed. Funds should arrive at %s", tradeBotData.getAtAddress(), receivingAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -943,6 +978,7 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("Refunded P2SH-B %s. Waiting for LockTime-A", p2shAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
/** Trade-bot is attempting to refund P2SH-A. */
|
||||
@ -987,6 +1023,12 @@ public class TradeBot {
|
||||
repository.saveChanges();
|
||||
|
||||
LOGGER.info(() -> String.format("LockTime-A reached. Refunded P2SH-A %s. Trade aborted", p2shAddress));
|
||||
notifyStateChange(tradeBotData);
|
||||
}
|
||||
|
||||
private static void notifyStateChange(TradeBotData tradeBotData) {
|
||||
StateChangeEvent stateChangeEvent = new StateChangeEvent(tradeBotData);
|
||||
EventBus.INSTANCE.notify(stateChangeEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user