Compare commits

...

7 commits

Author SHA1 Message Date
285b853a58 added grpc network listener annd new message spec 2025-06-30 06:59:30 +07:00
d3945fff7e alter release date 2025-06-20 05:07:57 +07:00
34cc564f18 no 2025-06-20 05:05:58 +07:00
1be47b7646 completing merge 2025-06-20 05:01:21 +07:00
97c349f6b3 minor fixes 2025-06-20 04:55:34 +07:00
401b363d07 Update README.md 2025-06-19 11:22:26 +00:00
ac4b7c597c seednodes 2025-06-19 11:40:12 +07:00
13 changed files with 209 additions and 80 deletions

1
.gitignore vendored
View file

@ -39,3 +39,4 @@ deploy
*/.factorypath
.flatpak-builder
exchange.haveno.Haveno.yaml
hs_ed25519_secret_key

View file

@ -1,7 +1 @@
Patched Branch for App
Any changes must be made to the patched_changes (this) branch only if something is less of a patch, and more of a core functionality you can request this from the haveno core team.
Explaination of changes
Changed protocol serialization from 'A' to 'X' to indicate a clear significant change in structure
Changes the daemon to be a shadowJar instead
The original Haveno core fork from haveno-dex/haveno adapted for the multiplatform app.

View file

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

@ -85,13 +85,13 @@ import org.bitcoinj.core.Coin;
public class HavenoUtils {
// configure release date
private static final String RELEASE_DATE = "25-05-2024 00:00:00"; // optionally set to release date of the network in format dd-mm-yyyy to impose temporary limits, etc. e.g. "25-05-2024 00:00:00"
private static final String RELEASE_DATE = "18-06-2025 00:00:00"; // optionally set to release date of the network in format dd-mm-yyyy to impose temporary limits, etc. e.g. "25-05-2024 00:00:00"
public static final int RELEASE_LIMIT_DAYS = 60; // number of days to limit sell offers to max buy limit for new accounts
public static final int WARN_ON_OFFER_EXCEEDS_UNSIGNED_BUY_LIMIT_DAYS = 182; // number of days to warn if sell offer exceeds unsigned buy limit
public static final int ARBITRATOR_ACK_TIMEOUT_SECONDS = 60;
// configure fees
public static final boolean ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS = true;
public static final boolean ARBITRATOR_ASSIGNS_TRADE_FEE_ADDRESS = false;
public static final double PENALTY_FEE_PCT = 0.02; // 2%
public static final double MAKER_FEE_PCT = 0.0015; // 0.15%
public static final double TAKER_FEE_PCT = 0.0075; // 0.75%
@ -476,9 +476,9 @@ public class HavenoUtils {
case XMR_STAGENET:
return "5B11hTJdG2XDNwjdKGLRxwSLwDhkbGg7C7UEAZBxjE6FbCeRMjudrpNACmDNtWPiSnNfjDQf39QRjdtdgoL69txv81qc2Mc";
case XMR_MAINNET:
throw new RuntimeException("Mainnet fee address not implemented");
return "8BTqDom6yukimzKTaLX7d4EgxznKUqTLjebpvUySkWGcg9irMqUwiHKccCTZ8dMu7CG3Ac89H7kqifpCwjCeWd2GUhhxy1F";
default:
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
return "8BTqDom6yukimzKTaLX7d4EgxznKUqTLjebpvUySkWGcg9irMqUwiHKccCTZ8dMu7CG3Ac89H7kqifpCwjCeWd2GUhhxy1F";
}
}

View file

@ -107,7 +107,7 @@ public class XmrWalletService extends XmrWalletBase {
// monero configuration
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_PATH = MONERO_BINS_DIR + File.separator + MONERO_WALLET_RPC_NAME;
public static final MoneroTxPriority PROTOCOL_FEE_PRIORITY = MoneroTxPriority.DEFAULT;
@ -120,8 +120,8 @@ public class XmrWalletService extends XmrWalletBase {
private static final String KEYS_FILE_POSTFIX = ".keys";
private static final String ADDRESS_FILE_POSTFIX = ".address.txt";
private static final int NUM_MAX_WALLET_BACKUPS = 2;
private static final int MAX_SYNC_ATTEMPTS = 3;
private static final boolean PRINT_RPC_STACK_TRACE = false;
private static final int MAX_SYNC_ATTEMPTS = 5;
private static final boolean PRINT_RPC_STACK_TRACE = true;
private static final String THREAD_ID = XmrWalletService.class.getSimpleName();
private static final long SHUTDOWN_TIMEOUT_MS = 60000;
private static final long NUM_BLOCKS_BEHIND_TOLERANCE = 5;

View file

@ -93,7 +93,7 @@ grant {
/* user data dir for Mac, Linux, Windows */
permission "java.io.FilePermission" "${user.home}${/}Library${/}Application Support${/}-", "read,write,delete";
permission "java.io.FilePermission" "${user.home}${/}.local${/}share${/}haveno-", "read,write,delete";
permission "java.io.FilePermission" "${user.home}${/}.local${/}share${/}-", "read,write,delete";
permission "java.io.FilePermission" "${appdata}${/}haveno-", "read,write,delete";
/* temp dir Mac, Linux, Windows TODO */

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

@ -0,0 +1 @@
ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion

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;
}
///////////////////////////////////////////////////////////////////////////////////////////
// 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