added grpc network listener annd new message spec
Some checks failed
CI / build (macos-13) (push) Has been cancelled
CI / build (ubuntu-22.04) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
Codacy Coverage Reporter / Publish coverage (push) Has been cancelled

This commit is contained in:
Kewbit 2025-06-30 06:59:30 +07:00
parent d3945fff7e
commit 285b853a58
7 changed files with 200 additions and 67 deletions

View file

@ -102,6 +102,7 @@ public class CoreApi {
private final CoreWalletsService walletsService; private final CoreWalletsService walletsService;
private final TradeStatisticsManager tradeStatisticsManager; private final TradeStatisticsManager tradeStatisticsManager;
private final CoreNotificationService notificationService; private final CoreNotificationService notificationService;
private final CoreNetworkService networkService;
private final XmrConnectionService xmrConnectionService; private final XmrConnectionService xmrConnectionService;
private final XmrLocalNode xmrLocalNode; private final XmrLocalNode xmrLocalNode;
@ -119,6 +120,7 @@ public class CoreApi {
CoreWalletsService walletsService, CoreWalletsService walletsService,
TradeStatisticsManager tradeStatisticsManager, TradeStatisticsManager tradeStatisticsManager,
CoreNotificationService notificationService, CoreNotificationService notificationService,
CoreNetworkService networkService,
XmrConnectionService xmrConnectionService, XmrConnectionService xmrConnectionService,
XmrLocalNode xmrLocalNode) { XmrLocalNode xmrLocalNode) {
this.config = config; this.config = config;
@ -134,6 +136,7 @@ public class CoreApi {
this.walletsService = walletsService; this.walletsService = walletsService;
this.tradeStatisticsManager = tradeStatisticsManager; this.tradeStatisticsManager = tradeStatisticsManager;
this.notificationService = notificationService; this.notificationService = notificationService;
this.networkService = networkService;
this.xmrConnectionService = xmrConnectionService; this.xmrConnectionService = xmrConnectionService;
this.xmrLocalNode = xmrLocalNode; this.xmrLocalNode = xmrLocalNode;
} }
@ -355,6 +358,14 @@ public class CoreApi {
notificationService.sendNotification(notification); notificationService.sendNotification(notification);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Network
///////////////////////////////////////////////////////////////////////////////////////////
public void addNetworkListener(NetworkListener listener) {
networkService.addListener(listener);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Disputes // Disputes
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,41 @@
package haveno.core.api;
import com.google.inject.Singleton;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import haveno.proto.grpc.NetworkMessage;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@Singleton
@Slf4j
public class CoreNetworkService {
private final Object lock = new Object();
private final List<NetworkListener> listeners = new LinkedList<>();
public void addListener(@NonNull NetworkListener listener) {
synchronized (lock) {
listeners.add(listener);
}
}
public void sendNetworMessage(@NonNull NetworkMessage network_message) {
synchronized (lock) {
for (Iterator<NetworkListener> iter = listeners.iterator(); iter.hasNext(); ) {
NetworkListener listener = iter.next();
try {
listener.onMessage(network_message);
} catch (RuntimeException e) {
log.warn("Failed to send network envelope to listener {}: {}", listener, e.getMessage());
iter.remove();
}
}
}
}
}

View file

@ -0,0 +1,9 @@
package haveno.core.api;
import haveno.proto.grpc.NetworkMessage;
import lombok.NonNull;
public interface NetworkListener {
void onMessage(@NonNull NetworkMessage network_message);
}

View file

@ -107,11 +107,11 @@ public class XmrWalletService extends XmrWalletBase {
// monero configuration // monero configuration
public static final int NUM_BLOCKS_UNLOCK = 10; public static final int NUM_BLOCKS_UNLOCK = 10;
public static final String MONERO_BINS_DIR = Config.appDataDir().getAbsolutePath(); public static final String MONERO_BINS_DIR = System.getenv().getOrDefault("MONERO_BINS_DIR", Config.appDataDir().getAbsolutePath());
public static final String MONERO_WALLET_RPC_NAME = Utilities.isWindows() ? "monero-wallet-rpc.exe" : "monero-wallet-rpc"; public static final String MONERO_WALLET_RPC_NAME = Utilities.isWindows() ? "monero-wallet-rpc.exe" : "monero-wallet-rpc";
public static final String MONERO_WALLET_RPC_PATH = MONERO_BINS_DIR + File.separator + MONERO_WALLET_RPC_NAME; public static final String MONERO_WALLET_RPC_PATH = MONERO_BINS_DIR + File.separator + MONERO_WALLET_RPC_NAME;
public static final MoneroTxPriority PROTOCOL_FEE_PRIORITY = MoneroTxPriority.DEFAULT; public static final MoneroTxPriority PROTOCOL_FEE_PRIORITY = MoneroTxPriority.DEFAULT;
public static final int MONERO_LOG_LEVEL = 1; // monero library log level, -1 to disable public static final int MONERO_LOG_LEVEL = -1; // monero library log level, -1 to disable
private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType(); private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType();
private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager(); private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
private static final String MONERO_WALLET_RPC_USERNAME = "haveno_user"; private static final String MONERO_WALLET_RPC_USERNAME = "haveno_user";

View file

@ -0,0 +1,81 @@
package haveno.daemon.grpc;
import com.google.inject.Inject;
import haveno.core.api.CoreApi;
import haveno.core.api.NetworkListener;
import haveno.daemon.grpc.interceptor.CallRateMeteringInterceptor;
import haveno.daemon.grpc.interceptor.GrpcCallRateMeter;
import static haveno.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
import haveno.proto.grpc.NetworkGrpc.NetworkImplBase;
import haveno.proto.grpc.NetworkMessage;
import static haveno.proto.grpc.NetworkGrpc.getRegisterNetworkListenerMethod;
import haveno.proto.grpc.RegisterNetworkListenerRequest;
import io.grpc.Context;
import io.grpc.ServerInterceptor;
import io.grpc.stub.ServerCallStreamObserver;
import io.grpc.stub.StreamObserver;
import java.util.HashMap;
import java.util.Optional;
import static java.util.concurrent.TimeUnit.SECONDS;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class GrpcNetworkService extends NetworkImplBase {
private final CoreApi coreApi;
private final GrpcExceptionHandler exceptionHandler;
@Inject
public GrpcNetworkService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) {
this.coreApi = coreApi;
this.exceptionHandler = exceptionHandler;
}
@Override
public void registerNetworkListener(RegisterNetworkListenerRequest request,
StreamObserver<NetworkMessage> responseObserver) {
Context ctx = Context.current().fork(); // context is independent for long-lived request
ctx.run(() -> {
try {
coreApi.addNetworkListener(new GrpcNetworkListener(responseObserver));
// No onNext / onCompleted, as the response observer should be kept open
} catch (Throwable t) {
exceptionHandler.handleException(log, t, responseObserver);
}
});
}
@Value
private static class GrpcNetworkListener implements NetworkListener {
@NonNull
StreamObserver<NetworkMessage> responseObserver;
@Override
public void onMessage(@NonNull NetworkMessage network_message) {
if (!((ServerCallStreamObserver<NetworkMessage>) responseObserver).isCancelled()) {
responseObserver.onNext(network_message);
}
}
}
final ServerInterceptor[] interceptors() {
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
return rateMeteringInterceptor.map(serverInterceptor ->
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
}
final Optional<ServerInterceptor> rateMeteringInterceptor() {
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
new HashMap<>() {{
put(getRegisterNetworkListenerMethod().getFullMethodName(), new GrpcCallRateMeter(10, SECONDS));
}}
)));
}
}

View file

@ -1,65 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=4SFl
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -1125,6 +1125,62 @@ message AddressBalanceInfo {
bool is_address_unused = 4; bool is_address_unused = 4;
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Network
///////////////////////////////////////////////////////////////////////////////////////////
service Network {
rpc GetOnlinePeers (GetOnlinePeersRequest) returns (GetOnlinePeersReply) {
}
rpc GetSeednodes (GetSeednodesRequest) returns (GetSeednodesReply) {
}
rpc GetRegisteredArbitrators (GetRegisteredArbitratorsRequest) returns (GetRegisteredArbitratorsReply) {
}
rpc GetNetworkFilter (GetNetworkFilterRequest) returns (GetNetworkFilterReply) {
}
rpc GetAlerts (GetAlertsRequest) returns (GetAlertsRequest) {
}
rpc RegisterNetworkListener (RegisterNetworkListenerRequest) returns (stream NetworkMessage) {
}
}
message RegisterNetworkListenerRequest {
}
message NetworkMessage {
NetworkEnvelope network_envelope = 1;
}
message GetOnlinePeersRequest {}
message GetOnlinePeersReply {
repeated Peer peers = 1;
}
message GetSeednodesRequest {}
message GetSeednodesReply {
repeated NodeAddress seednodes = 1;
}
message GetRegisteredArbitratorsRequest {}
message GetRegisteredArbitratorsReply {
repeated Arbitrator arbitrators = 1;
}
message GetNetworkFilterRequest {}
message GetNetworkFilterReply {
Filter filter = 1;
}
message GetAlertsRequest {}
message GetAlertsReply {
repeated Alert alerts = 1;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Marketplace Protospec // Marketplace Protospec