Commit 1cf48d9e authored by zeroleak's avatar zeroleak
Browse files

use java-websocket-server

parent b4c51861
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>io.samourai.code.whirlpool</groupId> <groupId>io.samourai.code.whirlpool</groupId>
<artifactId>whirlpool-server</artifactId> <artifactId>whirlpool-server</artifactId>
<version>0.23.21</version> <version>0.23.22-SNAPSHOT</version>
<name>whirlpool-server</name> <name>whirlpool-server</name>
<properties> <properties>
<spring-boot.version>2.1.6.RELEASE</spring-boot.version> <spring-boot.version>2.1.6.RELEASE</spring-boot.version>
...@@ -32,15 +32,20 @@ ...@@ -32,15 +32,20 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.samourai.code.wallet</groupId> <groupId>io.samourai.code.whirlpool</groupId>
<artifactId>extlibj</artifactId> <artifactId>java-websocket-server</artifactId>
<version>0.0.16-fetchWallet</version> <version>1.0.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>io.samourai.code.wallet</groupId>
<artifactId>extlibj</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>io.samourai.code.wallet</groupId>
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>extlibj</artifactId>
<version>${spring-boot.version}</version> <version>0.0.18-fetchWallet</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
...@@ -48,12 +53,6 @@ ...@@ -48,12 +53,6 @@
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
<version>${spring-security.version}</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId> <artifactId>commons-text</artifactId>
...@@ -81,6 +80,7 @@ ...@@ -81,6 +80,7 @@
<artifactId>micrometer-registry-prometheus</artifactId> <artifactId>micrometer-registry-prometheus</artifactId>
<version>1.6.0</version> <version>1.6.0</version>
</dependency> </dependency>
<!-- test -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
......
...@@ -3,6 +3,7 @@ package com.samourai.whirlpool.server.config; ...@@ -3,6 +3,7 @@ package com.samourai.whirlpool.server.config;
import com.samourai.http.client.HttpUsage; import com.samourai.http.client.HttpUsage;
import com.samourai.javaserver.config.ServerServicesConfig; import com.samourai.javaserver.config.ServerServicesConfig;
import com.samourai.javaserver.utils.ServerUtils; import com.samourai.javaserver.utils.ServerUtils;
import com.samourai.javawsserver.config.JWSSConfig;
import com.samourai.wallet.api.explorer.ExplorerApi; import com.samourai.wallet.api.explorer.ExplorerApi;
import com.samourai.wallet.bip47.rpc.java.SecretPointFactoryJava; import com.samourai.wallet.bip47.rpc.java.SecretPointFactoryJava;
import com.samourai.wallet.bip47.rpc.secretPoint.ISecretPointFactory; import com.samourai.wallet.bip47.rpc.secretPoint.ISecretPointFactory;
...@@ -12,6 +13,7 @@ import com.samourai.wallet.util.CryptoTestUtil; ...@@ -12,6 +13,7 @@ import com.samourai.wallet.util.CryptoTestUtil;
import com.samourai.wallet.util.FormatsUtilGeneric; import com.samourai.wallet.util.FormatsUtilGeneric;
import com.samourai.wallet.util.MessageSignUtilGeneric; import com.samourai.wallet.util.MessageSignUtilGeneric;
import com.samourai.wallet.util.TxUtil; import com.samourai.wallet.util.TxUtil;
import com.samourai.whirlpool.protocol.WhirlpoolEndpoint;
import com.samourai.whirlpool.protocol.WhirlpoolProtocol; import com.samourai.whirlpool.protocol.WhirlpoolProtocol;
import com.samourai.whirlpool.protocol.fee.WhirlpoolFee; import com.samourai.whirlpool.protocol.fee.WhirlpoolFee;
import com.samourai.whirlpool.server.services.JavaHttpClientService; import com.samourai.whirlpool.server.services.JavaHttpClientService;
...@@ -105,4 +107,24 @@ public class ServicesConfig extends ServerServicesConfig { ...@@ -105,4 +107,24 @@ public class ServicesConfig extends ServerServicesConfig {
return new XManagerClient( return new XManagerClient(
serverConfig.isTestnet(), false, httpClient.getHttpClient(HttpUsage.COORDINATOR_REST)); serverConfig.isTestnet(), false, httpClient.getHttpClient(HttpUsage.COORDINATOR_REST));
} }
@Bean
JWSSConfig jwssConfig() {
String[] endpoints =
new String[] {
WhirlpoolEndpoint.WS_CONNECT,
WhirlpoolEndpoint.WS_REGISTER_INPUT,
WhirlpoolEndpoint.WS_CONFIRM_INPUT,
WhirlpoolEndpoint.WS_REVEAL_OUTPUT,
WhirlpoolEndpoint.WS_SIGNING
};
String WS_PREFIX = "/ws/";
String WS_PREFIX_DESTINATION = "/topic/";
return new JWSSConfig(
endpoints,
WhirlpoolProtocol.WS_PREFIX_USER_PRIVATE,
WS_PREFIX,
WS_PREFIX_DESTINATION, // NOT USED
WhirlpoolProtocol.WS_PREFIX_USER_REPLY);
}
} }
package com.samourai.whirlpool.server.config.security;
import com.samourai.javawsserver.config.JWSSConfig;
import com.samourai.javawsserver.config.JWSSWebSocketConfigurationSupport;
import com.samourai.whirlpool.server.services.WSSessionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WSConfigurationSupport extends JWSSWebSocketConfigurationSupport {
@Autowired
public WSConfigurationSupport(JWSSConfig config, WSSessionService sessionService) {
super(config, sessionService);
}
}
package com.samourai.whirlpool.server.config.security;
import com.samourai.javawsserver.config.JWSSConfig;
import com.samourai.javawsserver.config.JWSSWebSocketSecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WSSecurityConfig extends JWSSWebSocketSecurityConfig {
@Autowired
public WSSecurityConfig(JWSSConfig config) {
super(config);
}
}
package com.samourai.whirlpool.server.config.security; package com.samourai.whirlpool.server.config.security;
import com.samourai.javaserver.config.ServerServicesConfig; import com.samourai.javaserver.config.ServerServicesConfig;
import com.samourai.javawsserver.config.JWSSConfig;
import com.samourai.whirlpool.protocol.WhirlpoolEndpoint; import com.samourai.whirlpool.protocol.WhirlpoolEndpoint;
import com.samourai.whirlpool.server.config.websocket.WebSocketConfig;
import com.samourai.whirlpool.server.controllers.rest.SystemController; import com.samourai.whirlpool.server.controllers.rest.SystemController;
import com.samourai.whirlpool.server.controllers.web.*; import com.samourai.whirlpool.server.controllers.web.*;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
...@@ -43,6 +44,13 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ...@@ -43,6 +44,13 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
SystemController.ENDPOINT_HEALTH SystemController.ENDPOINT_HEALTH
}; };
private JWSSConfig config;
@Autowired
public WebSecurityConfig(JWSSConfig config) {
this.config = config;
}
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
String WS_CONNECT_XHR = WhirlpoolEndpoint.WS_CONNECT + "/**"; String WS_CONNECT_XHR = WhirlpoolEndpoint.WS_CONNECT + "/**";
...@@ -68,7 +76,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ...@@ -68,7 +76,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.permitAll() .permitAll()
// public mixing websocket // public mixing websocket
.antMatchers(ArrayUtils.addAll(WebSocketConfig.WEBSOCKET_ENDPOINTS, WS_CONNECT_XHR)) .antMatchers(ArrayUtils.addAll(config.getWebsocketEndpoints(), WS_CONNECT_XHR))
.permitAll() .permitAll()
.antMatchers(REST_MIX_ENDPOINTS) .antMatchers(REST_MIX_ENDPOINTS)
.permitAll() .permitAll()
......
package com.samourai.whirlpool.server.config.security;
import com.samourai.whirlpool.server.config.websocket.WebSocketConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
super.configureInbound(messages);
messages
// allow websocket server endpoints
.simpMessageDestMatchers(WebSocketConfig.WEBSOCKET_ENDPOINTS)
.permitAll()
// deny any other messages (including client-to-client)
.simpMessageDestMatchers("/**")
.denyAll();
}
@Override
protected boolean sameOriginDisabled() {
return true;
}
}
package com.samourai.whirlpool.server.config.websocket;
import com.sun.security.auth.UserPrincipal;
import java.lang.invoke.MethodHandles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
/**
* Assign a principal for each websocket client. This is needed to be able to communicate with a
* specific client.
*/
public class AssignPrincipalChannelInterceptor implements ChannelInterceptor {
private static Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
if (log.isTraceEnabled()) {
log.trace(
"Assigning principal from sessionId: login="
+ (accessor.getLogin() != null ? accessor.getLogin() : "null")
+ ",sessionId="
+ accessor.getSessionId());
}
accessor.setUser(new UserPrincipal(accessor.getSessionId()));
}
return message;
}
}
package com.samourai.whirlpool.server.config.websocket;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
public class IpHandshakeInterceptor implements HandshakeInterceptor {
private static final String ATTR_IP = "ip";
public boolean beforeHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Map<String, Object> attributes)
throws Exception {
// Set ip attribute to WebSocket session
attributes.put(ATTR_IP, request.getRemoteAddress().getAddress().getHostAddress());
return true;
}
public void afterHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Exception exception) {}
public static String getIp(SimpMessageHeaderAccessor messageHeaderAccessor) {
return (String)
messageHeaderAccessor.getSessionAttributes().get(IpHandshakeInterceptor.ATTR_IP);
}
}
package com.samourai.whirlpool.server.config.websocket;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.samourai.whirlpool.protocol.WhirlpoolEndpoint;
import com.samourai.whirlpool.protocol.WhirlpoolProtocol;
import com.samourai.whirlpool.server.services.WebSocketSessionService;
import java.lang.invoke.MethodHandles;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.converter.DefaultContentTypeResolver;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurationSupport;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.messaging.SessionConnectEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;
/** Websocket configuration with STOMP. */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
implements WebSocketMessageBrokerConfigurer {
private static Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final int HEARTBEAT_DELAY = 20000;
public static String[] WEBSOCKET_ENDPOINTS =
new String[] {
WhirlpoolEndpoint.WS_CONNECT,
WhirlpoolEndpoint.WS_REGISTER_INPUT,
WhirlpoolEndpoint.WS_CONFIRM_INPUT,
WhirlpoolEndpoint.WS_REVEAL_OUTPUT,
WhirlpoolEndpoint.WS_SIGNING
};
@Autowired private ObjectMapper objectMapper;
@Autowired private WhirlpoolProtocol whirlpoolProtocol;
@Autowired private WebSocketSessionService webSocketSessionService;
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
super.configureWebSocketTransport(registry);
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
super.configureClientInboundChannel(registration);
registration.interceptors(new AssignPrincipalChannelInterceptor());
}
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
super.configureClientOutboundChannel(registration);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
super.addReturnValueHandlers(returnValueHandlers);
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint(WEBSOCKET_ENDPOINTS)
.setAllowedOrigins("*")
.addInterceptors(new IpHandshakeInterceptor())
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//// registry.setPreservePublishOrder()
// enable heartbeat (mandatory to detect client disconnect)
ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
te.setPoolSize(1);
te.setThreadNamePrefix("wss-heartbeat-thread-");
te.initialize();
registry
////// .setApplicationDestinationPrefixes(WS_WEBSOCKET_ENDPOINTS.WS_PREFIX)
.enableSimpleBroker(whirlpoolProtocol.WS_PREFIX_USER_REPLY)
.setHeartbeatValue(new long[] {HEARTBEAT_DELAY, HEARTBEAT_DELAY})
.setTaskScheduler(te);
registry.setUserDestinationPrefix(whirlpoolProtocol.WS_PREFIX_USER_PRIVATE);
}
@Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON);
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(objectMapper);
converter.setContentTypeResolver(resolver);
messageConverters.add(converter);
return false;
}
// listeners for logging purpose
@EventListener
public void handleSubscribeEvent(SessionSubscribeEvent event) {
String username = event.getUser() != null ? event.getUser().getName() : "unknown";
if (log.isDebugEnabled()) {
log.debug("(<) " + username + " subscribe");
}
}
@EventListener
public void handleConnectEvent(SessionConnectEvent event) {
String username = event.getUser() != null ? event.getUser().getName() : "unknown";
if (log.isDebugEnabled()) {
log.debug("(<) " + username + " connect");
}
webSocketSessionService.onConnect(username);
}
@EventListener
public void handleDisconnectEvent(SessionDisconnectEvent event) {
String username = event.getUser() != null ? event.getUser().getName() : "unknown";
if (log.isDebugEnabled()) {
log.debug("(<) " + username + " disconnect");
}
webSocketSessionService.onDisconnect(username);
}
}
...@@ -2,13 +2,13 @@ package com.samourai.whirlpool.server.controllers.websocket; ...@@ -2,13 +2,13 @@ package com.samourai.whirlpool.server.controllers.websocket;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.samourai.javaserver.exceptions.NotifiableException; import com.samourai.javaserver.exceptions.NotifiableException;
import com.samourai.javawsserver.interceptors.JWSSIpHandshakeInterceptor;
import com.samourai.whirlpool.protocol.WhirlpoolProtocol; import com.samourai.whirlpool.protocol.WhirlpoolProtocol;
import com.samourai.whirlpool.server.beans.export.ActivityCsv; import com.samourai.whirlpool.server.beans.export.ActivityCsv;
import com.samourai.whirlpool.server.config.websocket.IpHandshakeInterceptor;
import com.samourai.whirlpool.server.exceptions.IllegalInputException; import com.samourai.whirlpool.server.exceptions.IllegalInputException;
import com.samourai.whirlpool.server.services.ExportService; import com.samourai.whirlpool.server.services.ExportService;
import com.samourai.whirlpool.server.services.RegisterInputService; import com.samourai.whirlpool.server.services.RegisterInputService;
import com.samourai.whirlpool.server.services.WebSocketService; import com.samourai.whirlpool.server.services.WSMessageService;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.security.Principal; import java.security.Principal;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
...@@ -23,12 +23,12 @@ import org.springframework.messaging.simp.stomp.StompHeaderAccessor; ...@@ -23,12 +23,12 @@ import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
public abstract class AbstractWebSocketController { public abstract class AbstractWebSocketController {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private WebSocketService webSocketService; private WSMessageService WSMessageService;
private ExportService exportService; private ExportService exportService;
public AbstractWebSocketController( public AbstractWebSocketController(
WebSocketService webSocketService, ExportService exportService) { WSMessageService WSMessageService, ExportService exportService) {
this.webSocketService = webSocketService; this.WSMessageService = WSMessageService;
this.exportService = exportService; this.exportService = exportService;
} }
...@@ -57,7 +57,7 @@ public abstract class AbstractWebSocketController { ...@@ -57,7 +57,7 @@ public abstract class AbstractWebSocketController {
NotifiableException notifiable = NotifiableException.computeNotifiableException(e); NotifiableException notifiable = NotifiableException.computeNotifiableException(e);
String message = notifiable.getMessage(); String message = notifiable.getMessage();
String username = principal.getName(); String username = principal.getName();
webSocketService.sendPrivateError(username, message); WSMessageService.sendPrivateError(username, message);
// skip healthCheck // skip healthCheck
if (!RegisterInputService.HEALTH_CHECK_SUCCESS.equals(message)) { if (!RegisterInputService.HEALTH_CHECK_SUCCESS.equals(message)) {
...@@ -71,7 +71,7 @@ public abstract class AbstractWebSocketController { ...@@ -71,7 +71,7 @@ public abstract class AbstractWebSocketController {
Map<String, String> clientDetails = computeClientDetails(messageHeaderAccessor); Map<String, String> clientDetails = computeClientDetails(messageHeaderAccessor);
clientDetails.put("u", username); clientDetails.put("u", username);
Map<String, String> details = ImmutableMap.of("error", message); Map<String, String> details = ImmutableMap.of("error", message);
String ip = IpHandshakeInterceptor.getIp(messageHeaderAccessor); String ip = JWSSIpHandshakeInterceptor.getIp(messageHeaderAccessor);
ActivityCsv activityCsv = new ActivityCsv(activity, null, details, ip, clientDetails); ActivityCsv activityCsv = new ActivityCsv(activity, null, details, ip, clientDetails);
getExportService().exportActivity(activityCsv); getExportService().exportActivity(activityCsv);
} }
...@@ -101,8 +101,8 @@ public abstract class AbstractWebSocketController { ...@@ -101,8 +101,8 @@ public abstract class AbstractWebSocketController {
return clientDetails; return clientDetails;
} }
protected WebSocketService getWebSocketService() { protected WSMessageService getWSMessageService() {
return webSocketService; return WSMessageService;
} }
protected ExportService getExportService() { protected ExportService getExportService() {
......
...@@ -5,7 +5,7 @@ import com.samourai.whirlpool.protocol.WhirlpoolProtocol; ...@@ -5,7 +5,7 @@ import com.samourai.whirlpool.protocol.WhirlpoolProtocol;
import com.samourai.whirlpool.protocol.websocket.messages.ConfirmInputRequest; import com.samourai.whirlpool.protocol.websocket.messages.ConfirmInputRequest;
import com.samourai.whirlpool.server.services.ConfirmInputService; import com.samourai.whirlpool.server.services.ConfirmInputService;
import com.samourai.whirlpool.server.services.ExportService; import com.samourai.whirlpool.server.services.ExportService;
import com.samourai.whirlpool.server.services.WebSocketService; import com.samourai.whirlpool.server.services.WSMessageService;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.security.Principal; import java.security.Principal;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -26,10 +26,10 @@ public class ConfirmInputController extends AbstractWebSocketController { ...@@ -26,10 +26,10 @@ public class ConfirmInputController extends AbstractWebSocketController {
@Autowired @Autowired