Compare commits
27 commits
Author | SHA1 | Date | |
---|---|---|---|
62c423ce9c | |||
d3945fff7e | |||
34cc564f18 | |||
1be47b7646 | |||
97c349f6b3 | |||
401b363d07 | |||
ac4b7c597c | |||
30460f05f4 | |||
893fa2ec90 | |||
dc062984b3 | |||
16c2d87cea | |||
52bdd156f3 | |||
648165b368 | |||
36a371ef34 | |||
|
e4e118f70c | ||
|
53e2c5cc24 | ||
|
62d5eb4bc3 | ||
|
183782982c | ||
13b201b020 | |||
01db30156a | |||
f3c4d987dc | |||
|
ee49324fbb | ||
6a2672893c | |||
86d557c669 | |||
36667f0b09 | |||
a2f537ebf4 | |||
8e1c629abd |
1
.gitignore
vendored
|
@ -39,3 +39,4 @@ deploy
|
|||
*/.factorypath
|
||||
.flatpak-builder
|
||||
exchange.haveno.Haveno.yaml
|
||||
hs_ed25519_secret_key
|
12
Makefile
|
@ -595,3 +595,15 @@ user3-desktop-mainnet:
|
|||
--apiPort=1204 \
|
||||
--useNativeXmrWallet=false \
|
||||
--ignoreLocalXmrNode=false \
|
||||
|
||||
parner1-bot-stagenet:
|
||||
./haveno-bot$(APP_EXT) \
|
||||
--baseCurrencyNetwork=XMR_STAGENET \
|
||||
--useLocalhostForP2P=false \
|
||||
--useDevPrivilegeKeys=false \
|
||||
--nodePort=9999 \
|
||||
--appName=haveno-XMR_STAGENET_user3 \
|
||||
--apiPassword=apitest \
|
||||
--apiPort=1204 \
|
||||
--useNativeXmrWallet=false \
|
||||
--ignoreLocalXmrNode=false \
|
84
README.md
|
@ -1,83 +1 @@
|
|||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno-meta/721e52919b28b44d12b6e1e5dac57265f1c05cda/logo/haveno_logo_landscape.svg" alt="Haveno logo">
|
||||
|
||||

|
||||
[](https://github.com/haveno-dex/haveno/issues?q=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty)
|
||||
[](https://twitter.com/havenodex)
|
||||
[](https://matrix.to/#/#haveno:monero.social) [](https://github.com/haveno-dex/.github/blob/master/CODE_OF_CONDUCT.md)
|
||||
</div>
|
||||
|
||||
## What is Haveno?
|
||||
|
||||
Haveno (pronounced ha‧ve‧no) is an open source platform to exchange [Monero](https://getmonero.org) for fiat currencies like USD, EUR, and GBP or other cryptocurrencies like BTC, ETH, and BCH.
|
||||
|
||||
Main features:
|
||||
|
||||
- Communications are routed through **Tor**, to preserve your privacy.
|
||||
|
||||
- Trades are **peer-to-peer**: trades on Haveno happen between people only, there is no central authority.
|
||||
|
||||
- Trades are **non-custodial**: Haveno supports arbitration in case something goes wrong during the trade, but arbitrators never have access to your funds.
|
||||
|
||||
- There is **No token**, because it's not needed. Transactions between traders are secured by non-custodial multisignature transactions on the Monero network.
|
||||
|
||||
See the [FAQ on our website](https://haveno.exchange/faq/) for more information.
|
||||
|
||||
## Haveno Demo
|
||||
|
||||
https://github.com/user-attachments/assets/eb6b3af0-78ce-46a7-bfa1-2aacd8649d47
|
||||
|
||||
## Installing Haveno
|
||||
|
||||
Haveno can be installed on Linux, macOS, and Windows by using a third party installer and network.
|
||||
|
||||
> [!note]
|
||||
> The official Haveno repository does not support making real trades directly.
|
||||
>
|
||||
> To make real trades with Haveno, first find a third party network, and then use their installer or build their repository. We do not endorse any networks at this time.
|
||||
|
||||
A test network is also available for users to make test trades using Monero's stagenet. See the [instructions](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) to build Haveno and connect to the test network.
|
||||
|
||||
Alternatively, you can [create your own mainnet network](https://github.com/haveno-dex/haveno/blob/master/docs/create-mainnet.md).
|
||||
|
||||
Note that Haveno is being actively developed. If you find issues or bugs, please let us know.
|
||||
|
||||
## Main repositories
|
||||
|
||||
- **[haveno](https://github.com/haveno-dex/haveno)** - This repository. The core of Haveno.
|
||||
- **[haveno-ts](https://github.com/haveno-dex/haveno-ts)** - TypeScript library for using Haveno.
|
||||
- **[haveno-ui](https://github.com/haveno-dex/haveno-ui)** - A new user interface (WIP).
|
||||
- **[haveno-meta](https://github.com/haveno-dex/haveno-meta)** - For project-wide discussions and proposals.
|
||||
|
||||
If you wish to help, take a look at the repositories above and look for open issues. We run a bounty program to incentivize development. See [Bounties](#bounties).
|
||||
|
||||
## Keep in touch and help out!
|
||||
|
||||
Haveno is a community-driven project. For it to be successful it's fundamental to have the support and help of the community. Join the community rooms on our Matrix server:
|
||||
|
||||
- General discussions: **Haveno** ([#haveno:monero.social](https://matrix.to/#/#haveno:monero.social)) relayed on IRC/Libera (`#haveno`)
|
||||
- Development discussions: **Haveno Development** ([#haveno-development:monero.social](https://matrix.to/#/#haveno-development:monero.social)) relayed on IRC/Libera (`#haveno-development`)
|
||||
|
||||
Email: contact@haveno.exchange
|
||||
Website: [haveno.exchange](https://haveno.exchange)
|
||||
|
||||
## Contributing to Haveno
|
||||
|
||||
See the [developer guide](docs/developer-guide.md) to get started developing for Haveno.
|
||||
|
||||
See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for our styling guides.
|
||||
|
||||
If you are not able to contribute code and want to contribute development resources, [donations](#support-and-sponsorships) fund development bounties.
|
||||
|
||||
## Bounties
|
||||
|
||||
To incentivize development and reward contributors, we adopt a simple bounty system. Contributors may be awarded bounties after completing a task (resolving an issue). Take a look at the [issues labeled '💰bounty'](https://github.com/haveno-dex/haveno/issues?q=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty) in the main `haveno` repository. [Details and conditions for receiving a bounty](docs/bounties.md).
|
||||
|
||||
## Support
|
||||
|
||||
To bring Haveno to life, we need resources. If you have the possibility, please consider donating to the project:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="115" height="115"><br>
|
||||
<code>47fo8N5m2VVW4uojadGQVJ34LFR9yXwDrZDRugjvVSjcTWV2WFSoc1XfNpHmxwmVtfNY9wMBch6259G6BXXFmhU49YG1zfB</code>
|
||||
</p>
|
||||
The original Haveno core fork from haveno-dex/haveno adapted for the multiplatform app.
|
47
bot/src/main/java/haveno/bot/app/HavenoBot.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package haveno.bot.app;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
import haveno.core.app.misc.AppSetup;
|
||||
import haveno.core.app.misc.AppSetupWithP2P;
|
||||
import haveno.core.network.p2p.inventory.GetInventoryRequestHandler;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class HavenoBot {
|
||||
@Setter
|
||||
private Injector injector;
|
||||
private AppSetup appSetup;
|
||||
private GetInventoryRequestHandler getInventoryRequestHandler;
|
||||
|
||||
public HavenoBot() {
|
||||
}
|
||||
|
||||
public void startApplication() {
|
||||
appSetup = injector.getInstance(AppSetupWithP2P.class);
|
||||
appSetup.start();
|
||||
|
||||
getInventoryRequestHandler = injector.getInstance(GetInventoryRequestHandler.class);
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
getInventoryRequestHandler.shutDown();
|
||||
}
|
||||
}
|
187
bot/src/main/java/haveno/bot/app/HavenoBotMain.java
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package haveno.bot.app;
|
||||
|
||||
import haveno.common.UserThread;
|
||||
import haveno.common.app.AppModule;
|
||||
import haveno.common.handlers.ResultHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import haveno.common.Timer;
|
||||
import haveno.common.config.BaseCurrencyNetwork;
|
||||
import haveno.common.config.Config;
|
||||
import haveno.core.app.TorSetup;
|
||||
import haveno.core.app.misc.ExecutableForAppWithP2p;
|
||||
import haveno.core.app.misc.ModuleForAppWithP2p;
|
||||
import haveno.core.user.Cookie;
|
||||
import haveno.core.user.CookieKey;
|
||||
import haveno.core.user.User;
|
||||
import haveno.network.p2p.P2PService;
|
||||
import haveno.network.p2p.P2PServiceListener;
|
||||
import haveno.network.p2p.peers.PeerManager;
|
||||
|
||||
@Slf4j
|
||||
public class HavenoBotMain extends ExecutableForAppWithP2p {
|
||||
|
||||
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
|
||||
private static final String VERSION = "1.1.2";
|
||||
private HavenoBot bot;
|
||||
private Timer checkConnectionLossTime;
|
||||
|
||||
public HavenoBotMain() {
|
||||
super("Haveno Bot", "haveno-bot", "haveno_bot", VERSION);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("HavenoBot.VERSION: " + VERSION);
|
||||
new HavenoBotMain().execute(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doExecute() {
|
||||
super.doExecute();
|
||||
|
||||
checkMemory(config, this);
|
||||
|
||||
return keepRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addCapabilities() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void launchApplication() {
|
||||
UserThread.execute(() -> {
|
||||
try {
|
||||
bot = new HavenoBot();
|
||||
UserThread.execute(this::onApplicationLaunched);
|
||||
} catch (Exception e) {
|
||||
log.error("Error launching haveno bot: {}\n", e.toString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplicationLaunched() {
|
||||
super.onApplicationLaunched();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// We continue with a series of synchronous execution tasks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected AppModule getModule() {
|
||||
return new ModuleForAppWithP2p(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyInjector() {
|
||||
super.applyInjector();
|
||||
|
||||
bot.setInjector(injector);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startApplication() {
|
||||
Cookie cookie = injector.getInstance(User.class).getCookie();
|
||||
cookie.getAsOptionalBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART).ifPresent(wasCleanTorDirSet -> {
|
||||
if (wasCleanTorDirSet) {
|
||||
injector.getInstance(TorSetup.class).cleanupTorFiles(() -> {
|
||||
log.info("Tor directory reset");
|
||||
cookie.remove(CookieKey.CLEAN_TOR_DIR_AT_RESTART);
|
||||
}, log::error);
|
||||
}
|
||||
});
|
||||
|
||||
bot.startApplication();
|
||||
|
||||
injector.getInstance(P2PService.class).addP2PServiceListener(new P2PServiceListener() {
|
||||
@Override
|
||||
public void onDataReceived() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoPeersAvailable() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdatedDataReceived() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServicePublished() {
|
||||
UserThread.runAfter(() -> setupConnectionLossCheck(), 60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupFailed(Throwable throwable) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCustomBridges() {
|
||||
// Do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupConnectionLossCheck() {
|
||||
// For dev testing (usually on XMR_LOCAL) we don't want to get the seed shut
|
||||
// down as it is normal that the seed is the only actively running node.
|
||||
if (Config.baseCurrencyNetwork() != BaseCurrencyNetwork.XMR_MAINNET) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkConnectionLossTime != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkConnectionLossTime = UserThread.runPeriodically(() -> {
|
||||
if (injector.getInstance(PeerManager.class).getNumAllConnectionsLostEvents() > 1) {
|
||||
// We set a flag to clear tor cache files at re-start. We cannot clear it now as Tor is used and
|
||||
// that can cause problems.
|
||||
injector.getInstance(User.class).getCookie().putAsBoolean(CookieKey.CLEAN_TOR_DIR_AT_RESTART, true);
|
||||
shutDown(this);
|
||||
}
|
||||
}, CHECK_CONNECTION_LOSS_SEC);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gracefulShutDown(ResultHandler resultHandler) {
|
||||
bot.shutDown();
|
||||
super.gracefulShutDown(resultHandler);
|
||||
}
|
||||
}
|
19
bot/src/main/resources/logback.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<conversionRule conversionWord="hl2" converterClass="haveno.common.app.LogHighlighter" />
|
||||
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%hl2(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{40}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="TRACE">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.grpc.netty" level="WARN"/>
|
||||
<logger name="org.apache" level="WARN" />
|
||||
|
||||
</configuration>
|
194
build.gradle
|
@ -132,7 +132,9 @@ configure([project(':cli'),
|
|||
project(':seednode'),
|
||||
project(':statsnode'),
|
||||
project(':inventory'),
|
||||
project(':apitest')]) {
|
||||
project(':apitest'),
|
||||
//project(':bot')
|
||||
]) {
|
||||
|
||||
apply plugin: 'application'
|
||||
|
||||
|
@ -452,123 +454,6 @@ configure(project(':core')) {
|
|||
mainClass = 'haveno.core.util.GenerateKeyPairs'
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
}
|
||||
|
||||
task havenoDeps {
|
||||
doLast {
|
||||
// get monero binaries download url
|
||||
Map moneroBinaries = [
|
||||
'linux-x86_64' : 'https://github.com/haveno-dex/monero/releases/download/release6/monero-bins-haveno-linux-x86_64.tar.gz',
|
||||
'linux-x86_64-sha256' : '44470a3cf2dd9be7f3371a8cc89a34cf9a7e88c442739d87ef9a0ec3ccb65208',
|
||||
'linux-aarch64' : 'https://github.com/haveno-dex/monero/releases/download/release6/monero-bins-haveno-linux-aarch64.tar.gz',
|
||||
'linux-aarch64-sha256' : 'c9505524689b0d7a020b8d2fd449c3cb9f8fd546747f9bdcf36cac795179f71c',
|
||||
'mac' : 'https://github.com/haveno-dex/monero/releases/download/release6/monero-bins-haveno-mac.tar.gz',
|
||||
'mac-sha256' : 'dea6eddefa09630cfff7504609bd5d7981316336c64e5458e242440694187df8',
|
||||
'windows' : 'https://github.com/haveno-dex/monero/releases/download/release6/monero-bins-haveno-windows.zip',
|
||||
'windows-sha256' : '284820e28c4770d7065fad7863e66fe0058053ca2372b78345d83c222edc572d'
|
||||
]
|
||||
|
||||
String osKey
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
osKey = 'windows'
|
||||
} else if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
osKey = 'mac'
|
||||
} else {
|
||||
String architecture = System.getProperty("os.arch").toLowerCase()
|
||||
if (architecture.contains('aarch64') || architecture.contains('arm')) {
|
||||
osKey = 'linux-aarch64'
|
||||
} else {
|
||||
osKey = 'linux-x86_64'
|
||||
}
|
||||
}
|
||||
|
||||
String moneroDownloadUrl = moneroBinaries[osKey]
|
||||
String moneroSHA256Hash = moneroBinaries[osKey + '-sha256']
|
||||
String moneroArchiveFileName = moneroDownloadUrl.tokenize('/').last()
|
||||
String localnetDirName = '.localnet'
|
||||
File localnetDir = new File(project.rootDir, localnetDirName)
|
||||
localnetDir.mkdirs()
|
||||
File moneroArchiveFile = new File(localnetDir, moneroArchiveFileName)
|
||||
ext.downloadAndVerifyDependencies(moneroDownloadUrl, moneroSHA256Hash, moneroArchiveFile)
|
||||
|
||||
// extract if dependencies are missing or if archive was updated
|
||||
File monerodFile
|
||||
File moneroRpcFile
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
monerodFile = new File(localnetDir, 'monerod.exe')
|
||||
moneroRpcFile = new File(localnetDir, 'monero-wallet-rpc.exe')
|
||||
} else {
|
||||
monerodFile = new File(localnetDir, 'monerod')
|
||||
moneroRpcFile = new File(localnetDir, 'monero-wallet-rpc')
|
||||
}
|
||||
if (ext.dependencyDownloadedAndVerified || !monerodFile.exists() || !moneroRpcFile.exists()) {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
ext.extractArchiveZip(moneroArchiveFile, localnetDir)
|
||||
} else {
|
||||
ext.extractArchiveTarGz(moneroArchiveFile, localnetDir)
|
||||
}
|
||||
}
|
||||
|
||||
// add the current platform's monero dependencies into the resources folder for installation
|
||||
copy {
|
||||
from "${monerodFile}"
|
||||
into "${project(':core').projectDir}/src/main/resources/bin"
|
||||
}
|
||||
copy {
|
||||
from "${moneroRpcFile}"
|
||||
into "${project(':core').projectDir}/src/main/resources/bin"
|
||||
}
|
||||
}
|
||||
|
||||
ext.extractArchiveTarGz = { File tarGzFile, File destinationDir ->
|
||||
println "Extracting tar.gz ${tarGzFile}"
|
||||
// Gradle's tar extraction preserves permissions (crucial for jpackage to function correctly)
|
||||
copy {
|
||||
from tarTree(resources.gzip(tarGzFile))
|
||||
into destinationDir
|
||||
}
|
||||
println "Extracted to ${destinationDir}"
|
||||
}
|
||||
|
||||
ext.extractArchiveZip = { File zipFile, File destinationDir ->
|
||||
println "Extracting zip ${zipFile}..."
|
||||
ant.unzip(src: zipFile, dest: destinationDir)
|
||||
println "Extracted to ${destinationDir}"
|
||||
}
|
||||
|
||||
ext.downloadAndVerifyDependencies = { String archiveURL, String archiveSHA256, File destinationArchiveFile ->
|
||||
ext.dependencyDownloadedAndVerified = false
|
||||
|
||||
// if archive exists, check to see if its already up to date
|
||||
if (destinationArchiveFile.exists()) {
|
||||
println "Verifying existing archive ${destinationArchiveFile}"
|
||||
ant.archiveHash = archiveSHA256
|
||||
ant.checksum(file: destinationArchiveFile, algorithm: 'SHA-256', property: '${archiveHash}', verifyProperty: 'existingHashMatches')
|
||||
if (ant.properties['existingHashMatches'] != 'true') {
|
||||
println "Existing archive does not match hash ${archiveSHA256}"
|
||||
} else {
|
||||
println "Existing archive matches hash"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// download archives
|
||||
println "Downloading ${archiveURL}"
|
||||
ant.get(src: archiveURL, dest: destinationArchiveFile)
|
||||
println 'Download saved to ' + destinationArchiveFile
|
||||
|
||||
// verify checksum
|
||||
println 'Verifying checksum for downloaded binary ...'
|
||||
ant.archiveHash = archiveSHA256
|
||||
ant.checksum(file: destinationArchiveFile, algorithm: 'SHA-256', property: '${archiveHash}', verifyProperty: 'downloadedHashMatches') // use a different verifyProperty name from existing verification or it will always fail
|
||||
if (ant.properties['downloadedHashMatches'] != 'true') {
|
||||
ant.fail('Checksum mismatch: Downloaded archive has a different checksum than expected')
|
||||
}
|
||||
println 'Checksum verified'
|
||||
ext.dependencyDownloadedAndVerified = true
|
||||
}
|
||||
}
|
||||
|
||||
processResources.dependsOn havenoDeps // before both test and build
|
||||
}
|
||||
|
||||
configure(project(':cli')) {
|
||||
|
@ -787,7 +672,12 @@ configure(project(':statsnode')) {
|
|||
}
|
||||
|
||||
configure(project(':daemon')) {
|
||||
mainClassName = 'haveno.daemon.app.HavenoDaemonMain'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'application'
|
||||
|
||||
application {
|
||||
mainClass = 'haveno.daemon.app.HavenoDaemonMain'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':proto')
|
||||
|
@ -847,6 +737,72 @@ configure(project(':daemon')) {
|
|||
}
|
||||
}
|
||||
|
||||
//configure(project(':bot')) {
|
||||
// apply plugin: 'com.github.johnrengelman.shadow'
|
||||
// apply plugin: 'application'
|
||||
//
|
||||
// application {
|
||||
// mainClass = 'haveno.bot.app.HavenoBotMain'
|
||||
// }
|
||||
//
|
||||
// dependencies {
|
||||
// implementation project(':proto')
|
||||
// implementation project(':common')
|
||||
// implementation project(':p2p')
|
||||
// implementation project(':core')
|
||||
// annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
// compileOnly "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
|
||||
// compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
// implementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
// implementation "ch.qos.logback:logback-core:$logbackVersion"
|
||||
// implementation "com.google.code.gson:gson:$gsonVersion"
|
||||
// implementation "com.google.guava:guava:$guavaVersion"
|
||||
// implementation "com.google.protobuf:protobuf-java:$protobufVersion"
|
||||
// implementation "org.apache.commons:commons-lang3:$langVersion"
|
||||
// implementation "org.jetbrains:annotations:$jetbrainsAnnotationsVersion"
|
||||
// implementation "org.slf4j:slf4j-api:$slf4jVersion"
|
||||
// implementation("com.github.bisq-network:bitcoinj:$bitcoinjVersion") {
|
||||
// exclude(module: 'bcprov-jdk15on')
|
||||
// exclude(module: 'guava')
|
||||
// exclude(module: 'jsr305')
|
||||
// exclude(module: 'okhttp')
|
||||
// exclude(module: 'okio')
|
||||
// exclude(module: 'protobuf-java')
|
||||
// exclude(module: 'slf4j-api')
|
||||
// }
|
||||
// implementation("com.google.inject:guice:$guiceVersion") {
|
||||
// exclude(module: 'guava')
|
||||
// }
|
||||
// implementation("io.grpc:grpc-protobuf:$grpcVersion") {
|
||||
// exclude(module: 'animal-sniffer-annotations')
|
||||
// exclude(module: 'guava')
|
||||
// }
|
||||
// implementation("io.grpc:grpc-stub:$grpcVersion") {
|
||||
// exclude(module: 'animal-sniffer-annotations')
|
||||
// exclude(module: 'guava')
|
||||
// }
|
||||
// runtimeOnly("io.grpc:grpc-netty-shaded:$grpcVersion") {
|
||||
// exclude(module: 'animal-sniffer-annotations')
|
||||
// exclude(module: 'guava')
|
||||
// }
|
||||
// testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
// testCompileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
// testImplementation "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
|
||||
// testImplementation "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
|
||||
// testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
|
||||
//
|
||||
// implementation("io.github.woodser:monero-java:$moneroJavaVersion") {
|
||||
// exclude(module: 'jackson-core')
|
||||
// exclude(module: 'jackson-annotations')
|
||||
// exclude(module: 'jackson-databind')
|
||||
// exclude(module: 'bcprov-jdk15on')
|
||||
// exclude(group: 'org.slf4j', module: 'slf4j-simple')
|
||||
// }
|
||||
// implementation "org.openjfx:javafx-base:$javafxVersion:$os"
|
||||
// implementation "org.openjfx:javafx-graphics:$javafxVersion:$os"
|
||||
// }
|
||||
//}
|
||||
|
||||
configure(project(':inventory')) {
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ public class Version {
|
|||
|
||||
// The version no. for the objects sent over the network. A change will break the serialization of old objects.
|
||||
// If objects are used for both network and database the network version is applied.
|
||||
public static final String P2P_NETWORK_VERSION = "A";
|
||||
public static final String P2P_NETWORK_VERSION = System.getenv().getOrDefault("P2P_NETWORK_VERSION", "X");
|
||||
|
||||
// The version no. of the serialized data stored to disc. A change will break the serialization of old objects.
|
||||
// VERSION = 0.5.0 -> LOCAL_DB_VERSION = 1
|
||||
|
|
|
@ -97,25 +97,13 @@ public class AlertManager {
|
|||
}
|
||||
|
||||
protected List<String> getPubKeyList() {
|
||||
if (useDevPrivilegeKeys) return List.of(DevEnv.DEV_PRIVILEGE_PUB_KEY);
|
||||
switch (Config.baseCurrencyNetwork()) {
|
||||
case XMR_LOCAL:
|
||||
return List.of(
|
||||
"027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee",
|
||||
"024baabdba90e7cc0dc4626ef73ea9d722ea7085d1104491da8c76f28187513492");
|
||||
case XMR_STAGENET:
|
||||
return List.of(
|
||||
"036d8a1dfcb406886037d2381da006358722823e1940acc2598c844bbc0fd1026f",
|
||||
"026c581ad773d987e6bd10785ac7f7e0e64864aedeb8bce5af37046de812a37854",
|
||||
"025b058c9f2c60d839669dbfa5578cf5a8117d60e6b70e2f0946f8a691273c6a36");
|
||||
case XMR_MAINNET:
|
||||
return List.of();
|
||||
default:
|
||||
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
|
||||
}
|
||||
return List.of(
|
||||
"0326b14f3a55d02575dceed5202b8b125f458cbe0fdceeee294b443bf1a8d8cf78",
|
||||
"03d62d14438adbe7aea688ade1f73933c6f0a705f238c02c5b54b83dd1e4fca225",
|
||||
"023c8fdea9ff2d03daef54337907e70a7b0e20084a75fcc3ad2f0c28d8b691dea1"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -96,22 +96,11 @@ public class PrivateNotificationManager implements MessageListener {
|
|||
}
|
||||
|
||||
protected List<String> getPubKeyList() {
|
||||
if (useDevPrivilegeKeys) return List.of(DevEnv.DEV_PRIVILEGE_PUB_KEY);
|
||||
switch (Config.baseCurrencyNetwork()) {
|
||||
case XMR_LOCAL:
|
||||
return List.of(
|
||||
"027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee",
|
||||
"024baabdba90e7cc0dc4626ef73ea9d722ea7085d1104491da8c76f28187513492");
|
||||
case XMR_STAGENET:
|
||||
return List.of(
|
||||
"02ba7c5de295adfe57b60029f3637a2c6b1d0e969a8aaefb9e0ddc3a7963f26925",
|
||||
"026c581ad773d987e6bd10785ac7f7e0e64864aedeb8bce5af37046de812a37854",
|
||||
"025b058c9f2c60d839669dbfa5578cf5a8117d60e6b70e2f0946f8a691273c6a36");
|
||||
case XMR_MAINNET:
|
||||
return List.of();
|
||||
default:
|
||||
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
|
||||
}
|
||||
return List.of(
|
||||
"0326b14f3a55d02575dceed5202b8b125f458cbe0fdceeee294b443bf1a8d8cf78",
|
||||
"03d62d14438adbe7aea688ade1f73933c6f0a705f238c02c5b54b83dd1e4fca225",
|
||||
"023c8fdea9ff2d03daef54337907e70a7b0e20084a75fcc3ad2f0c28d8b691dea1"
|
||||
);
|
||||
}
|
||||
|
||||
private void handleMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, NodeAddress senderNodeAddress) {
|
||||
|
|
|
@ -114,11 +114,11 @@ public class FilterManager {
|
|||
this.providersRepository = providersRepository;
|
||||
this.ignoreDevMsg = ignoreDevMsg;
|
||||
|
||||
publicKeys = useDevPrivilegeKeys ?
|
||||
Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) :
|
||||
List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27",
|
||||
"029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f",
|
||||
"034dc7530bf66ffd9580aa98031ea9a18ac2d269f7c56c0e71eca06105b9ed69f9");
|
||||
publicKeys = List.of(
|
||||
"0326b14f3a55d02575dceed5202b8b125f458cbe0fdceeee294b443bf1a8d8cf78",
|
||||
"03d62d14438adbe7aea688ade1f73933c6f0a705f238c02c5b54b83dd1e4fca225",
|
||||
"023c8fdea9ff2d03daef54337907e70a7b0e20084a75fcc3ad2f0c28d8b691dea1"
|
||||
);
|
||||
|
||||
banFilter.setBannedNodePredicate(this::isNodeAddressBannedFromNetwork);
|
||||
}
|
||||
|
|
|
@ -406,6 +406,13 @@ public class CurrencyUtil {
|
|||
removedCryptoCurrency.isPresent() ? removedCryptoCurrency.get().getName() : Res.get("shared.na");
|
||||
return getCryptoCurrency(currencyCode).map(TradeCurrency::getName).orElse(xmrOrRemovedAsset);
|
||||
}
|
||||
if (isTraditionalNonFiatCurrency(currencyCode)) {
|
||||
return getTraditionalNonFiatCurrencies().stream()
|
||||
.filter(currency -> currency.getCode().equals(currencyCode))
|
||||
.findAny()
|
||||
.map(TradeCurrency::getName)
|
||||
.orElse(currencyCode);
|
||||
}
|
||||
try {
|
||||
return Currency.getInstance(currencyCode).getDisplayName();
|
||||
} catch (Throwable t) {
|
||||
|
|
|
@ -59,29 +59,11 @@ public class ArbitratorManager extends DisputeAgentManager<Arbitrator> {
|
|||
|
||||
@Override
|
||||
protected List<String> getPubKeyList() {
|
||||
switch (Config.baseCurrencyNetwork()) {
|
||||
case XMR_LOCAL:
|
||||
return List.of(
|
||||
"027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee",
|
||||
"024baabdba90e7cc0dc4626ef73ea9d722ea7085d1104491da8c76f28187513492",
|
||||
"026eeec3c119dd6d537249d74e5752a642dd2c3cc5b6a9b44588eb58344f29b519");
|
||||
case XMR_STAGENET:
|
||||
return List.of(
|
||||
"03bb559ce207a4deb51d4c705076c95b85ad8581d35936b2a422dcb504eaf7cdb0",
|
||||
"026c581ad773d987e6bd10785ac7f7e0e64864aedeb8bce5af37046de812a37854",
|
||||
"025b058c9f2c60d839669dbfa5578cf5a8117d60e6b70e2f0946f8a691273c6a36",
|
||||
"036c7d3f4bf05ef39b9d1b0a5d453a18210de36220c3d83cd16e59bd6132b037ad",
|
||||
"030f7122a10ff73cd73808bddace95be77a94189c8a0eb24586265e125ce5ce6b9",
|
||||
"03aa23e062afa0dda465f46986f8aa8d0374ad3e3f256141b05681dcb1e39c3859",
|
||||
"02d3beb1293ca2ca14e6d42ca8bd18089a62aac62fd6bb23923ee6ead46ac60fba",
|
||||
"03fa0f38f27bdd324db6f933f7e57851dadf3b911e4db6b19dd0950492c4525a31",
|
||||
"02a1a458df5acf4ab08fdca748e28f33a955a30854c8c1a831ee733dca7f0d2fcd",
|
||||
"0374dd70f3fa6e47ec5ab97932e1cec6233e98e6ae3129036b17118650c44fd3de");
|
||||
case XMR_MAINNET:
|
||||
return List.of();
|
||||
default:
|
||||
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
|
||||
}
|
||||
return List.of(
|
||||
"0326b14f3a55d02575dceed5202b8b125f458cbe0fdceeee294b443bf1a8d8cf78",
|
||||
"03d62d14438adbe7aea688ade1f73933c6f0a705f238c02c5b54b83dd1e4fca225",
|
||||
"023c8fdea9ff2d03daef54337907e70a7b0e20084a75fcc3ad2f0c28d8b691dea1"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -76,7 +76,7 @@ public class ClosedTradableFormatter {
|
|||
}
|
||||
|
||||
public String getTotalTxFeeAsString(BigInteger totalTradeAmount, BigInteger totalTxFee) {
|
||||
double percentage = HavenoUtils.divide(totalTxFee, totalTradeAmount);
|
||||
double percentage = totalTradeAmount.equals(BigInteger.ZERO) ? 0 : HavenoUtils.divide(totalTxFee, totalTradeAmount);
|
||||
return Res.get(I18N_KEY_TOTAL_TX_FEE,
|
||||
HavenoUtils.formatXmr(totalTxFee, true),
|
||||
formatToPercentWithSymbol(percentage));
|
||||
|
@ -104,7 +104,7 @@ public class ClosedTradableFormatter {
|
|||
}
|
||||
|
||||
public String getTotalTradeFeeAsString(BigInteger totalTradeAmount, BigInteger totalTradeFee) {
|
||||
double percentage = HavenoUtils.divide(totalTradeFee, totalTradeAmount);
|
||||
double percentage = totalTradeAmount.equals(BigInteger.ZERO) ? 0 : HavenoUtils.divide(totalTradeFee, totalTradeAmount);
|
||||
return Res.get(I18N_KEY_TOTAL_TRADE_FEE_BTC,
|
||||
HavenoUtils.formatXmr(totalTradeFee, true),
|
||||
formatToPercentWithSymbol(percentage));
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
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;
|
||||
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 MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
|
||||
private static final String MONERO_WALLET_RPC_USERNAME = "haveno_user";
|
||||
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
# nodeaddress.onion:port [(@owner,@backup)]
|
||||
placeholder.onion:8000 (@placeholder)
|
||||
# nodeaddress.onion:port [(@owner,@backup)]
|
||||
5i6blbmuflq4s4im6zby26a7g22oef6kyp7vbwyru6oq5e36akzo3ayd.onion:2001 (@op1)
|
||||
dx4ktxyiemjc354imehuaswbhqlidhy62b4ifzigk5p2rb37lxqbveqd.onion:2002 (@op2)
|
||||
ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion:2003 (@op3)
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
# nodeaddress.onion:port [(@owner)]
|
||||
dl57jitswby4yhzpqpu7pwq6iyqg2x6vkio73araparbftlqoqxhvqad.onion:2002 (@devtest1)
|
||||
3cqlkowdu766sto5wrdqpntpsi7kezwkkakc532i6jeiyu7hha726ead.onion:3003 (@devtest1)
|
||||
# nodeaddress.onion:port [(@owner)]
|
|
@ -216,7 +216,7 @@ public class HavenoAppMain extends HavenoExecutable {
|
|||
|
||||
// Set the dialog content
|
||||
VBox vbox = new VBox(10);
|
||||
ImageView logoImageView = new ImageView(ImageUtil.getImageByPath("logo_splash_light.png"));
|
||||
ImageView logoImageView = new ImageView(ImageUtil.getImageByPath("logo_splash_light_mode.png"));
|
||||
logoImageView.setFitWidth(342);
|
||||
logoImageView.setPreserveRatio(true);
|
||||
vbox.getChildren().addAll(logoImageView, passwordField, errorMessageField, versionField);
|
||||
|
|
|
@ -2283,6 +2283,7 @@ textfield */
|
|||
-fx-background-insets: 44;
|
||||
-fx-background-radius: 15;
|
||||
-fx-border-radius: 15;
|
||||
-fx-effect: dropshadow(gaussian, -bs-text-color-dropshadow-light-mode, 44, 0, 0, 0);
|
||||
}
|
||||
|
||||
.notification-popup-bg, .peer-info-popup-bg {
|
||||
|
|
|
@ -305,8 +305,12 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
}
|
||||
});
|
||||
|
||||
// add spacer to center the nav buttons when window is small
|
||||
Region rightSpacer = new Region();
|
||||
HBox.setHgrow(rightSpacer, Priority.ALWAYS);
|
||||
|
||||
HBox primaryNav = new HBox(getLogoPane(), marketButton, getNavigationSpacer(), buyButton, getNavigationSpacer(),
|
||||
sellButton, getNavigationSpacer(), portfolioButtonWithBadge, getNavigationSpacer(), fundsButton);
|
||||
sellButton, getNavigationSpacer(), portfolioButtonWithBadge, getNavigationSpacer(), fundsButton, rightSpacer);
|
||||
|
||||
primaryNav.setAlignment(Pos.CENTER_LEFT);
|
||||
primaryNav.getStyleClass().add("nav-primary");
|
||||
|
|
|
@ -578,15 +578,15 @@
|
|||
}
|
||||
|
||||
#image-logo-splash {
|
||||
-fx-image: url("../../images/logo_splash_dark.png");
|
||||
-fx-image: url("../../images/logo_splash_dark_mode.png");
|
||||
}
|
||||
|
||||
#image-logo-splash-testnet {
|
||||
-fx-image: url("../../images/logo_splash_testnet_dark.png");
|
||||
-fx-image: url("../../images/logo_splash_testnet_dark_mode.png");
|
||||
}
|
||||
|
||||
#image-logo-landscape {
|
||||
-fx-image: url("../../images/logo_landscape_dark.png");
|
||||
-fx-image: url("../../images/logo_landscape_dark_mode.png");
|
||||
}
|
||||
|
||||
.table-view .placeholder {
|
||||
|
|
|
@ -144,15 +144,15 @@
|
|||
}
|
||||
|
||||
#image-logo-splash {
|
||||
-fx-image: url("../../images/logo_splash_light.png");
|
||||
-fx-image: url("../../images/logo_splash_light_mode.png");
|
||||
}
|
||||
|
||||
#image-logo-splash-testnet {
|
||||
-fx-image: url("../../images/logo_splash_testnet_light.png");
|
||||
-fx-image: url("../../images/logo_splash_testnet_light_mode.png");
|
||||
}
|
||||
|
||||
#image-logo-landscape {
|
||||
-fx-image: url("../../images/logo_landscape_light.png");
|
||||
-fx-image: url("../../images/logo_landscape_light_mode.png");
|
||||
}
|
||||
|
||||
#charts .default-color0.chart-series-area-fill {
|
||||
|
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
32
docker/Dockerfile.core
Normal file
|
@ -0,0 +1,32 @@
|
|||
FROM openjdk:21-jdk-bullseye
|
||||
|
||||
RUN set -ex && \
|
||||
apt update && \
|
||||
apt --no-install-recommends --yes install \
|
||||
make \
|
||||
git \
|
||||
tor
|
||||
|
||||
RUN set -ex && adduser --system --group --disabled-password haveno && \
|
||||
mkdir -p /home/haveno && \
|
||||
chown -R haveno:haveno /home/haveno
|
||||
|
||||
USER haveno
|
||||
|
||||
WORKDIR /home/haveno
|
||||
|
||||
RUN set -ex && git clone https://foss.haveno.com/haveno-network/haveno-core.git
|
||||
|
||||
WORKDIR /home/haveno/haveno-core
|
||||
|
||||
RUN git checkout master && \
|
||||
git reset --hard
|
||||
|
||||
RUN make clean && make skip-tests
|
||||
|
||||
WORKDIR /home/haveno/haveno-core
|
||||
|
||||
ENTRYPOINT [ "./haveno-seednode" ]
|
||||
|
||||
## CMDs are conditional based on type
|
||||
CMD ["--baseCurrencyNetwork=XMR_MAINNET", "--useLocalhostForP2P=false", "--useDevPrivilegeKeys=false", "--nodePort=2002"]
|
1
docker/config/nodes/haveno_seednode_3/hostname
Normal file
|
@ -0,0 +1 @@
|
|||
ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion
|
BIN
docker/config/nodes/haveno_seednode_3/hs_ed25519_public_key
Normal file
3
docker/daemon/.env
Normal file
|
@ -0,0 +1,3 @@
|
|||
NETWORK_VERSION_TAG=1.1.2
|
||||
BASE_NETWORK=MAINNET
|
||||
P2P_NETWORK_VERSION="X"
|
28
docker/daemon/docker-compose.yml
Normal file
|
@ -0,0 +1,28 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
haveno-daemon:
|
||||
image: haveno-core
|
||||
build:
|
||||
context: ../
|
||||
ports:
|
||||
- "127.0.0.1:3201:3201"
|
||||
environment:
|
||||
- P2P_NETWORK_VERSION=X
|
||||
- NETWORK_VERSION_TAG=${NETWORK_VERSION_TAG}
|
||||
- BASE_CURRENCY_NETWORK=XMR_${BASE_NETWORK}
|
||||
- USE_LOCALHOST_FOR_P2P=false
|
||||
- USE_DEV_PRIVILEGE_KEYS=false
|
||||
- NODE_PORT=9999
|
||||
- APP_NAME=haveno-XMR_${BASE_NETWORK}_DAEMON
|
||||
- EXPORT_SHADOW=yes
|
||||
- EXPORT_SHADOW_PATH=daemon/build/libs/daemon-all.sh # Leave this blank in most cases
|
||||
entrypoint: ["./haveno-daemon"]
|
||||
command:
|
||||
- "--baseCurrencyNetwork=XMR_${BASE_NETWORK}"
|
||||
- "--useLocalhostForP2P=false"
|
||||
- "--useDevPrivilegeKeys=false"
|
||||
- "--nodePort=9999"
|
||||
- "--appName=XMR_${BASE_NETWORK}_DAEMON"
|
||||
- "--seedNodes=5i6blbmuflq4s4im6zby26a7g22oef6kyp7vbwyru6oq5e36akzo3ayd.onion:2001,dx4ktxyiemjc354imehuaswbhqlidhy62b4ifzigk5p2rb37lxqbveqd.onion:2002,ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion:2003"
|
||||
restart: unless-stopped
|
2
docker/seednode/.env
Normal file
|
@ -0,0 +1,2 @@
|
|||
BASE_NETWORK=MAINNET
|
||||
P2P_NETWORK_VERSION="X"
|
79
docker/seednode/docker-compose.yml
Normal file
|
@ -0,0 +1,79 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
haveno-core:
|
||||
image: haveno-core
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: Dockerfile.core
|
||||
# No ports, this one just builds the image
|
||||
entrypoint: ["true"] # No need to run
|
||||
|
||||
haveno-seednode_2001:
|
||||
image: haveno-core
|
||||
depends_on:
|
||||
- haveno-core
|
||||
ports:
|
||||
- "127.0.0.1:2001:2001"
|
||||
environment:
|
||||
- BASE_CURRENCY_NETWORK=XMR_${BASE_NETWORK}
|
||||
- USE_LOCALHOST_FOR_P2P=false
|
||||
- USE_DEV_PRIVILEGE_KEYS=false
|
||||
- NODE_PORT=2001
|
||||
- APP_NAME=haveno-XMR_${BASE_NETWORK}_Seed_2001
|
||||
entrypoint: ["./haveno-seednode"]
|
||||
command:
|
||||
- "--baseCurrencyNetwork=XMR_${BASE_NETWORK}"
|
||||
- "--useLocalhostForP2P=false"
|
||||
- "--useDevPrivilegeKeys=false"
|
||||
- "--nodePort=2001"
|
||||
- "--appName=haveno-XMR_${BASE_NETWORK}_Seed_2001"
|
||||
- "--hiddenServiceAddress=5i6blbmuflq4s4im6zby26a7g22oef6kyp7vbwyru6oq5e36akzo3ayd.onion"
|
||||
- "--seedNodes=5i6blbmuflq4s4im6zby26a7g22oef6kyp7vbwyru6oq5e36akzo3ayd.onion:2001,dx4ktxyiemjc354imehuaswbhqlidhy62b4ifzigk5p2rb37lxqbveqd.onion:2002,ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion:2003"
|
||||
restart: unless-stopped
|
||||
|
||||
haveno-seednode_2002:
|
||||
image: haveno-core
|
||||
depends_on:
|
||||
- haveno-core
|
||||
ports:
|
||||
- "127.0.0.1:2002:2002"
|
||||
environment:
|
||||
- BASE_CURRENCY_NETWORK=XMR_${BASE_NETWORK}
|
||||
- USE_LOCALHOST_FOR_P2P=false
|
||||
- USE_DEV_PRIVILEGE_KEYS=false
|
||||
- NODE_PORT=2002
|
||||
- APP_NAME=haveno-XMR_${BASE_NETWORK}_Seed_2002
|
||||
entrypoint: ["./haveno-seednode"]
|
||||
command:
|
||||
- "--baseCurrencyNetwork=XMR_${BASE_NETWORK}"
|
||||
- "--useLocalhostForP2P=false"
|
||||
- "--useDevPrivilegeKeys=false"
|
||||
- "--nodePort=2002"
|
||||
- "--appName=haveno-XMR_${BASE_NETWORK}_Seed_2002"
|
||||
- "--hiddenServiceAddress=dx4ktxyiemjc354imehuaswbhqlidhy62b4ifzigk5p2rb37lxqbveqd.onion"
|
||||
- "--seedNodes=5i6blbmuflq4s4im6zby26a7g22oef6kyp7vbwyru6oq5e36akzo3ayd.onion:2001,dx4ktxyiemjc354imehuaswbhqlidhy62b4ifzigk5p2rb37lxqbveqd.onion:2002,ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion:2003"
|
||||
restart: unless-stopped
|
||||
|
||||
haveno-seednode_2003:
|
||||
image: haveno-core
|
||||
depends_on:
|
||||
- haveno-core
|
||||
ports:
|
||||
- "127.0.0.1:2003:2003"
|
||||
environment:
|
||||
- BASE_CURRENCY_NETWORK=XMR_${BASE_NETWORK}
|
||||
- USE_LOCALHOST_FOR_P2P=false
|
||||
- USE_DEV_PRIVILEGE_KEYS=false
|
||||
- NODE_PORT=2003
|
||||
- APP_NAME=haveno-XMR_${BASE_NETWORK}_Seed_2003
|
||||
entrypoint: ["./haveno-seednode"]
|
||||
command:
|
||||
- "--baseCurrencyNetwork=XMR_${BASE_NETWORK}"
|
||||
- "--useLocalhostForP2P=false"
|
||||
- "--useDevPrivilegeKeys=false"
|
||||
- "--nodePort=2003"
|
||||
- "--appName=haveno-XMR_${BASE_NETWORK}_Seed_2003"
|
||||
- "--hiddenServiceAddress=ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion"
|
||||
- "--seedNodes=5i6blbmuflq4s4im6zby26a7g22oef6kyp7vbwyru6oq5e36akzo3ayd.onion:2001,dx4ktxyiemjc354imehuaswbhqlidhy62b4ifzigk5p2rb37lxqbveqd.onion:2002,ajbqx4clnjlr7lmzoftuvpvmqafdiilidsgocvokx6bqj3okk56ccfqd.onion:2003"
|
||||
restart: unless-stopped
|
109
docker/torrc
Normal file
|
@ -0,0 +1,109 @@
|
|||
## Configuration file for Haveno Seednode
|
||||
##
|
||||
## Tor opens a socks proxy on port 9050 by default -- even if you don't
|
||||
## configure one below. Set "SocksPort 0" if you plan to run Tor only
|
||||
## as a relay, and not make any local application connections yourself.
|
||||
#SocksPort 9050 # Default: Bind to localhost:9050 for local connections.
|
||||
# ### SocksPort flag: OnionTrafficOnly ###
|
||||
## Tell the tor client to only connect to .onion addresses in response to SOCKS5 requests on this connection.
|
||||
## This is equivalent to NoDNSRequest, NoIPv4Traffic, NoIPv6Traffic.
|
||||
# ### SocksPort flag: ExtendedErrors ###
|
||||
## Return extended error code in the SOCKS reply. So far, the possible errors are:
|
||||
# X'F0' Onion Service Descriptor Can Not be Found
|
||||
# X'F1' Onion Service Descriptor Is Invalid
|
||||
# X'F2' Onion Service Introduction Failed
|
||||
# X'F3' Onion Service Rendezvous Failed
|
||||
# X'F4' Onion Service Missing Client Authorization
|
||||
# X'F5' Onion Service Wrong Client Authorization
|
||||
# X'F6' Onion Service Invalid Address
|
||||
# X'F7' Onion Service Introduction Timed Out
|
||||
SocksPort 9050 OnionTrafficOnly ExtendedErrors
|
||||
|
||||
## Entry policies to allow/deny SOCKS requests based on IP address.
|
||||
## First entry that matches wins. If no SocksPolicy is set, we accept
|
||||
## all (and only) requests that reach a SocksPort. Untrusted users who
|
||||
## can access your SocksPort may be able to learn about the connections
|
||||
## you make.
|
||||
SocksPolicy accept 127.0.0.1
|
||||
SocksPolicy accept6 [::1]
|
||||
SocksPolicy reject *
|
||||
|
||||
## Tor will reject application connections that use unsafe variants of the socks protocol
|
||||
## — ones that only provide an IP address, meaning the application is doing a DNS resolve first.
|
||||
## Specifically, these are socks4 and socks5 when not doing remote DNS. (Default: 0)
|
||||
#SafeSocks 1
|
||||
|
||||
## Tor will make a notice-level log entry for each connection to the Socks port indicating
|
||||
## whether the request used a safe socks protocol or an unsafe one (see above entry on SafeSocks).
|
||||
## This helps to determine whether an application using Tor is possibly leaking DNS requests. (Default: 0)
|
||||
TestSocks 1
|
||||
|
||||
## Logs go to stdout at level "notice" unless redirected by something
|
||||
## else, like one of the below lines. You can have as many Log lines as
|
||||
## you want.
|
||||
##
|
||||
## We advise using "notice" in most cases, since anything more verbose
|
||||
## may provide sensitive information to an attacker who obtains the logs.
|
||||
##
|
||||
## Send all messages of level 'notice' or higher to /var/log/tor/notices.log
|
||||
#Log notice file /var/log/tor/notices.log
|
||||
## Send every possible message to /var/log/tor/debug.log
|
||||
#Log debug file /var/log/tor/debug.log
|
||||
## Use the system log instead of Tor's logfiles (This is default)
|
||||
#Log notice syslog
|
||||
## To send all messages to stderr:
|
||||
#Log debug stderr
|
||||
|
||||
# Try to write to disk less frequently than we would otherwise. This is useful when running on flash memory.
|
||||
AvoidDiskWrites 1
|
||||
|
||||
HiddenServiceStatistics 0
|
||||
|
||||
## Compute the hash of a password with "tor --hash-password password".
|
||||
HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
|
||||
#CookieAuthentication 0 # (Default: 1)
|
||||
|
||||
## MetricsPort provides an interface to the underlying Tor relay metrics.
|
||||
## Exposing publicly is dangerous, set a very strict access policy.
|
||||
## Retrieve the metrics with: curl http://127.0.0.1:9035/metrics
|
||||
MetricsPort 127.0.0.1:9035
|
||||
MetricsPortPolicy accept 127.0.0.1
|
||||
MetricsPortPolicy accept [::1]
|
||||
|
||||
HiddenServiceDir /var/lib/tor/haveno_seednode_2001
|
||||
HiddenServicePort 2001 127.0.0.1:2001
|
||||
HiddenServicePort 2001 [::1]:2001
|
||||
HiddenServiceEnableIntroDoSDefense 1
|
||||
HiddenServicePoWDefensesEnabled 1
|
||||
HiddenServicePoWQueueRate 50 # (Default: 250)
|
||||
HiddenServicePoWQueueBurst 250 # (Default: 2500)
|
||||
HiddenServiceMaxStreams 25
|
||||
|
||||
|
||||
HiddenServiceDir /var/lib/tor/haveno_seednode_2002
|
||||
HiddenServicePort 2002 127.0.0.1:2002
|
||||
HiddenServicePort 2002 [::1]:2002
|
||||
HiddenServiceEnableIntroDoSDefense 1
|
||||
#HiddenServiceEnableIntroDoSRatePerSec 25 # (Default: 25)
|
||||
#HiddenServiceEnableIntroDoSBurstPerSec 200 # (Default: 200)
|
||||
HiddenServicePoWDefensesEnabled 1
|
||||
HiddenServicePoWQueueRate 50 # (Default: 250)
|
||||
HiddenServicePoWQueueBurst 250 # (Default: 2500)
|
||||
HiddenServiceMaxStreams 25
|
||||
#HiddenServiceMaxStreamsCloseCircuit 1
|
||||
|
||||
|
||||
HiddenServiceDir /var/lib/tor/haveno_seednode_2003
|
||||
HiddenServicePort 2003 127.0.0.1:2003
|
||||
HiddenServicePort 2003 [::1]:2003
|
||||
HiddenServiceEnableIntroDoSDefense 1
|
||||
#HiddenServiceEnableIntroDoSRatePerSec 25 # (Default: 25)
|
||||
#HiddenServiceEnableIntroDoSBurstPerSec 200 # (Default: 200)
|
||||
#HiddenServiceNumIntroductionPoints 3 # (Default: 3)
|
||||
HiddenServicePoWDefensesEnabled 1
|
||||
HiddenServicePoWQueueRate 50 # (Default: 250)
|
||||
HiddenServicePoWQueueBurst 250 # (Default: 2500)
|
||||
HiddenServiceMaxStreams 25
|
||||
#HiddenServiceMaxStreamsCloseCircuit 1
|
||||
|
||||
LongLivedPorts 2001,2002,2003
|
|
@ -1124,3 +1124,201 @@ message AddressBalanceInfo {
|
|||
int64 num_confirmations = 3;
|
||||
bool is_address_unused = 4;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Marketplace Protospec
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Orders
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service Orders {
|
||||
rpc GetOrder (GetOrderRequest) returns (GetOrderReply) {
|
||||
}
|
||||
rpc GetOrders (GetOrdersRequest) returns (GetOrdersReply) {
|
||||
}
|
||||
rpc MakeOrder (MakeOrderRequest) returns (MakeOrderReply) {
|
||||
}
|
||||
rpc ConfirmOrderAccepted (ConfirmOrderAcceptedRequest) returns (ConfirmOrderAcceptedReply) {
|
||||
}
|
||||
rpc ConfirmOrderProcessing (ConfirmOrderProcessingRequest) returns (ConfirmOrderProcessingReply) {
|
||||
}
|
||||
rpc ConfirmOrderShipped (ConfirmOrderShippedRequest) returns (ConfirmOrderShippedReply) {
|
||||
}
|
||||
rpc FinalizeOrder (FinalizeOrderRequest) returns (FinalizeOrderReply) {
|
||||
}
|
||||
rpc GetChatMessages (GetChatMessagesRequest) returns (GetChatMessagesReply) {
|
||||
}
|
||||
rpc SendChatMessage (SendChatMessageRequest) returns (SendChatMessageReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message GetMyOrderRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetMyOrderReply {
|
||||
OrderInfo order = 1;
|
||||
}
|
||||
|
||||
message GetOrdersRequest {
|
||||
string direction = 1;
|
||||
string currency_code = 2;
|
||||
}
|
||||
|
||||
message GetOrdersReply {
|
||||
repeated OfferInfo orders = 1;
|
||||
}
|
||||
|
||||
message GetMyOrdersRequest {
|
||||
}
|
||||
|
||||
message GetMyOrdersReply {
|
||||
repeated OfferInfo orders = 1;
|
||||
}
|
||||
|
||||
message MakeOrderRequest {
|
||||
string currency_code = 1;
|
||||
string direction = 2;
|
||||
string price = 3;
|
||||
bool use_market_based_price = 4;
|
||||
double market_price_margin_pct = 5;
|
||||
uint64 amount = 6 [jstype = JS_STRING];
|
||||
uint64 min_amount = 7 [jstype = JS_STRING];
|
||||
double security_deposit_pct = 8;
|
||||
string trigger_price = 9;
|
||||
bool reserve_exact_amount = 10;
|
||||
string payment_account_id = 11;
|
||||
bool is_private_order = 12;
|
||||
bool buyer_as_taker_without_deposit = 13;
|
||||
string extra_info = 14;
|
||||
string source_order_id = 15;
|
||||
}
|
||||
|
||||
message MakeOrderReply {
|
||||
OrderInfo order = 1;
|
||||
}
|
||||
|
||||
message CancelOrderRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message CancelOrderReply {
|
||||
}
|
||||
|
||||
message ConfirmOrderAcceptedRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ConfirmOrderAcceptedReply {
|
||||
OrderInfo offer = 1;
|
||||
}
|
||||
|
||||
message ConfirmOrderProcessingRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ConfirmOrderProcessingReply {
|
||||
OrderInfo order = 1;
|
||||
}
|
||||
|
||||
message ConfirmOrderShippedRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message ConfirmOrderShippedReply {
|
||||
OrderInfo order = 1;
|
||||
}
|
||||
|
||||
message FinalizeOrderRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message FinalizeOrderReply {
|
||||
OrderInfo order = 1;
|
||||
}
|
||||
|
||||
message OrderInfo {
|
||||
string id = 1;
|
||||
string category = 2;
|
||||
string purchase_price = 3;
|
||||
bool use_market_based_price = 4;
|
||||
double market_price_margin_pct = 5;
|
||||
uint64 amount = 6 [jstype = JS_STRING];
|
||||
uint64 quantity = 7 [jstype = JS_STRING];
|
||||
double buyer_fee_pct = 8;
|
||||
double seller_fee_pct = 9;
|
||||
double penalty_fee_pct = 10;
|
||||
double buyer_security_deposit_pct = 11;
|
||||
double seller_security_deposit_pct = 12;
|
||||
string max_purchase_quantity = 13;
|
||||
ShippingOptionInfo shipping_option = 14;
|
||||
string trigger_price = 15;
|
||||
string payment_account_id = 16;
|
||||
string payment_method_id = 17;
|
||||
string payment_method_short_name = 18;
|
||||
string base_currency_code = 19;
|
||||
string seller_public_key = 20;
|
||||
uint64 date = 21;
|
||||
string state = 22;
|
||||
bool is_activated = 23;
|
||||
bool is_my_order = 24;
|
||||
string store_node_address = 25;
|
||||
string pub_key_ring = 26;
|
||||
string version_nr = 27;
|
||||
int32 protocol_version = 28;
|
||||
string arbitrator_signer = 29;
|
||||
string split_output_tx_hash = 30;
|
||||
uint64 split_output_tx_fee = 31 [jstype = JS_STRING];
|
||||
bool is_private_order = 32;
|
||||
string challenge = 33;
|
||||
string extra_info = 34;
|
||||
}
|
||||
|
||||
message GetOrderRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetOrderReply {
|
||||
OrderInfo order = 1;
|
||||
}
|
||||
|
||||
message ShippingOptionInfo {
|
||||
string id = 1;
|
||||
string label = 2;
|
||||
string price = 3;
|
||||
StoreInfo store = 4;
|
||||
}
|
||||
|
||||
message GetShippingOptionRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetShippingOptionReply {
|
||||
ShippingOptionInfo shipping_option = 1;
|
||||
}
|
||||
|
||||
message StoreInfo {
|
||||
string id = 1;
|
||||
string label = 2;
|
||||
string name = 3;
|
||||
bool is_active = 4;
|
||||
double cost = 5;
|
||||
uint64 estimated_days = 6 [jstype = JS_STRING];
|
||||
uint64 quantity = 7 [jstype = JS_STRING];
|
||||
StoreInfo store = 8;
|
||||
}
|
||||
|
||||
message GetStoreRequest {
|
||||
string store_id = 1;
|
||||
}
|
||||
|
||||
message GetStoreReply {
|
||||
StoreInfo store = 1;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Products
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
@ -12,5 +12,6 @@ include 'seednode'
|
|||
include 'statsnode'
|
||||
include 'inventory'
|
||||
include 'apitest'
|
||||
//include 'bot'
|
||||
|
||||
rootProject.name = 'haveno'
|
||||
|
|