` (e.g., `testGetBaseSeedRoundTrip`, `testDiscovery_FindsUsedAddresses`)
+- Tests use `@TestMethodOrder(OrderAnnotation.class)` + `@Order(n)` for sequencing
+- Use `static import org.junit.jupiter.api.Assertions.*` for assertions
+
+### Error Handling & Security
+
+- Crypto ops: try/finally with `Arrays.fill(bytes, (byte) 0)` to clear sensitive data
+- Call `PBEKeySpec.clearPassword()` after key derivation
+- Never use `e.printStackTrace()` — use `LOGGER.level(msg + e.getMessage());`
+- Catch specific exceptions before generic `Exception`
+- `RuntimeException` for unrecoverable state; return `null`/`false` for expected failures
+- AES-CBC + random IV for new encryption; ECB only for legacy migration
+- PBKDF2WithHmacSHA256, 100k iterations; `SecureRandom.getInstanceStrong()` for RNG
+- Always use `StandardCharsets.UTF_8` for `getBytes()` calls
+- Passphrases use `char[]` — `String` is unsupported (immutable, cannot be wiped)
+
+## Project Structure
+
+```
+src/main/java/io/xlite/daemon/app/
+ crypto/ KeyHandler (wallet encryption/key management)
+ net/ CoinInstance (coin lifecycle), JSON-RPC servers, protocols/
+ util/ ConfigHelper, AddressDiscoveryService, UTXO, logging
+ wallet/ WalletHelper
+ App.java Main entry point
+
+src/test/java/
+ KeyHandlerTest, CoinInstanceTest, ConfigHelperTest,
+ AddressDiscoveryServiceTest, TestHelper
+```
+
+## Key Dependencies
+
+| Library | Purpose |
+|---------|---------|
+| bitcoinj-core 0.15.10 | Bitcoin/crypto, MnemonicCode, ECKey |
+| Gson 2.13.2 | JSON serialization |
+| Netty 4.2.7 | HTTP servers, networking |
+| Guava 28.2-android | Joiner, Preconditions, AtomicDouble |
+| Orchid 1.2.1 | Hex/Base64 encoders |
+| httpclient 4.5.14 | HTTP client |
+| json 20250517 | JSONObject/JSONArray |
+| java-dotenv 5.2.2 | .env file support |
+| JUnit Jupiter 5.11.3 | Test framework |
+| Mockito 5.15.2 | Test mocking |
+
+## Commit Message Style
+
+Recent commits use conventional commits: `type(scope): description`
+
+Types: `feat`, `fix`, `chore`, `refactor`, `build`, `security`, `test`.
+
+Some older commits use `[category] description` style (e.g., `[security] Upgrade wallet encryption`).
+
+## Things to Watch For
+
+- The `rewrite-maven-plugin` no longer runs in the default build (it cost ~8s
+ per build); it executes only under the `-Prewrite` profile and may auto-modify
+ imports and formatting. Run it before releases or style sweeps, and always
+ review `git diff` afterwards.
+- `ConfigHelper.CONFIG_DIR` is a mutable static used to override config path in tests.
+- Tests use `@TempDir` (JUnit 5 auto-cleanup); never run with parallel execution
+ due to mutable `ConfigHelper.CONFIG_DIR` static state.
+- Mockito requires ByteBuddy agent (configured in pom.xml surefire plugin).
+- App reads `.env` via java-dotenv (`App.getEnv()` wraps `Dotenv`).
+- Javadoc uses `` tags and `{@code}` inline; section dividers use `// ===` / `// ---`.
+- The enforcer plugin requires Java 21 and Maven 3.8.6+.
diff --git a/README.md b/README.md
index 720df77..503d2ac 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,13 @@
# Xlite Wallet Backend
-The Xlite Wallet Backend is a Java-based project that serves as the backend infrastructure for the Xlite wallet application. It is built using Java with JDK version 21 and utilizes Gradle for build automation. The project incorporates the org.bitcoinj library version 0.14.7 for Bitcoin-related functionality.
+The Xlite Wallet Backend is a Java-based project that serves as the backend infrastructure for the Xlite wallet application. It is built using Java with JDK version 21 and utilizes Maven for build automation. The project incorporates the org.bitcoinj library version 0.14.7 for Bitcoin-related functionality.
## Table of Contents
- [Project Overview](#project-overview)
- [Prerequisites](#prerequisites)
- [Getting Started](#getting-started)
+- [Maven Build Commands](#maven-build-commands)
- [Usage](#usage)
- [Configuration](#configuration)
- [Contributing](#contributing)
@@ -21,6 +22,7 @@ Provide a brief description of the Xlite Wallet Backend project. Explain its pur
List the prerequisites required to set up and run the Xlite Wallet Backend. Include the following:
- JDK 21: Install the Java Development Kit version 21 or a compatible version.
+- Maven 3.8.6 or higher: Install Apache Maven for build automation.
## Getting Started
@@ -34,18 +36,59 @@ git clone https://github.com/blocknetdx/xlite-daemon
```
cd xlite-daemon
-# mac/linux:
-chmod +x gradlew
-./gradlew nativeImage
+# Make Maven wrapper executable (mac/linux):
+chmod +x mvnw
-# windows:
-nativeImageWindows.bat
+# Build native image:
+./mvnw clean package -Pnative
+
+# Or for faster build without tests:
+./mvnw clean package -Pnative-fast
```
3. Configuration: If any configuration files or settings need to be modified, provide instructions on how to set them up.
-4. Run the application:
+4. Run the application:
+```
+./target/xlite-daemon
+```
+
+## Maven Build Commands
+
+### Basic Maven Operations
+
+```bash
+# Clean and compile
+mvn clean compile
+
+# Run tests
+mvn test
+
+# Package JAR (without native compilation)
+mvn package -DskipTests
+
+# Build native image
+mvn clean package -Pnative
+
+# Run application
+mvn exec:java
+
+# Skip tests for faster builds
+mvn clean package -Pnative-fast
```
-binary to find in build/graal/ folder
+
+### Profile-Specific Commands
+
+- **native**: Full native image compilation with all optimizations
+- **native-fast**: Faster native compilation with reduced optimizations for development
+
+### Common Maven Goals
+
+- `mvn clean`: Remove build artifacts
+- `mvn compile`: Compile source code
+- `mvn test`: Run unit tests
+- `mvn package`: Package compiled code into distributable format
+- `mvn install`: Install package into local repository
+- `mvn dependency:tree`: Display dependency tree
```
## Usage
@@ -69,6 +112,7 @@ gettxout - Get info about an unspent transaction output
=====Network=====
getinfo - Get information such as balances, protocol version, and more
getnetworkinfo - Get network information
+getfees - Get fee per byte and minimum transaction fee for the current coin
getrawmempool - Get raw mempool
getblockchaininfo - Get blockchain info
getblockhash - Get the hash of a block at a given height
@@ -99,15 +143,20 @@ xlite-daemon (Backend) Configuration Files:
```
Windows
-%appdata%\CloudChains\settings\config-*.json
+%appdata%\xlite-daemon\settings\config-*.json
MacOS
-~/Library/Application Support/CloudChains/settings/config-*.json
+~/Library/Application Support/xlite-daemon/settings/config-*.json
Linux
-~/.config/CloudChains/settings/config-*.json
+~/.config/xlite-daemon/settings/config-*.json
```
+Set `XLITE_DATA_HOME` to override the platform directory above. The
+daemon aborts at startup when no override is set and the platform
+default is missing (Windows with unset `%AppData%`, or an empty
+`user.home`).
+
## Contributing
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 123e51d..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,118 +0,0 @@
-plugins {
- id 'application'
- id 'org.graalvm.buildtools.native' version '0.11.3'
- id 'com.gradleup.shadow' version '9.3.0'
-}
-
-java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(21)
- }
-}
-
-application {
- mainClass = 'io.cloudchains.app.App'
-}
-
-ext {
- bitcoinjVersion = '0.14.7'
- nettyVersion = '4.1.115.Final'
- junitVersion = '5.11.3'
- slf4jVersion = '2.0.16'
- gsonVersion = '2.13.2'
-}
-
-version = "0.5.14"
-group = 'io.cloudchains'
-
-repositories {
- mavenCentral()
- gradlePluginPortal()
-}
-
-run {
- if (project.hasProperty('args')) {
- args project.args.split("=")
- }
- standardInput = System.in
-}
-
-shadowJar {
- // mergeServiceFiles()
-}
-
-test {
- useJUnitPlatform()
-}
-
-
-graalvmNative {
- binaries {
- main {
- buildArgs.add('--no-fallback')
- imageName = 'xlite-daemon'
- mainClass = 'io.cloudchains.app.App'
- buildArgs.addAll([
- '--enable-native-access=ALL-UNNAMED',
- '--enable-url-protocols=http,https',
- '--features=com.oracle.svm.thirdparty.gson.GsonFeature',
- '--features=com.oracle.svm.polyglot.groovy.GroovyIndyInterfaceFeature',
- '--allow-incomplete-classpath',
- '--verbose',
- '--no-fallback',
- '--no-server'
- ])
-
- // Build-time initialization
- buildArgs.addAll([
- '--initialize-at-build-time=com.google.common.base.Charsets',
- '--initialize-at-build-time=com.google.common.math.IntMath$1',
- '--initialize-at-build-time=com.google.common.math.IntMath',
- '--initialize-at-build-time=com.google.common.base.Charsets$1',
- '--initialize-at-build-time=com.google.common.base.StandardCharsets',
- '--initialize-at-build-time=io.netty,org.apache.commons.logging,org.slf4j',
- '--initialize-at-build-time=org.slf4j.helpers.NOPLogger',
- '--initialize-at-build-time=org.slf4j.helpers.NOPLoggerFactory',
- '--initialize-at-build-time=org.slf4j.helpers.SubstituteLoggerFactory',
- '--initialize-at-build-time=org.slf4j.helpers.Util',
- '--initialize-at-build-time=org.slf4j.helpers.NOP_FallbackServiceProvider',
- '--initialize-at-build-time=org.slf4j.nop.NOPServiceProvider',
- '--initialize-at-build-time=org.slf4j.helpers.SubstituteServiceProvider',
- '--initialize-at-build-time=org.bitcoinj.core.Utils',
- '--initialize-at-build-time=org.bitcoinj.core.Sha256Hash',
- '--initialize-at-build-time=org.bitcoinj.crypto.MnemonicCode',
- '--initialize-at-build-time=com.google.common.io.BaseEncoding',
- '--initialize-at-build-time=com.google.common.io.BaseEncoding$Base16Encoding',
- '--initialize-at-build-time=com.google.common.io.BaseEncoding$Alphabet'
- ])
-
- // Runtime initialization
- buildArgs.addAll([
- '--initialize-at-run-time=io.netty.util.internal.logging.Log4JLogger',
- '--initialize-at-run-time=io.netty.handler.codec.http.HttpObjectEncoder',
- '--initialize-at-run-time=io.netty.handler.codec.http2.DefaultHttp2FrameWriter',
- '--initialize-at-run-time=io.netty.handler.codec.http2.Http2CodecUtil'
- ])
- }
- }
-}
-
-dependencies {
- implementation "org.bitcoinj:bitcoinj-core:${bitcoinjVersion}"
- implementation "org.bitcoinj:orchid:1.2.1"
- implementation 'net.jcip:jcip-annotations:1.0'
- implementation "org.json:json:20250517"
- implementation "org.apache.httpcomponents:httpclient:4.5.14"
- implementation "com.google.code.gson:gson:${gsonVersion}"
- implementation "commons-logging:commons-logging:1.2"
- implementation "org.slf4j:slf4j-nop:${slf4jVersion}"
- implementation "io.netty:netty-all:${nettyVersion}"
- implementation "com.google.code.findbugs:jsr305:3.0.2"
- compileOnly "org.graalvm.nativeimage:svm:21.+"
-
- testImplementation platform("org.junit:junit-bom:${junitVersion}")
- testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
- testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
- testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}"
- testImplementation "org.junit.platform:junit-platform-launcher"
-}
diff --git a/ci/WaitForShutdownTime.ps1 b/ci/WaitForShutdownTime.ps1
deleted file mode 100644
index 0d8cc58..0000000
--- a/ci/WaitForShutdownTime.ps1
+++ /dev/null
@@ -1,12 +0,0 @@
-while ($true) {
- $os = Get-WmiObject win32_operatingsystem -ComputerName localhost -ErrorAction SilentlyContinue
- $uptime = ((get-date) - ($os.ConvertToDateTime($os.lastbootuptime)))
- $totalUptime = ($uptime.Days * 1440) + ($uptime.Hours * 60) + ($uptime.Minutes)
- Write-Output Uptime: $totalUptime
-
- if ($totalUptime -gt 50) {
- Stop-Computer -ComputerName localhost
- };
-
- Start-Sleep 30;
-}
diff --git a/ci/windows-build.cmd b/ci/windows-build.cmd
deleted file mode 100644
index 4b7a869..0000000
--- a/ci/windows-build.cmd
+++ /dev/null
@@ -1,59 +0,0 @@
-REM GRAALVM 20.1.0 IS REQUIRED - INSTALL FROM
-REM https://github.com/graalvm/graalvm-ce-dev-builds/releases/download/20.1.0-dev-20200212_0349/graalvm-ce-java8-windows-amd64-20.1.0-dev.zip
-
-choco install -y windows-sdk-7.1 kb2519277
-choco install -y vcredist2010
-
-
-SET NATIVE_IMAGE=%1
-SET CC_PACKAGED_JAR=%2
-SET CC_DIR=%3
-
-echo Initializing Microsoft SDK 7.1 environment
-call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd"
-
-call %NATIVE_IMAGE% -jar %CC_PACKAGED_JAR% ^
- -H:Name=Cloudchains-SPV ^
- -H:Class=io.cloudchains.app.App ^
- -H:+JNI ^
- -H:+UseServiceLoaderFeature ^
- -H:ReflectionConfigurationFiles=contrib/netty-reflection.json ^
- -H:ReflectionConfigurationResources=META-INF/native-image/io.netty/transport/reflection-config.json ^
- -H:ResourceConfigurationFiles=contrib/resource-config.json ^
- -H:IncludeResources='.*/wordlist/english.txt$' ^
- -H:Log=registerResource ^
- --no-fallback ^
- --no-server ^
- -da ^
- --enable-url-protocols=http,https ^
- --initialize-at-build-time=io.netty ^
- --initialize-at-build-time=com.google.common ^
- --initialize-at-build-time=org.apache.commons.logging ^
- --initialize-at-build-time=org.slf4j.LoggerFactory ^
- --initialize-at-build-time=org.slf4j.impl.StaticLoggerBinder ^
- --initialize-at-build-time=org.slf4j.helpers.NOPLogger ^
- --initialize-at-build-time=org.slf4j.helpers.NOPLoggerFactory ^
- --initialize-at-build-time=org.slf4j.helpers.SubstituteLoggerFactory ^
- --initialize-at-build-time=org.slf4j.helpers.Util ^
- --initialize-at-build-time=org.bitcoinj.core.Utils ^
- --initialize-at-build-time=org.bitcoinj.core.Sha256Hash ^
- --initialize-at-build-time=org.bitcoinj.crypto.MnemonicCode ^
- --initialize-at-run-time=io.netty.util.internal.logging.Log4JLogger ^
- --initialize-at-run-time=io.netty.handler.codec.http.HttpObjectEncoder ^
- --initialize-at-run-time=io.netty.handler.codec.http2.DefaultHttp2FrameWriter ^
- --initialize-at-run-time=io.netty.handler.codec.http2.Http2CodecUtil ^
- --initialize-at-run-time=io.netty.buffer.PooledByteBufAllocator ^
- --initialize-at-run-time=io.netty.buffer.ByteBufAllocator ^
- --initialize-at-run-time=io.netty.buffer.ByteBufUtil ^
- --initialize-at-run-time=io.netty.buffer.AbstractReferenceCountedByteBuf ^
- --initialize-at-run-time=io.netty.handler.codec.http2.Http2CodecUtil ^
- --initialize-at-run-time=io.netty.handler.codec.http2.Http2ClientUpgradeCodec ^
- --initialize-at-run-time=io.netty.handler.codec.http2.Http2ConnectionHandler ^
- --initialize-at-run-time=io.netty.handler.codec.http2.DefaultHttp2FrameWriter ^
- --initialize-at-run-time=io.netty.util.AbstractReferenceCounted ^
- --initialize-at-run-time=io.netty.handler.codec.http.HttpObjectEncoder ^
- --initialize-at-run-time=io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder ^
- --initialize-at-run-time=io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder ^
- --initialize-at-run-time=io.netty.handler.ssl.util.ThreadLocalInsecureRandom ^
- --allow-incomplete-classpath ^
- --verbose
diff --git a/contrib/netty-reflection.json b/contrib/netty-reflection.json
deleted file mode 100644
index d2af5dc..0000000
--- a/contrib/netty-reflection.json
+++ /dev/null
@@ -1,98 +0,0 @@
-[
- {
- "name": "io.netty.channel.socket.nio.NioServerSocketChannel",
- "methods": [
- { "name": "", "parameterTypes": [] }
- ]
- },
- {
- "name" : "io.cloudchains.app.net.CoinInstance",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "io.cloudchains.app.net.api.JSONRPCController",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "io.cloudchains.app.net.api.http.HTTPServerHandler",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "java.lang.String",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "org.bitcoinj.script.Script",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "org.bitcoinj.script.Script$ScriptType",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "org.bitcoinj.wallet.DeterministicSeed",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "org.apache.commons.logging.LogFactory",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- },
- {
- "name" : "org.apache.commons.logging.impl.LogFactoryImpl",
- "allDeclaredConstructors" : true,
- "allPublicConstructors" : true,
- "allDeclaredMethods" : true,
- "allPublicMethods" : true,
- "allDeclaredClasses" : true,
- "allPublicClasses" : true
- }
-]
\ No newline at end of file
diff --git a/contrib/resource-config.json b/contrib/resource-config.json
deleted file mode 100644
index a146d7a..0000000
--- a/contrib/resource-config.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "resources":[
- {"pattern":"META-INF/services/jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory"},
- {"pattern":"META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator"},
- {"pattern":"mozilla/public-suffix-list.txt"},
- {"pattern":"org/apache/http/client/version.properties"},
- {"pattern":"org/bitcoinj/crypto/mnemonic/wordlist/english.txt"},
- {"pattern":"org/slf4j/impl/StaticLoggerBinder.class"},
- {"pattern":"sun/net/idn/uidna.spp"},
- {"pattern":"sun/text/resources/unorm.icu"}
- ]
-}
diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md
new file mode 100644
index 0000000..db1a287
--- /dev/null
+++ b/docs/USER_GUIDE.md
@@ -0,0 +1,877 @@
+# XLite Daemon User Guide
+
+## Table of Contents
+1. [Overview](#overview)
+2. [System Requirements](#system-requirements)
+3. [Installation](#installation)
+4. [Configuration](#configuration)
+5. [Usage](#usage)
+6. [Supported Cryptocurrencies](#supported-cryptocurrencies)
+7. [Security Features](#security-features)
+8. [Troubleshooting](#troubleshooting)
+9. [API Reference](#api-reference)
+10. [Advanced Configuration](#advanced-configuration)
+
+## Overview
+
+The XLite Daemon is a multi-cryptocurrency wallet backend that provides secure wallet management, blockchain connectivity, and JSON-RPC API services for various cryptocurrencies. It serves as the backend infrastructure for the XLite wallet application.
+
+### Key Features
+- **Multi-Currency Support**: Supports 10+ cryptocurrencies including Bitcoin, Litecoin, Dash, and more
+- **Secure Wallet Management**: Industry-standard encryption with PBKDF2 key derivation
+- **Address Discovery**: Automatic detection of used addresses for wallet recovery
+- **JSON-RPC API**: Comprehensive API for wallet operations and blockchain queries
+- **XRouter Integration**: Support for cross-chain communication (Blocknet network)
+- **Native Compilation**: Optimized native binary for better performance
+
+## System Requirements
+
+### Minimum Requirements
+- **Operating System**: Windows 10+, macOS 10.14+, Linux (kernel 3.10+)
+- **Java**: JDK 21 or higher
+- **Maven**: 3.8.6 or higher
+- **Memory**: 2GB RAM minimum, 4GB recommended
+- **Storage**: 500MB free space for wallet data and blockchain metadata
+
+### Recommended Requirements
+- **Operating System**: Latest stable version of your preferred OS
+- **Java**: Latest JDK 21 LTS
+- **Memory**: 8GB RAM or more
+- **Storage**: SSD with 1GB+ free space
+- **Network**: Stable internet connection
+
+## Installation
+
+### Prerequisites
+1. Install JDK 21:
+ ```bash
+ # Ubuntu/Debian
+ sudo apt update && sudo apt install openjdk-21-jdk
+
+ # macOS (using Homebrew)
+ brew install openjdk@21
+
+ # Windows: Download from Oracle or Adoptium
+ ```
+
+2. Install Maven 3.8.6+:
+ ```bash
+ # Ubuntu/Debian
+ sudo apt install maven
+
+ # macOS (using Homebrew)
+ brew install maven
+
+ # Verify installation
+ java -version
+ mvn -version
+ ```
+
+### Building from Source
+
+1. **Clone the repository**:
+ ```bash
+ git clone https://github.com/blocknetdx/xlite-daemon
+ cd xlite-daemon
+ ```
+
+2. **Make Maven wrapper executable** (Linux/macOS only):
+ ```bash
+ chmod +x mvnw
+ ```
+
+3. **Build the project**:
+ ```bash
+ # Full build with native compilation (recommended)
+ ./mvnw clean package -Pnative
+
+ # Faster build without tests (development)
+ ./mvnw clean package -Pnative-fast
+ ```
+
+4. **Run the application**:
+ ```bash
+ # Using the native binary
+ ./target/xlite-daemon
+
+ # Or using Maven
+ ./mvnw exec:java
+ ```
+
+### Environment Variables
+
+Set these environment variables before running:
+
+```bash
+# Required for wallet initialization
+export WALLET_MNEMONIC="your twelve word mnemonic phrase here"
+export WALLET_PASSWORD="your secure password"
+
+# Optional: Custom EXR endpoints (comma-separated list of backend servers)
+export EXR_ENDPOINT="https://server1.example.com,https://server2.example.com"
+```
+
+## Configuration
+
+### Configuration Files Location
+
+The daemon stores configuration files in your system's application data directory:
+
+- **Windows**: `%appdata%\xlite-daemon\settings\config-*.json`
+- **macOS**: `~/Library/Application Support/xlite-daemon/settings/config-*.json`
+- **Linux**: `~/.config/xlite-daemon/settings/config-*.json`
+
+Set `XLITE_DATA_HOME` to override the platform directory above. The
+daemon aborts at startup when no override is set and the platform
+default is missing (Windows with unset `%AppData%`, or an empty
+`user.home`).
+
+### Configuration Structure
+
+Each cryptocurrency has its own configuration file named `config-{ticker}.json`:
+
+```json
+{
+ "fee": 0.0001,
+ "feeFlat": true,
+ "rpcEnabled": true,
+ "rpcUsername": "xlite",
+ "rpcPassword": "securepassword123",
+ "rpcPort": 9955,
+ "addressCount": 25
+}
+```
+
+### Configuration Options
+
+| Setting | Type | Description | Default |
+|---------|------|-------------|---------|
+| `fee` | number | Transaction fee rate | 0.0001 |
+| `feeFlat` | boolean | Use flat fee vs dynamic | true |
+| `rpcEnabled` | boolean | Enable JSON-RPC server | false |
+| `rpcUsername` | string | RPC authentication username | "" |
+| `rpcPassword` | string | RPC authentication password | "" |
+| `rpcPort` | number | RPC server port | -1000 (auto) |
+| `addressCount` | number | Number of addresses to generate | 0 |
+
+## Usage
+
+### Starting the Daemon
+
+1. **Basic startup**:
+ ```bash
+ ./target/xlite-daemon
+ ```
+
+2. **With custom arguments**:
+ ```bash
+ ./target/xlite-daemon --network=testnet --debug
+ ```
+
+3. **As a background service**:
+ ```bash
+ # Linux/macOS
+ nohup ./target/xlite-daemon > daemon.log 2>&1 &
+
+ # Windows
+ start /B xlite-daemon.exe > daemon.log
+ ```
+
+### Wallet Management
+
+#### Creating a New Wallet
+
+1. **Generate a new mnemonic**:
+ ```bash
+ # The daemon will automatically generate a 12-word mnemonic on first run
+ # or you can provide one via WALLET_MNEMONIC environment variable
+ ```
+
+2. **Set a secure password**:
+ ```bash
+ export WALLET_PASSWORD="your-secure-password-here"
+ ```
+
+3. **Verify password strength**:
+ The daemon includes a password strength calculator that evaluates:
+ - Length (8+ characters required)
+ - Character variety (uppercase, lowercase, numbers, symbols)
+ - Overall security score (0-10)
+
+#### Importing an Existing Wallet
+
+```bash
+# Set your existing mnemonic
+export WALLET_MNEMONIC="your existing twelve word phrase here"
+export WALLET_PASSWORD="your password"
+
+# Start the daemon
+./target/xlite-daemon
+```
+
+
+### Supported Operations
+
+#### Basic Wallet Operations
+
+```bash
+# Get wallet balance
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getinfo","params":[],"id":1}'
+
+# Generate new address
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getnewaddress","params":[],"id":1}'
+
+# List unspent transactions (UTXOs)
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"listunspent","params":[],"id":1}'
+```
+
+#### Transaction Operations
+
+```bash
+# Create raw transaction
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"createrawtransaction","params":[[{"txid":"...","vout":0}],{"address":0.001}],"id":1}'
+
+# Sign transaction
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"signrawtransaction","params":["rawtx"],"id":1}'
+
+# Broadcast transaction
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"sendrawtransaction","params":["signedtx"],"id":1}'
+```
+
+## Supported Cryptocurrencies
+
+The daemon supports the following cryptocurrencies:
+
+| Coin | Ticker | Network | RPC Port | Status |
+|------|--------|---------|----------|---------|
+| Blocknet | BLOCK | Mainnet | 41419 | ✅ Active |
+| Bitcoin | BTC | Mainnet | 8332 | ✅ Active |
+| Litecoin | LTC | Mainnet | 9332 | ✅ Active |
+| Dash | DASH | Mainnet | 9998 | ✅ Active |
+| Dogecoin | DOGE | Mainnet | 22555 | ✅ Active |
+| Syscoin | SYS | Mainnet | 8370 | ✅ Active |
+| PIVX | PIVX | Mainnet | 9951 | ✅ Active |
+| Unobtanium | UNO | Mainnet | 65111 | ✅ Active |
+
+### Network Configuration
+
+Each coin has specific network parameters:
+- **Mainnet**: Production blockchain networks
+- **Testnet**: Testing and development networks
+- **RPC Ports**: Default ports for JSON-RPC communication
+
+## Security Features
+
+### Wallet Encryption
+
+The daemon uses industry-standard security practices:
+
+1. **PBKDF2 Key Derivation**: SHA-256 with 100,000 iterations
+2. **AES Encryption**: 256-bit encryption for wallet data
+3. **Secure Random Generation**: Cryptographically strong random number generation
+4. **Memory Protection**: Sensitive data cleared from memory after use
+
+### Legacy Wallet Migration
+
+The daemon automatically migrates legacy wallets:
+
+1. **Detection**: Identifies SHA-1 based legacy wallets
+2. **Backup**: Creates backup before migration
+3. **Upgrade**: Migrates to SHA-256 with improved security
+4. **Validation**: Verifies migration success before cleanup
+
+### Password Security
+
+The daemon includes password strength validation:
+
+```bash
+# Password scoring system:
+# 8-9 characters: 1 point
+# 10+ characters: 2 points
+# Contains digit: +2 points
+# Contains lowercase: +2 points
+# Contains uppercase: +2 points
+# Contains special character: +2 points
+# Maximum score: 10 points
+```
+
+### Address Security
+
+1. **HD Wallet Support**: Hierarchical Deterministic wallet generation
+2. **Address Gap Limit**: Prevents address exhaustion attacks
+3. **Forward Address Generation**: Pre-generates addresses for performance
+4. **Address Discovery**: Automatically finds used addresses for recovery
+
+## Troubleshooting
+
+### Common Issues
+
+#### 1. Port Already in Use
+
+**Problem**: "Address already in use" error on startup
+
+**Solution**:
+```bash
+# Check which process is using the port
+lsof -i :9955 # Linux/macOS
+netstat -ano | findstr :9955 # Windows
+
+# Kill the process or change the port in config
+```
+
+#### 2. Wallet Not Found
+
+**Problem**: "Wallet not found on disk" error
+
+**Solution**:
+```bash
+# Check if wallet file exists
+ls ~/.config/xlite-daemon/key.dat
+
+# Verify permissions
+chmod 600 ~/.config/xlite-daemon/key.dat
+```
+
+#### 3. Network Connection Issues
+
+**Problem**: Cannot connect to blockchain networks
+
+**Solution**:
+```bash
+# Check network connectivity
+ping blockexplorer.com
+
+# Verify firewall settings
+# Ensure outbound connections are allowed
+```
+
+#### 4. Memory Issues
+
+**Problem**: OutOfMemoryError or slow performance
+
+**Solution**:
+```bash
+# Increase Java heap size
+export JAVA_OPTS="-Xmx2g -Xms1g"
+
+# Or modify the startup script
+java -Xmx2g -jar xlite-daemon.jar
+```
+
+### Log Analysis
+
+#### Log File Locations
+
+- **Error logs**: `~/.config/xlite-daemon/error-YYYY-MM-DD.log`
+- **Application logs**: Console output (configurable)
+
+#### Common Log Patterns
+
+```bash
+# Wallet initialization
+[wallet] Initializing wallet for BTC
+
+# Network connection
+[peer] Connecting to network...
+
+# RPC requests
+[rpc] Received JSON-RPC request: getinfo
+
+# Address discovery
+[discovery] Processing batch starting at index 100
+```
+
+### Debug Mode
+
+Enable debug logging:
+
+```bash
+# Set logging level
+export LOG_LEVEL=DEBUG
+
+# Or modify logging.properties
+handlers=java.util.logging.ConsoleHandler
+.level=FINE
+```
+
+### Performance Optimization
+
+#### Address Discovery Optimization
+
+1. **Batch Size**: Adjust batch size for your network
+2. **Gap Limit**: Modify gap limit for faster discovery
+3. **Timeout Settings**: Configure discovery timeouts
+
+#### Memory Management
+
+1. **Heap Size**: Allocate sufficient memory
+2. **GC Tuning**: Optimize garbage collection
+3. **Connection Pooling**: Reuse network connections
+
+### Recovery Procedures
+
+#### Wallet Recovery
+
+1. **From Mnemonic**:
+ ```bash
+ export WALLET_MNEMONIC="your twelve word phrase"
+ ./target/xlite-daemon
+ ```
+
+2. **From Backup**:
+ ```bash
+ # Restore from backup directory
+ cp ~/.config/xlite-daemon/backups/key-backup-*.dat ~/.config/xlite-daemon/key.dat
+ ```
+
+#### Configuration Recovery
+
+1. **Reset Configuration**:
+ ```bash
+ # Remove config files to reset
+ rm ~/.config/xlite-daemon/settings/config-*.json
+ ```
+
+2. **Rebuild from Source**:
+ ```bash
+ ./mvnw clean package -Pnative
+ ```
+
+## API Reference
+
+### JSON-RPC API
+
+The daemon provides a comprehensive JSON-RPC API for wallet and blockchain operations.
+
+#### Authentication
+
+All RPC requests require authentication:
+
+```bash
+# Set credentials in config file or environment
+rpcUsername=xlite
+rpcPassword=yourpassword
+```
+
+#### Blockchain Operations
+
+##### getblockchaininfo
+Get blockchain information
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getblockchaininfo","params":[],"id":1}'
+```
+
+**Response**:
+```json
+{
+ "result": {
+ "chain": "main",
+ "blocks": 700000,
+ "headers": 700000,
+ "bestblockhash": "00000000000000000007bd1b11320e37172c4467554f7a87bf769874e65e361d",
+ "difficulty": 21768156514421.46,
+ "mediantime": 1640995200
+ }
+}
+```
+
+##### getblockhash
+Get block hash by height
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getblockhash","params":[700000],"id":1}'
+```
+
+##### getblock
+Get block information
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getblock","params":["blockhash"],"id":1}'
+```
+
+#### Wallet Operations
+
+##### getinfo
+Get wallet information
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getinfo","params":[],"id":1}'
+```
+
+**Response**:
+```json
+{
+ "result": {
+ "version": "0.5.15",
+ "protocolversion": 70015,
+ "walletversion": 169900,
+ "balance": 1.50000000,
+ "blocks": 700000,
+ "timeoffset": 0,
+ "connections": 8,
+ "proxy": "",
+ "difficulty": 21768156514421.46,
+ "testnet": false,
+ "keypoololdest": 1640995200,
+ "keypoolsize": 1000,
+ "paytxfee": 0.00001000,
+ "relayfee": 0.00001000,
+ "errors": ""
+ }
+}
+```
+
+##### getnewaddress
+Generate new wallet address
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"getnewaddress","params":[],"id":1}'
+```
+
+**Response**:
+```json
+{
+ "result": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
+}
+```
+
+##### listunspent
+List unspent transaction outputs
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"listunspent","params":[],"id":1}'
+```
+
+**Response**:
+```json
+{
+ "result": [
+ {
+ "txid": "a1b2c3d4...",
+ "vout": 0,
+ "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
+ "scriptPubKey": "001472749d6b6e...",
+ "amount": 0.50000000,
+ "confirmations": 100,
+ "spendable": true
+ }
+ ]
+}
+```
+
+##### gettransaction
+Get transaction details
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"gettransaction","params":["txid"],"id":1}'
+```
+
+#### Raw Transaction Operations
+
+##### createrawtransaction
+Create raw transaction
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"createrawtransaction","params":[[{"txid":"a1b2c3d4...","vout":0}],[{"bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh":0.1}]],"id":1}'
+```
+
+##### decoderawtransaction
+Decode raw transaction
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"decoderawtransaction","params":["rawtx"],"id":1}'
+```
+
+##### signrawtransaction
+Sign raw transaction
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"signrawtransaction","params":["rawtx"],"id":1}'
+```
+
+##### sendrawtransaction
+Broadcast transaction
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"sendrawtransaction","params":["signedtx"],"id":1}'
+```
+
+#### Utility Operations
+
+##### dumpprivkey
+Export private key
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"dumpprivkey","params":["address"],"id":1}'
+```
+
+##### importprivkey
+Import private key
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"importprivkey","params":["privatekey"],"id":1}'
+```
+
+##### signmessage
+Sign message with address
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"signmessage","params":["address","message"],"id":1}'
+```
+
+##### verifymessage
+Verify signed message
+
+```bash
+curl -X POST http://localhost:9955 \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"verifymessage","params":["address","signature","message"],"id":1}'
+```
+
+### Error Handling
+
+#### Common Error Codes
+
+| Code | Message | Description |
+|------|---------|-------------|
+| -1 | "Bad password" | Incorrect wallet password |
+| -2 | "Unsupported coin" | Coin not supported |
+| -3 | "Bad mnemonic" | Invalid mnemonic phrase |
+| -4 | "Change password failed" | Password change failed |
+| -5 | "Wallet not found" | No wallet file exists |
+
+#### Error Response Format
+
+```json
+{
+ "error": {
+ "code": -1,
+ "message": "Bad password"
+ },
+ "id": 1
+}
+```
+
+## Advanced Configuration
+
+### Custom RPC Ports
+
+Configure custom RPC ports for each coin:
+
+```json
+{
+ "rpcPort": 9955,
+ "rpcEnabled": true,
+ "rpcUsername": "xlite",
+ "rpcPassword": "securepassword"
+}
+```
+
+### Network Configuration
+
+#### Proxy Settings
+
+Configure proxy for network connections:
+
+```bash
+# Set proxy environment variables
+export http_proxy="http://proxy.company.com:8080"
+export https_proxy="http://proxy.company.com:8080"
+```
+
+#### Custom Nodes
+
+Configure custom blockchain nodes:
+
+```json
+{
+ "customNodes": [
+ "node1.example.com:8333",
+ "node2.example.com:8333"
+ ]
+}
+```
+
+### Performance Tuning
+
+#### JVM Parameters
+
+Optimize Java Virtual Machine settings:
+
+```bash
+# High-performance settings
+export JAVA_OPTS="-Xmx4g -Xms2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
+```
+
+#### Network Optimization
+
+```bash
+# Increase connection limits
+export MAX_CONNECTIONS=50
+
+# Optimize network buffers
+export NETWORK_BUFFER_SIZE=65536
+```
+
+### Security Hardening
+
+#### Firewall Configuration
+
+Allow necessary ports:
+
+```bash
+# Linux (iptables)
+sudo iptables -A INPUT -p tcp --dport 9955 -j ACCEPT
+sudo iptables -A INPUT -p tcp --dport 41419 -j ACCEPT
+
+# Windows (PowerShell)
+New-NetFirewallRule -DisplayName "XLite Daemon" -Direction Inbound -Protocol TCP -LocalPort 9955 -Action Allow
+```
+
+#### File Permissions
+
+Secure wallet and configuration files:
+
+```bash
+# Set restrictive permissions
+chmod 600 ~/.config/xlite-daemon/key.dat
+chmod 600 ~/.config/xlite-daemon/settings/config-*.json
+chmod 700 ~/.config/xlite-daemon/
+```
+
+### Monitoring and Logging
+
+#### Log Rotation
+
+Configure automatic log rotation:
+
+```bash
+# Create logrotate configuration
+sudo nano /etc/logrotate.d/xlite-daemon
+
+# Add configuration
+/home/user/.config/xlite-daemon/error-*.log {
+ daily
+ rotate 30
+ compress
+ delaycompress
+ missingok
+ notifempty
+}
+```
+
+#### Health Monitoring
+
+Monitor daemon health:
+
+```bash
+# Check process status
+ps aux | grep xlite-daemon
+
+# Monitor logs in real-time
+tail -f ~/.config/xlite-daemon/error-*.log
+
+# Check network connections
+netstat -tulpn | grep xlite-daemon
+```
+
+### Backup and Recovery
+
+#### Automated Backups
+
+Create automated backup script:
+
+```bash
+#!/bin/bash
+# backup-wallet.sh
+
+BACKUP_DIR="/backup/xlite-daemon"
+DATE=$(date +%Y%m%d_%H%M%S)
+
+mkdir -p "$BACKUP_DIR"
+
+# Backup wallet file
+cp ~/.config/xlite-daemon/key.dat "$BACKUP_DIR/key-$DATE.dat"
+
+# Backup configuration
+cp ~/.config/xlite-daemon/settings/config-*.json "$BACKUP_DIR/"
+
+# Compress backup
+tar -czf "$BACKUP_DIR/backup-$DATE.tar.gz" -C "$BACKUP_DIR" .
+
+# Clean old backups (keep 30 days)
+find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
+
+echo "Backup completed: $BACKUP_DIR/backup-$DATE.tar.gz"
+```
+
+#### Recovery Script
+
+Create recovery script:
+
+```bash
+#!/bin/bash
+# restore-wallet.sh
+
+BACKUP_FILE="$1"
+
+if [ -z "$BACKUP_FILE" ]; then
+ echo "Usage: $0 "
+ exit 1
+fi
+
+# Stop daemon
+pkill xlite-daemon
+
+# Backup current data
+mv ~/.config/xlite-daemon ~/.config/xlite-daemon.backup
+
+# Extract backup
+tar -xzf "$BACKUP_FILE" -C ~/.config/
+
+echo "Wallet restored from $BACKUP_FILE"
+echo "Start the daemon to verify recovery"
+```
+
+This comprehensive user guide provides everything needed to install, configure, and use the XLite Daemon effectively. For additional support, refer to the troubleshooting section or consult the API reference for detailed technical information.
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
deleted file mode 100644
index d7377e6..0000000
--- a/gradle.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-# Gradle performance improvements
-org.gradle.parallel=true
-org.gradle.caching=true
-org.gradle.configureondemand=true
-org.gradle.daemon=true
-org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+UseG1GC
-
-# Java compatibility
-kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 41d9927..0000000
Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index cd4b7aa..0000000
--- a/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
-networkTimeout=10000
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
deleted file mode 100755
index 1b6c787..0000000
--- a/gradlew
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-##############################################################################
-#
-# Gradle start up script for POSIX generated by Gradle.
-#
-# Important for running:
-#
-# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-# noncompliant, but you have some other compliant shell such as ksh or
-# bash, then to run this script, type that shell name before the whole
-# command line, like:
-#
-# ksh Gradle
-#
-# Busybox and similar reduced shells will NOT work, because this script
-# requires all of these POSIX shell features:
-# * functions;
-# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-# * compound commands having a testable exit status, especially «case»;
-# * various built-in commands including «command», «set», and «ulimit».
-#
-# Important for patching:
-#
-# (2) This script targets any POSIX shell, so it avoids extensions provided
-# by Bash, Ksh, etc; in particular arrays are avoided.
-#
-# The "traditional" practice of packing multiple parameters into a
-# space-separated string is a well documented source of bugs and security
-# problems, so this is (mostly) avoided, by progressively accumulating
-# options in "$@", and eventually passing that to Java.
-#
-# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-# see the in-line comments for details.
-#
-# There are tweaks for specific operating systems such as AIX, CygWin,
-# Darwin, MinGW, and NonStop.
-#
-# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-# within the Gradle project.
-#
-# You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
- APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
- [ -h "$app_path" ]
-do
- ls=$( ls -ld "$app_path" )
- link=${ls#*' -> '}
- case $link in #(
- /*) app_path=$link ;; #(
- *) app_path=$APP_HOME$link ;;
- esac
-done
-
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
-APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
- echo "$*"
-} >&2
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in #(
- CYGWIN* ) cygwin=true ;; #(
- Darwin* ) darwin=true ;; #(
- MSYS* | MINGW* ) msys=true ;; #(
- NONSTOP* ) nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD=$JAVA_HOME/jre/sh/java
- else
- JAVACMD=$JAVA_HOME/bin/java
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
- case $MAX_FD in #(
- max*)
- MAX_FD=$( ulimit -H -n ) ||
- warn "Could not query maximum file descriptor limit"
- esac
- case $MAX_FD in #(
- '' | soft) :;; #(
- *)
- ulimit -n "$MAX_FD" ||
- warn "Could not set maximum file descriptor limit to $MAX_FD"
- esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-# * args from the command line
-# * the main class name
-# * -classpath
-# * -D...appname settings
-# * --module-path (only if needed)
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
- APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
- JAVACMD=$( cygpath --unix "$JAVACMD" )
-
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- for arg do
- if
- case $arg in #(
- -*) false ;; # don't mess with options #(
- /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
- [ -e "$t" ] ;; #(
- *) false ;;
- esac
- then
- arg=$( cygpath --path --ignore --mixed "$arg" )
- fi
- # Roll the args list around exactly as many times as the number of
- # args, so each arg winds up back in the position where it started, but
- # possibly modified.
- #
- # NB: a `for` loop captures its iteration list before it begins, so
- # changing the positional parameters here affects neither the number of
- # iterations, nor the values presented in `arg`.
- shift # remove old arg
- set -- "$@" "$arg" # push replacement arg
- done
-fi
-
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
-
-set -- \
- "-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
- "$@"
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-# set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
- printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
- xargs -n1 |
- sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
- tr '\n' ' '
- )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
deleted file mode 100644
index 107acd3..0000000
--- a/gradlew.bat
+++ /dev/null
@@ -1,89 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..bd8896b
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,295 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.4
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+scriptDir="$(dirname "$0")"
+scriptName="$(basename "$0")"
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+actualDistributionDir=""
+
+# First try the expected directory name (for regular distributions)
+if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
+ if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$distributionUrlNameMain"
+ fi
+fi
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if [ -z "$actualDistributionDir" ]; then
+ # enable globbing to iterate over items
+ set +f
+ for dir in "$TMP_DOWNLOAD_DIR"/*; do
+ if [ -d "$dir" ]; then
+ if [ -f "$dir/bin/$MVN_CMD" ]; then
+ actualDistributionDir="$(basename "$dir")"
+ break
+ fi
+ fi
+ done
+ set -f
+fi
+
+if [ -z "$actualDistributionDir" ]; then
+ verbose "Contents of $TMP_DOWNLOAD_DIR:"
+ verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
+ die "Could not find Maven distribution directory in extracted archive"
+fi
+
+verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..5761d94
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,189 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.4
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+
+$MAVEN_M2_PATH = "$HOME/.m2"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
+}
+
+if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
+ New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
+}
+
+$MAVEN_WRAPPER_DISTS = $null
+if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
+ $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
+} else {
+ $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
+}
+
+$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+
+# Find the actual extracted directory name (handles snapshots where filename != directory name)
+$actualDistributionDir = ""
+
+# First try the expected directory name (for regular distributions)
+$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
+$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
+if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
+ $actualDistributionDir = $distributionUrlNameMain
+}
+
+# If not found, search for any directory with the Maven executable (for snapshots)
+if (!$actualDistributionDir) {
+ Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
+ $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
+ if (Test-Path -Path $testPath -PathType Leaf) {
+ $actualDistributionDir = $_.Name
+ }
+ }
+}
+
+if (!$actualDistributionDir) {
+ Write-Error "Could not find Maven distribution directory in extracted archive"
+}
+
+Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/nativeImageWindows.bat b/nativeImageWindows.bat
index c2cb081..936f3f9 100644
--- a/nativeImageWindows.bat
+++ b/nativeImageWindows.bat
@@ -135,10 +135,23 @@ SET "PATH=%SDK_BIN_PATH%;%PATH%"
SET "INCLUDE=%SDK_INCLUDE_PATH%;%INCLUDE%"
SET "LIB=%SDK_LIB_PATH%;%LIB%"
-echo Building XLite Daemon with nativeCompile...
+echo Building XLite Daemon with Maven native profile...
+
+REM Check if Maven wrapper exists, if not use mvn directly
+if exist mvnw.cmd (
+ echo Using Maven wrapper (mvnw.cmd)...
+ set "MAVEN_CMD=mvnw.cmd"
+) else if exist mvnw.bat (
+ echo Using Maven wrapper (mvnw.bat)...
+ set "MAVEN_CMD=mvnw.bat"
+) else (
+ echo Maven wrapper not found, using system mvn command...
+ set "MAVEN_CMD=mvn"
+)
-REM Build the project using Gradle nativeCompile task
-gradlew.bat clean nativeCompile --info
+REM Build the project using Maven with native profile
+echo Running: %MAVEN_CMD% clean compile exec:java -Pnative -DskipTests
+%MAVEN_CMD% clean compile exec:java -Pnative -DskipTests
if %ERRORLEVEL% NEQ 0 (
echo Build failed!
@@ -146,7 +159,7 @@ if %ERRORLEVEL% NEQ 0 (
)
REM Additional check for build output
-if not exist build\native\nativeCompile\xlite-daemon.exe (
+if not exist target\xlite-daemon.exe (
echo Build completed but native image not found at expected location
exit /b 1
)
@@ -154,9 +167,9 @@ if not exist build\native\nativeCompile\xlite-daemon.exe (
echo Build completed successfully!
REM Rename the output file
-if exist build\native\nativeCompile\xlite-daemon.exe (
- ren build\native\nativeCompile\xlite-daemon.exe xlite-daemon-win64.exe
- echo Native image created: build\native\nativeCompile\xlite-daemon-win64.exe
+if exist target\xlite-daemon.exe (
+ ren target\xlite-daemon.exe xlite-daemon-win64.exe
+ echo Native image created: target\xlite-daemon-win64.exe
) else (
echo Error: Native image not found at expected location
exit /b 1
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..9d51d0c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,528 @@
+
+
+ 4.0.0
+
+
+ io.xlite.daemon
+ xlite-daemon
+ 0.5.15
+ jar
+
+ XLite Daemon
+ XLite Daemon - Multi-cryptocurrency wallet daemon
+ https://github.com/blocknetdx/xlite-daemon
+
+
+
+ UTF-8
+ 21
+ 21
+ 21
+ 21
+
+
+ compatibility
+
+
+ 0.15.10
+ 4.2.7.Final
+ 5.11.3
+ 2.13.2
+
+
+ 0.11.3
+ 3.13.0
+ 3.6.0
+ 3.5.0
+ 6.25.0
+
+
+
+
+
+
+
+ org.junit
+ junit-bom
+ ${junit.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.bitcoinj
+ bitcoinj-core
+ ${bitcoinj.version}
+
+
+ commons-logging
+ commons-logging
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+
+
+ com.google.guava
+ guava
+ 28.2-android
+
+
+
+
+ org.bitcoinj
+ orchid
+ 1.2.1
+
+
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.14
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.36
+
+
+
+
+ org.slf4j
+ slf4j-nop
+ 1.7.36
+
+
+
+
+ org.json
+ json
+ 20250517
+
+
+
+
+ io.github.cdimascio
+ java-dotenv
+ 5.2.2
+
+
+
+
+ io.netty
+ netty-common
+ ${netty.version}
+
+
+ io.netty
+ netty-buffer
+ ${netty.version}
+
+
+ io.netty
+ netty-transport
+ ${netty.version}
+
+
+ io.netty
+ netty-handler
+ ${netty.version}
+
+
+ io.netty
+ netty-codec-http
+ ${netty.version}
+
+
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.15.2
+ test
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.5.2
+
+
+ **/*Test*.java
+
+ -javaagent:${settings.localRepository}/net/bytebuddy/byte-buddy-agent/1.15.11/byte-buddy-agent-1.15.11.jar -Djava.util.logging.level=WARNING
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.plugin.version}
+
+ ${java.version}
+ ${java.version}
+ ${java.version}
+
+ -Xlint:unchecked
+ -Xlint:deprecation
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.5.0
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+ 21
+
+
+ 3.8.6
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ ${maven.shade.plugin.version}
+
+
+
+ shade
+
+ package
+
+ true
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+ META-INF/DEPENDENCIES
+ META-INF/LICENSE.txt
+ META-INF/NOTICE.txt
+ META-INF/MANIFEST.MF
+
+ META-INF/versions/*/module-info.class
+ META-INF/versions/9/module-info
+ META-INF/versions/11/module-info
+
+ META-INF/io.netty.versions.properties
+
+
+
+
+
+ simplelogger.properties
+
+
+
+
+
+
+
+ org.bitcoinj:bitcoinj-core
+ org.bouncycastle:bcprov-jdk15to18
+ com.google.protobuf:protobuf-java
+ com.google.guava:guava
+ net.jcip:jcip-annotations
+ com.lambdaworks:scrypt
+ com.squareup.okhttp:okhttp
+ com.squareup.okio:okio
+ org.bitcoinj:orchid
+ com.google.code.gson:gson
+ com.google.errorprone:error_prone_annotations
+ org.apache.httpcomponents:httpclient
+ commons-logging:commons-logging
+ commons-codec:commons-codec
+ org.json:json
+ io.netty:netty-common
+ io.netty:netty-buffer
+ io.netty:netty-transport
+ io.netty:netty-resolver
+ io.netty:netty-handler
+ io.netty:netty-transport-native-unix-common
+ io.netty:netty-codec-base
+ io.netty:netty-codec-http
+ io.netty:netty-codec-compression
+ com.google.code.findbugs:jsr305
+ io.github.cdimascio:java-dotenv
+
+
+
+ org.apache.httpcomponents:httpcore
+ org.slf4j:slf4j-api
+
+
+
+
+
+
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${native.maven.plugin.version}
+
+
+ io.xlite.daemon.app.App
+
+
+ xlite-daemon
+
+
+ false
+ true
+
+
+
+ --enable-url-protocols=http,https
+ --enable-native-access=ALL-UNNAMED
+ --strict-image-heap
+ -march=${native.march}
+ -H:+ReportExceptionStackTraces
+
+
+ --initialize-at-run-time=io.netty
+ --initialize-at-run-time=io.netty.util.internal
+ --initialize-at-run-time=io.netty.channel
+ --initialize-at-run-time=io.netty.channel.nio
+ --initialize-at-run-time=io.netty.buffer
+ --initialize-at-run-time=io.netty.handler
+ --initialize-at-run-time=io.netty.bootstrap
+ --initialize-at-run-time=org.apache.httpcomponents
+ --initialize-at-run-time=org.json
+ --initialize-at-run-time=org.slf4j
+ --initialize-at-run-time=org.slf4j.impl
+
+ --initialize-at-run-time=io.xlite.daemon.app.App
+ --initialize-at-run-time=io.xlite.daemon.app.net.api.JSONRPCController
+
+
+ --initialize-at-build-time=com.google.common.base.Charsets
+ --initialize-at-build-time=com.google.common.base.CharMatcher
+ --initialize-at-build-time=com.google.common.base.CharMatcher$8
+ --initialize-at-build-time=com.google.common.math.IntMath
+ --initialize-at-build-time=com.google.common.base.StandardCharsets
+ --initialize-at-build-time=com.google.common.base.Joiner
+ --initialize-at-build-time=com.google.common.io.BaseEncoding
+ --initialize-at-build-time=com.google.common.io.BaseEncoding$StandardBaseEncoding
+ --initialize-at-build-time=com.google.common.io.BaseEncoding$Alphabet
+ --initialize-at-build-time=com.google.common.io.BaseEncoding$Base16Encoding
+ --initialize-at-build-time=org.apache.commons.logging
+ --initialize-at-build-time=org.slf4j.helpers.NOPLogger
+ --initialize-at-build-time=org.bitcoinj.core.Utils
+ --initialize-at-build-time=org.bitcoinj.core.Utils$Runtime
+ --initialize-at-build-time=org.bitcoinj.core.Sha256Hash
+ --initialize-at-build-time=org.bitcoinj.crypto.MnemonicCode
+ --initialize-at-build-time=io.xlite.daemon.app.util
+ --initialize-at-build-time=io.xlite.daemon.app.crypto
+ --initialize-at-build-time=io.xlite.daemon.app.console
+ --initialize-at-build-time=io.xlite.daemon.app.net.api
+ --initialize-at-build-time=io.xlite.daemon.app.net.protocols
+ --initialize-at-build-time=io.xlite.daemon.app.net.xrouter
+ --initialize-at-build-time=io.xlite.daemon.app.net.api.http
+
+
+
+ --report-unsupported-elements-at-runtime
+
+
+
+
+ build-native
+
+ compile-no-fork
+
+ package
+
+
+
+
+
+
+ org.openrewrite.maven
+ rewrite-maven-plugin
+ ${rewrite.maven.plugin.version}
+
+
+
+
+ org.openrewrite.maven.cleanup.DependencyManagementDependencyRequiresVersion
+ org.openrewrite.maven.cleanup.ExplicitPluginGroupId
+ org.openrewrite.maven.cleanup.ExplicitPluginVersion
+ org.openrewrite.maven.cleanup.PrefixlessExpressions
+ org.openrewrite.maven.RemoveDuplicateDependencies
+ org.openrewrite.maven.RemoveDuplicatePluginDeclarations
+ org.openrewrite.maven.RemoveRedundantDependencyVersions
+ org.openrewrite.maven.RemoveRedundantProperties
+ org.openrewrite.maven.RemoveUnusedProperties
+ org.openrewrite.maven.OrderPomElements
+ org.openrewrite.maven.ModernizeObsoletePoms
+ org.openrewrite.maven.BestPractices
+
+
+ org.openrewrite.java.RemoveUnusedImports
+ org.openrewrite.java.format.AutoFormat
+ org.openrewrite.java.OrderImports
+ org.openrewrite.java.format.RemoveTrailingWhitespace
+ org.openrewrite.java.RemoveObjectsIsNull
+ org.openrewrite.java.ShortenFullyQualifiedTypeReferences
+ org.openrewrite.java.SimplifySingleElementAnnotation
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec.maven.plugin.version}
+
+ io.xlite.daemon.app.App
+ true
+
+
+ java.util.logging.config.file
+ logging.properties
+
+
+
+
+
+
+
+
+
+
+
+ native
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ 0.11.3
+
+ false
+
+
+
+
+
+
+
+
+ native-fast
+
+ true
+
+
+
+
+
+ rewrite
+
+
+
+ org.openrewrite.maven
+ rewrite-maven-plugin
+ ${rewrite.maven.plugin.version}
+
+
+
+ cleanup-code
+ process-classes
+
+ run
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/io/cloudchains/app/App.java b/src/main/java/io/cloudchains/app/App.java
deleted file mode 100644
index 6bc7315..0000000
--- a/src/main/java/io/cloudchains/app/App.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package io.cloudchains.app;
-
-import io.cloudchains.app.console.*;
-import io.cloudchains.app.net.api.JSONRPCController;
-import io.cloudchains.app.net.api.JSONRPCMasterServer;
-import io.cloudchains.app.net.api.http.client.HTTPClient;
-import io.cloudchains.app.net.api.http.client.EXRWrapper;
-import io.cloudchains.app.net.api.http.client.EXRServerPool;
-import io.cloudchains.app.util.CCLogger;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.logging.*;
-
-public class App {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private static final boolean isLoggingEnabled = false;
- // DEBUG ENDPOINT
- public static String BASE_URL = "https://xliterevp.mywire.org/";
- // "http://xl-dae-prox.airdns.org:42111/";
- // DEBUG ENDPOINT
- public static String EXR_ENDPOINT = null;
- public static EXRServerPool exrServerPool = null;
- public static HTTPClient feeUpdateHttpClient = new HTTPClient(2);
- public static HTTPClient heightUpdateHttpClient = new HTTPClient(2);
- public static JSONRPCMasterServer masterRPC = JSONRPCController.getMasterServer();
- public static ConsoleMenu console = null;
-
- static {
- // Check for EXR_ENDPOINT environment variable
- String exrEndpoint = System.getenv("EXR_ENDPOINT");
- if (exrEndpoint != null && !exrEndpoint.isEmpty()) {
- EXR_ENDPOINT = exrEndpoint;
- exrServerPool = new EXRServerPool(EXR_ENDPOINT);
- LOGGER.log(Level.INFO, "[app] EXR mode enabled with " + exrServerPool.getServerCount() + " servers: " + EXR_ENDPOINT);
- }
- }
-
- public static void main(String[] args) {
- CCLogger.setLogging(isLoggingEnabled);
- LOGGER.setLevel(Level.INFO);
- LOGGER.setUseParentHandlers(false);
-
- Runtime.getRuntime().addShutdownHook(new Thread(App::shutdown));
-
- try {
- String userHomeDir;
- String OS = (System.getProperty("os.name")).toLowerCase();
-
- if (OS.contains("win")) {
- userHomeDir = System.getenv("AppData");
- } else if (OS.contains("nix") || OS.contains("nux") || OS.contains("aix")) {
- userHomeDir = System.getProperty("user.home") + File.separator + ".config";
- } else if (OS.contains("mac")) {
- userHomeDir = System.getProperty("user.home") + File.separator + "Library" + File.separator + "Application Support";
- } else {
- userHomeDir = System.getProperty("user.home") + File.separator + ".config";
- }
-
- DateTimeFormatter timeStampPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- Handler fileHandler = new FileHandler(
- userHomeDir +
- File.separator +
- "CloudChains" +
- File.separator +
- "error-" +
- timeStampPattern.format(java.time.LocalDateTime.now()) +
- ".log",
- true
- );
-
- fileHandler.setFormatter(new SimpleFormatter() {
- private static final String format = "[%1$tF %1$tT] [%2$-7s] %3$s %n";
-
- @Override
- public synchronized String format(LogRecord lr) {
- return String.format(format,
- new Date(lr.getMillis()),
- lr.getLevel().getLocalizedName(),
- lr.getMessage()
- );
- }
- });
- fileHandler.setLevel(Level.INFO);
-
- LOGGER.addHandler(fileHandler);
-
- } catch (IOException e) {
- // TODO Auto-generated catch block
- }
-
- ConsoleHandler consoleHandler = new ConsoleHandler (){
- @Override
- protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
- super.setOutputStream(System.out);
- }
- };
- consoleHandler.setLevel(Level.FINE);
-
- LOGGER.addHandler(consoleHandler);
-
- console = new ConsoleMenu(args);
- console.init();
- }
-
- public static void shutdown() {
- if (masterRPC.isAlive()) {
- System.out.println("Shutting down...");
- }
-
- if (feeUpdateHttpClient != null) {
- feeUpdateHttpClient.close();
- }
-
- if (heightUpdateHttpClient != null) {
- heightUpdateHttpClient.close();
- }
-
- if (exrServerPool != null) {
- exrServerPool.close();
- }
-
- if (masterRPC != null) {
- masterRPC.deinit();
- }
-
- if (console != null) {
- console.deinit();
- }
-
- for (Handler handler : LOGGER.getHandlers()) {
- LOGGER.removeHandler(handler);
- handler.close();
- }
- }
-}
diff --git a/src/main/java/io/cloudchains/app/console/ArgMenu.java b/src/main/java/io/cloudchains/app/console/ArgMenu.java
deleted file mode 100644
index 3657a2e..0000000
--- a/src/main/java/io/cloudchains/app/console/ArgMenu.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package io.cloudchains.app.console;
-
-import io.cloudchains.app.crypto.KeyHandler;
-import io.cloudchains.app.crypto.LoginUtils;
-import io.cloudchains.app.net.CoinInstance;
-import io.cloudchains.app.net.CoinTicker;
-
-public class ArgMenu {
- private String[] arguments;
-
- public ArgMenu(String[] args) {
- this.arguments = args;
- }
-
- public void init() {
- int selection = 2;
- String password = "";
-
- System.out.println("-------------------------");
- System.out.println("Help: ");
- System.out.println("Create new wallet: --new-wallet (password)");
- System.out.println("Decrypt wallet: --decrypt-wallet (password)");
-
- if (arguments.length == 0) {
- System.out.println("No arguments given.");
- System.exit(0);
- } else {
- if (arguments.length == 1) {
- password = arguments[0];
- } else if (arguments.length == 2 && arguments[0].equals("--new-wallet")) {
- selection = 1;
- password = arguments[1];
- } else if (arguments.length == 2 && arguments[0].equals("--decrypt-wallet")) {
- password = arguments[1];
- }
- }
-
- String entropy = null;
-
- switch (selection) {
- case 1: {
- if (KeyHandler.existsBaseECKeyFromLocal()) {
- System.out.println("Key already exists");
- return;
- }
-
- int strength = KeyHandler.calculatePasswordStrength(password);
-
- if (!KeyHandler.existsBaseECKeyFromLocal() && strength < 9) {
- System.out.println("Bad password.");
- return;
- }
-
- entropy = LoginUtils.loginToEntropy(password);
- break;
- }
- case 2: {
- int strength = KeyHandler.calculatePasswordStrength(password);
-
- if (!KeyHandler.existsBaseECKeyFromLocal() && strength < 9) {
- System.out.println("Bad password.");
- return;
- }
-
- entropy = LoginUtils.loginToEntropy(password);
- break;
- }
- case 3: {
- System.out.println("Exiting...");
- System.exit(0);
- }
- break;
- default:
- throw new IllegalStateException("Unexpected value: " + selection);
- }
-
- if (entropy == null)
- return;
-
- completeLogin(entropy, null);
- }
-
- private void completeLogin(String entropy, String userMnemonic) {
- CoinInstance.CoinError coinError = CoinInstance.getInstance(CoinTicker.BLOCKNET).init(entropy, userMnemonic, false);
- if (coinError != null) {
- System.out.println("[master] Error(" + coinError.getCode().name() + "): " + coinError.getMessage());
- System.exit(0);
- }
-
- for (CoinTicker cointicker : CoinTicker.coins()) {
- if (cointicker == CoinTicker.BLOCKNET || cointicker == CoinTicker.BLOCKNET_TESTNET5 || cointicker == CoinTicker.BITCOIN)
- continue;
- coinError = CoinInstance.getInstance(cointicker).init(entropy, userMnemonic, false);
- if (coinError != null)
- System.out.println("[" + cointicker.name() + "] Error(" + coinError.getCode().name() + "): " + coinError.getMessage());
- }
- }
-}
diff --git a/src/main/java/io/cloudchains/app/console/ConsoleMenu.java b/src/main/java/io/cloudchains/app/console/ConsoleMenu.java
deleted file mode 100644
index 2b33c31..0000000
--- a/src/main/java/io/cloudchains/app/console/ConsoleMenu.java
+++ /dev/null
@@ -1,409 +0,0 @@
-package io.cloudchains.app.console;
-
-import io.cloudchains.app.App;
-import io.cloudchains.app.Version;
-import io.cloudchains.app.crypto.KeyHandler;
-import io.cloudchains.app.crypto.LoginUtils;
-import io.cloudchains.app.net.CoinInstance;
-import io.cloudchains.app.net.CoinTicker;
-import io.cloudchains.app.net.CoinTickerUtils;
-import io.cloudchains.app.net.api.http.client.EXRWrapper;
-import io.cloudchains.app.net.api.http.client.EXRServerPool;
-import io.cloudchains.app.net.protocols.blocknet.BlocknetNetworkParameters;
-import io.cloudchains.app.util.ConfigHelper;
-import io.cloudchains.app.util.background.BackgroundTimerThread;
-import org.bitcoinj.core.Context;
-
-import java.security.SecureRandom;
-import java.util.Base64;
-import java.util.Scanner;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class ConsoleMenu {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
- private String[] arguments;
- private BackgroundTimerThread backgroundTimerThread = null;
- private boolean xliteRPC = false;
-
- public ConsoleMenu(String[] args) {
- this.arguments = args;
- LOGGER.setLevel(Level.INFO);
- }
-
- public void logBadPassword(String msg) {
- if (msg == null || msg.isEmpty())
- msg = "Bad password";
- LOGGER.log(Level.INFO, "[master] Error(" + CoinInstance.CoinError.CoinErrorCode.BADPASSWORD.name() + "): " + msg);
- }
-
- public void logBadMnemonic() {
- LOGGER.log(Level.INFO, "[master] Error(" + CoinInstance.CoinError.CoinErrorCode.BADMNEMONIC.name() + "): Bad mnemonic");
- }
-
- public void logBadChangePass(String msg) {
- LOGGER.log(Level.INFO, "[master] Error(" + CoinInstance.CoinError.CoinErrorCode.CHANGEPASSWORDFAILED.name() + "): " + msg);
- }
-
- public void init() {
- if (System.getenv("WALLET_MNEMONIC") != null) {
- String mnemonicImport = System.getenv("WALLET_MNEMONIC");
- if (mnemonicImport == null) {
- LOGGER.log(Level.INFO, "Bad mnemonic.");
- return;
- }
-
- completeLogin(mnemonicImport, null, true);
- return;
- } else if (System.getenv("WALLET_PASSWORD") != null) {
- String password = System.getenv("WALLET_PASSWORD");
- if (password == null) {
- LOGGER.log(Level.INFO, "Bad password.");
- return;
- }
-
- int strength = KeyHandler.calculatePasswordStrength(password);
-
- if (!KeyHandler.existsBaseECKeyFromLocal() && strength < 9) {
- LOGGER.log(Level.INFO, "Bad password.");
- return;
- }
-
- completeLogin(LoginUtils.loginToEntropy(password), null, false);
- }
-
- int selection;
- String newWalletStr = "";
- Scanner input = new Scanner(System.in);
-
- if (KeyHandler.existsBaseECKeyFromLocal()) {
- newWalletStr = "- Disabled. Wallet already exists.";
- }
-
- if (arguments.length > 0) {
- for (int i = 0; i < arguments.length; i++) {
- String argument = arguments[i];
-
- switch (argument) {
- case "--enablerpcandconfigure":
- autoGenerateRPCConfig();
- break;
- case "--development-endpoint": {
- // sample endpoint url: "https://utils.blocknet.org/"
- if (i + 1 < arguments.length) {
- // Check if there is another argument after "--development-endpoint"
- String customEndpoint = arguments[i + 1];
- App.BASE_URL = customEndpoint;
- i++; // Increment i to skip the next argument (custom endpoint)
- } else {
- LOGGER.log(Level.WARNING, "Missing custom endpoint after '--development-endpoint'");
- }
- break;
- }
- case "--exr-endpoint": {
- if (i + 1 < arguments.length) {
- // Check if there is another argument after "--exr-endpoint"
- String exrEndpoint = arguments[i + 1];
- App.EXR_ENDPOINT = exrEndpoint;
- App.exrServerPool = new EXRServerPool(App.EXR_ENDPOINT);
- LOGGER.log(Level.INFO, "[console] EXR mode enabled with " + App.exrServerPool.getServerCount() + " servers: " + App.EXR_ENDPOINT);
- // Start capability probing in background
- new Thread(() -> {
- try {
- Thread.sleep(1000); // Wait 1 second before starting probe
- App.exrServerPool.probeAllCapabilities();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }, "EXR-Capability-Prober").start();
- i++; // Increment i to skip the next argument (EXR endpoint)
- } else {
- LOGGER.log(Level.WARNING, "Missing EXR endpoint after '--exr-endpoint'");
- }
- break;
- }
- case "--version":
- LOGGER.log(Level.INFO, Version.CLIENT_VERSION);
- return;
- case "--createdefaultwallet": {
- if (KeyHandler.existsBaseECKeyFromLocal()) {
- LOGGER.log(Level.INFO, "Wallet already exists");
- return;
- }
-
- String password = readPassword(input, arguments, i + 1, "");
- int strength = KeyHandler.calculatePasswordStrength(password);
- if (strength < 9) {
- logBadPassword(null);
- return;
- }
-
- String entropy = LoginUtils.loginToEntropy(password);
- completeLogin(entropy, null, false);
-
- return;
- }
- case "--createwalletmnemonic": {
- if (KeyHandler.existsBaseECKeyFromLocal()) {
- LOGGER.log(Level.INFO, "Wallet already exists");
- return;
- }
-
- String password = readPassword(input, arguments, i + 1, "");
- String mnemonic = readPassword(input, arguments, i + 2, "Mnemonic:\n").trim();
- int strength = KeyHandler.calculatePasswordStrength(password);
- if (strength < 9) {
- logBadPassword(null);
- return;
- }
- if (mnemonic.isEmpty()) {
- logBadMnemonic();
- return;
- }
-
- String entropy = LoginUtils.loginToEntropy(password);
- completeLogin(entropy, mnemonic, false);
- return;
- }
- case "--xliterpc": {
- // Increment RPC port by 1
- xliteRPC = true;
-
- break;
- }
- case "--password": {
- // If password is provided as an arg then use it, otherwise ask for
- // password via stdin. Don't mistake another cmd option as the
- // password.
- String password = readPassword(input, arguments, i + 1, "");
- int strength = KeyHandler.calculatePasswordStrength(password);
-
- if (!KeyHandler.existsBaseECKeyFromLocal() && strength < 9) {
- LOGGER.log(Level.INFO, "Bad password.");
- return;
- }
-
- String entropy = LoginUtils.loginToEntropy(password);
- completeLogin(entropy, null, false);
-
- return;
- }
- case "--getmnemonic": {
- String password = readPassword(input, arguments, i + 1, "");
-
- if (!KeyHandler.existsBaseECKeyFromLocal()) {
- LOGGER.log(Level.INFO, "No wallet found.");
- return;
- }
-
- String entropy = LoginUtils.loginToEntropy(password);
- String mnemonic = CoinInstance.getMnemonicForPw(entropy);
- System.out.println(mnemonic);
- return;
- }
- case "--changepassword": {
- if (!KeyHandler.existsBaseECKeyFromLocal()) {
- logBadChangePass("Wallet not found");
- return;
- }
-
- String currentPassword = readPassword(input, arguments, i + 1, "");
- String newPassword = readPassword(input, arguments, i + 2, "");
- if (currentPassword.isEmpty() || newPassword.isEmpty()) {
- LOGGER.log(Level.INFO, "Password cannot be empty");
- return;
- }
- if (currentPassword.equals(newPassword)) {
- LOGGER.log(Level.INFO, "New password must be different from old password");
- return;
- }
-
- // Check new password strength
- int strength = KeyHandler.calculatePasswordStrength(newPassword);
- if (strength < 9) {
- LOGGER.log(Level.INFO, "Unable to change the password: New password is not strong enough");
- return;
- }
-
- CoinInstance.CoinError err = CoinInstance.changePassword(LoginUtils.loginToEntropy(currentPassword),
- LoginUtils.loginToEntropy(newPassword));
- if (err != null)
- logBadChangePass(err.getMessage());
- else
- LOGGER.log(Level.INFO, "Wallet password changed successfully");
-
- return;
- }
- case "--help":
- displayHelp();
- return; // Exit after displaying help
- }
- }
- }
-
- String entropy = null;
-
- while (entropy == null) {
- LOGGER.log(Level.INFO, "-------------------------");
- LOGGER.log(Level.INFO, "1 - Create new wallet " + newWalletStr);
- LOGGER.log(Level.INFO, "2 - Decrypt wallet");
- LOGGER.log(Level.INFO, "3 - Import from mnemonic");
- LOGGER.log(Level.INFO, "4 - Quit");
-
- LOGGER.log(Level.INFO, "Selection: ");
- selection = input.nextInt();
- input.nextLine(); // clear buffer
-
- switch (selection) {
- case 1: {
- if (KeyHandler.existsBaseECKeyFromLocal()) {
- LOGGER.log(Level.INFO, "Key already exists");
- return;
- }
-
- LOGGER.log(Level.INFO, "Enter new password: ");
- String password = input.next();
- int strength = KeyHandler.calculatePasswordStrength(password);
-
- if (!KeyHandler.existsBaseECKeyFromLocal() && strength < 9) {
- LOGGER.log(Level.INFO, "Bad password.");
- return;
- }
- entropy = LoginUtils.loginToEntropy(password);
- break;
- }
- case 2: {
- LOGGER.log(Level.INFO, "Enter password: ");
- String password = new String(System.console().readPassword());
- int strength = KeyHandler.calculatePasswordStrength(password);
-
- if (!KeyHandler.existsBaseECKeyFromLocal() && strength < 9) {
- LOGGER.log(Level.INFO, "Bad password.");
- return;
- }
-
- entropy = LoginUtils.loginToEntropy(password);
- break;
- }
- case 3: {
- LOGGER.log(Level.INFO, "Enter mnemonic: ");
- String mnemonicImport = input.nextLine().trim();
-
- completeLogin(mnemonicImport, null, true);
- return;
- }
- case 4: {
- LOGGER.log(Level.INFO, "Exiting...");
- System.exit(0);
- }
- default: {
- LOGGER.log(Level.INFO, "Unknown Option.");
- }
- }
- }
-
- input.close();
- completeLogin(entropy, null, false);
- }
-
- public void deinit() {
- if (backgroundTimerThread != null)
- backgroundTimerThread.stop();
- for (CoinTicker cointicker : CoinTicker.coins())
- CoinInstance.getInstance(cointicker).deinit();
- }
-
- private void completeLogin(String entropy, String userMnemonic, boolean isMnemonic) {
- if (entropy == null && userMnemonic == null) {
- logBadPassword(null);
- System.exit(0);
- }
-
- CoinInstance.CoinError coinError = CoinInstance.getInstance(CoinTicker.BLOCKNET).init(entropy, userMnemonic, isMnemonic, xliteRPC);
- if (coinError != null) {
- String msg = "[master] Error(" + coinError.getCode().name() + "): " + coinError.getMessage();
- LOGGER.log(Level.SEVERE, msg);
- System.out.println(msg);
- System.exit(0);
- }
-
- for (CoinTicker cointicker : CoinTicker.coins()) {
- if (cointicker == CoinTicker.BLOCKNET || cointicker == CoinTicker.BLOCKNET_TESTNET5)
- continue;
- coinError = CoinInstance.getInstance(cointicker).init(entropy, userMnemonic, isMnemonic, xliteRPC);
- if (coinError != null) // fail silently
- LOGGER.log(Level.WARNING, "[" + cointicker.name() + "] Error(" + coinError.getCode().name() + "): " + coinError.getMessage());
- }
-
- App.masterRPC.start();
- backgroundTimerThread = new BackgroundTimerThread();
- (new Thread(backgroundTimerThread)).start();
-
- // Start EXR capability probing after wallet is decrypted
- if (App.exrServerPool != null) {
- App.exrServerPool.probeAllCapabilities();
- }
- }
-
- private void autoGenerateRPCConfig() {
- for (CoinTicker cointicker : CoinTicker.coins()) {
- ConfigHelper configHelper = new ConfigHelper(CoinTickerUtils.tickerToString(cointicker));
-
- configHelper.setRpcUsername(generateRandomString(24));
- configHelper.setRpcPassword(generateRandomString(32));
- configHelper.setRpcEnabled(true);
- configHelper.writeConfig();
- }
- }
-
- private String generateRandomString(int length) {
- SecureRandom secureRandom = new SecureRandom();
-
- byte[] token = new byte[length];
- secureRandom.nextBytes(token);
-
- return Base64.getUrlEncoder().withoutPadding().encodeToString(token);
- }
-
- /**
- * Reads the password from args or from stdin if password is not specified.
- * @param input Stdin
- * @param args Program arguments
- * @param argPos Current arg position
- * @param msg Message to display on stdin
- * @return Password
- */
- private String readPassword(Scanner input, String[] args, int argPos, String msg) {
- if (msg.isEmpty())
- msg = "Password:\n";
- if (args.length <= argPos || args[argPos].contains("--")) { // ask pw on stdin
- System.out.println(msg);
- return input.nextLine(); // clear buffer
- }
- // get pw from args
- return args[argPos];
- }
-
- // Function to display help information
- private static void displayHelp() {
- System.out.println("Usage: xlite-daemon [options]");
- System.out.println("Options:");
- System.out.println(" --enablerpcandconfigure Enable and configure RPC");
- System.out.println(" --development-endpoint Set a custom development endpoint");
- System.out.println(" Example: --development-endpoint ");
- System.out.println(" --exr-endpoint Set EXR endpoint for EXR server");
- System.out.println(" Example: --exr-endpoint ");
- System.out.println(" --version Display the version");
- System.out.println(" --createdefaultwallet Create a default wallet");
- System.out.println(" --createwalletmnemonic Create a wallet with a mnemonic");
- System.out.println(" --xliterpc Increment RPC port by 1");
- System.out.println(" --password Set password without prompt");
- System.out.println(" Example: --password ");
- System.out.println(" --getmnemonic Retrieve mnemonic for a password");
- System.out.println(" Example: --getmnemonic ");
- System.out.println(" --changepassword Change wallet password");
- System.out.println(" Example: --changepassword ");
- }
-}
diff --git a/src/main/java/io/cloudchains/app/crypto/KeyHandler.java b/src/main/java/io/cloudchains/app/crypto/KeyHandler.java
deleted file mode 100644
index 1741d78..0000000
--- a/src/main/java/io/cloudchains/app/crypto/KeyHandler.java
+++ /dev/null
@@ -1,247 +0,0 @@
-package io.cloudchains.app.crypto;
-
-import com.google.common.base.Joiner;
-import com.subgraph.orchid.encoders.Base64;
-import io.cloudchains.app.util.ConfigHelper;
-import org.bitcoinj.core.ECKey;
-import org.bitcoinj.crypto.MnemonicCode;
-import org.bitcoinj.crypto.MnemonicException;
-import org.bitcoinj.wallet.DeterministicSeed;
-import org.bitcoinj.wallet.UnreadableWalletException;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-import java.io.*;
-import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-
-public class KeyHandler {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private ECKey ecKey;
-
- public KeyHandler(ECKey key) {
- this.ecKey = key;
- }
-
- public ECKey getBaseECKey() {
- return this.ecKey;
- }
-
- public ECKey getPublicKey() {
- return ECKey.fromPublicOnly(this.ecKey.getPubKey());
- }
-
-
- public static boolean existsBaseECKeyFromLocal() {
- String keyPath = ConfigHelper.getLocalDataDirectory() + "key.dat";
- File keyFile = new File(keyPath);
-
- return keyFile.exists();
- }
-
- private static String encryptBaseSeed(String passphrase, byte[] seedBytes, byte[] salt) {
- try {
- SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
- PBEKeySpec spec = new PBEKeySpec(passphrase.toCharArray(), salt, 16384, 256);
- SecretKey tmp = skf.generateSecret(spec);
- SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
-
- Cipher cipher = Cipher.getInstance("AES");
- cipher.init(Cipher.ENCRYPT_MODE, key);
- byte[] encrypted = cipher.doFinal(seedBytes);
- byte[] encryptedValue = Base64.encode(encrypted);
- return new String(encryptedValue);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static List getBaseSeed(String passphrase) {
- File keyFile = new File(ConfigHelper.getLocalDataDirectory() + "key.dat");
- BufferedReader bufferedReader;
-
- if (existsBaseECKeyFromLocal()) {
- try {
- bufferedReader = new BufferedReader(new FileReader(keyFile));
- String saltB64 = bufferedReader.readLine();
- String seedEncryptedB64 = bufferedReader.readLine();
- bufferedReader.close();
-
- byte[] salt = Base64.decode(saltB64);
- byte[] seedEncrypted = Base64.decode(seedEncryptedB64);
-
- SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
- PBEKeySpec spec = new PBEKeySpec(passphrase.toCharArray(), salt, 16384, 256);
- SecretKey tmp = skf.generateSecret(spec);
- SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
-
- Cipher cipher = Cipher.getInstance("AES");
- cipher.init(Cipher.DECRYPT_MODE, key);
- String seed = new String(cipher.doFinal(seedEncrypted));
-
- return Arrays.asList(seed.split(" "));
- } catch (Exception e) {
- LOGGER.log(Level.FINER, "Error while obtaining base seed: " + e);
- LOGGER.log(Level.FINER, "Bad password.");
-
- return null;
- }
- } else {
-
- DeterministicSeed seed = null;
- seed = new DeterministicSeed(new SecureRandom(), 128, "", System.currentTimeMillis() / 1000);
-
- String mnemonic = Joiner.on(" ").join(Objects.requireNonNull(seed.getMnemonicCode()));
-
- if (writeInitialData(keyFile, mnemonic, passphrase)) {
- return seed.getMnemonicCode();
- } else {
- return null;
- }
- }
- }
-
- public static boolean importFromMnemonic(List mnemonicList, String passphrase) {
- File keyFile = new File(ConfigHelper.getLocalDataDirectory() + "key.dat");
- byte[] entropy;
-
- try {
- MnemonicCode mnemonicCode = new MnemonicCode();
- entropy = mnemonicCode.toEntropy(mnemonicList);
- } catch (IOException | MnemonicException.MnemonicWordException | MnemonicException.MnemonicChecksumException | MnemonicException.MnemonicLengthException e) {
- e.printStackTrace();
- return false;
- }
-
- DeterministicSeed seed = new DeterministicSeed(entropy , "", System.currentTimeMillis() / 1000);
-
- String mnemonic = Joiner.on(" ").join(Objects.requireNonNull(seed.getMnemonicCode()));
-
- if (!seed.getMnemonicCode().toString().equals(mnemonicList.toString()))
- return false;
-
- return writeInitialData(keyFile, mnemonic, passphrase);
- }
-
- public static byte[] mnemonicToEntropy(List mnemonicList) {
- byte[] entropy = null;
-
- try {
- MnemonicCode mnemonicCode = new MnemonicCode();
- entropy = mnemonicCode.toEntropy(mnemonicList);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return entropy;
- }
-
- private static File findRenameFile() {
- for (int i = 0; i < 100; i++) {
- File file = new File(ConfigHelper.getLocalDataDirectory() + "key-" + i + ".dat");
-
- if (!file.exists()) {
- return file;
- }
- }
-
- return null;
- }
-
- private static boolean writeInitialData(File keyFile, String mnemonic, String passphrase) {
- BufferedWriter bufferedWriter;
-
- // Move current wallet file to backups
- if (keyFile.exists()) {
- // Backups dir
- String backups = ConfigHelper.getLocalDataDirectory() + "backups" + File.separator;
- File backupsDir = new File(backups);
- if (!backupsDir.exists() && !backupsDir.mkdir())
- LOGGER.warning("Failed to create backups dir " + backupsDir.getPath());
-
- Date date = new Date();
- SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
- String newFileName = backups + "key-" + formatter.format(date) + ".dat";
-
- File oldFile = new File(keyFile.getPath());
- if (!oldFile.renameTo(new File(newFileName)))
- LOGGER.info("Failed to rename old wallet file");
- else
- LOGGER.info("Created wallet backup " + newFileName);
- }
-
- Random r = new SecureRandom();
- byte[] salt = new byte[20];
- r.nextBytes(salt);
-
- byte[] mnemonicByte = mnemonic.getBytes();
-
- String encryptedSeed = encryptBaseSeed(passphrase, mnemonicByte, salt);
-
- // Print mnemonic to console
- // System.out.println(mnemonic + "\n");
-
- try {
- bufferedWriter = new BufferedWriter(new FileWriter(keyFile));
- if (encryptedSeed != null) {
- bufferedWriter.write(new String(Base64.encode(salt)));
- bufferedWriter.newLine();
- bufferedWriter.write(encryptedSeed);
- bufferedWriter.newLine();
- }
- bufferedWriter.flush();
- bufferedWriter.close();
- return true;
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- }
-
- public static List getMnemonicFromString(String mnemonic) {
- return Arrays.asList(mnemonic.split(" "));
- }
-
- public static int calculatePasswordStrength(String password){
- // Password must be greater than 8 characters, contain at least one digit, one lowercase letter, one uppercase letter and one special character.
-
- int totalScore = 0;
-
- if( password.length() < 8 )
- return 0;
- else if( password.length() >= 10 )
- totalScore += 2;
- else
- totalScore += 1;
-
- //if it contains one digit, add 2 to total score
- if( password.matches("(?=.*[0-9]).*") )
- totalScore += 2;
-
- //if it contains one lower case letter, add 2 to total score
- if( password.matches("(?=.*[a-z]).*") )
- totalScore += 2;
-
- //if it contains one upper case letter, add 2 to total score
- if( password.matches("(?=.*[A-Z]).*") )
- totalScore += 2;
-
- //if it contains one special character, add 2 to total score
- if( password.matches("(?=.*[~!@#$%^&*()_-]).*") )
- totalScore += 2;
-
- return totalScore;
- }
-}
diff --git a/src/main/java/io/cloudchains/app/crypto/LoginUtils.java b/src/main/java/io/cloudchains/app/crypto/LoginUtils.java
deleted file mode 100644
index a7430e8..0000000
--- a/src/main/java/io/cloudchains/app/crypto/LoginUtils.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package io.cloudchains.app.crypto;
-
-import java.security.MessageDigest;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class LoginUtils {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private static String toSha256(String message) {
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- digest.update((message).getBytes());
- byte[] hash = digest.digest();
- StringBuilder hex = new StringBuilder();
- for (byte b : hash) {
- if ((0xff & b) < 0x10)
- hex.append("0").append(Integer.toHexString((0xFF & b)));
- else
- hex.append(Integer.toHexString(0xFF & b));
- }
- return hex.toString();
- } catch (Exception e) {
- LOGGER.log(Level.FINER, "Error while hashing message with SHA256!");
- e.printStackTrace();
- }
- return null;
- }
-
- public static String loginToEntropy(String password) {
- String shaPassword = toSha256(password);
-
- if (shaPassword == null) {
- LOGGER.log(Level.FINER, "Password hashing failed");
- return null;
- }
-
- return shaPassword;
- }
-
-}
diff --git a/src/main/java/io/cloudchains/app/net/ActiveCoinChangedEventListener.java b/src/main/java/io/cloudchains/app/net/ActiveCoinChangedEventListener.java
deleted file mode 100644
index c55092b..0000000
--- a/src/main/java/io/cloudchains/app/net/ActiveCoinChangedEventListener.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package io.cloudchains.app.net;
-
-public interface ActiveCoinChangedEventListener {
-
- void onActiveCoinChanged(CoinInstance newActiveCoin);
-
-}
diff --git a/src/main/java/io/cloudchains/app/net/CoinInstance.java b/src/main/java/io/cloudchains/app/net/CoinInstance.java
deleted file mode 100644
index a8fe647..0000000
--- a/src/main/java/io/cloudchains/app/net/CoinInstance.java
+++ /dev/null
@@ -1,1070 +0,0 @@
-package io.cloudchains.app.net;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.AtomicDouble;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-//import com.subgraph.orchid.encoders.Hex;
-import io.cloudchains.app.App;
-import io.cloudchains.app.Version;
-import io.cloudchains.app.crypto.KeyHandler;
-import io.cloudchains.app.net.api.JSONRPCController;
-import io.cloudchains.app.net.api.JSONRPCServer;
-//import io.cloudchains.app.net.protocols.alqocoin.AlqocoinNetworkParameters;
-//import io.cloudchains.app.net.protocols.bitbay.BitbayNetworkParameters;
-//import io.cloudchains.app.net.protocols.bitcoincash.BitcoinCashNetworkParameters;
-import io.cloudchains.app.net.protocols.blocknet.*;
-import io.cloudchains.app.net.protocols.dashcoin.DashcoinNetworkParameters;
-//import io.cloudchains.app.net.protocols.digibyte.DigibyteNetworkParameters;
-import io.cloudchains.app.net.protocols.dogecoin.DogecoinNetworkParameters;
-import io.cloudchains.app.net.protocols.litecoin.LitecoinNetworkParameters;
-//import io.cloudchains.app.net.protocols.phorecoin.PhorecoinNetworkParameters;
-import io.cloudchains.app.net.protocols.pivx.PivxNetworkParameters;
-//import io.cloudchains.app.net.protocols.poliscoin.PoliscoinNetworkParameters;
-//import io.cloudchains.app.net.protocols.ravencoin.RavencoinNetworkParameters;
-import io.cloudchains.app.net.protocols.syscoin.SyscoinNetworkParameters;
-import io.cloudchains.app.net.protocols.unobtanium.UnobtaniumNetworkParameters;
-//import io.cloudchains.app.net.protocols.trezarcoin.TrezarcoinNetworkParameters;
-import io.cloudchains.app.net.xrouter.XRouterMessage;
-import io.cloudchains.app.net.xrouter.XRouterMessageSerializer;
-import io.cloudchains.app.net.xrouter.XRouterPacketManager;
-import io.cloudchains.app.util.AddressBalance;
-import io.cloudchains.app.util.AddressDiscoveryService;
-import io.cloudchains.app.util.CloudTransaction;
-import io.cloudchains.app.util.ConfigHelper;
-import io.cloudchains.app.util.UTXO;
-import io.cloudchains.app.util.history.Transaction;
-import io.cloudchains.app.wallet.WalletHelper;
-import org.bitcoinj.core.*;
-import org.bitcoinj.params.MainNetParams;
-import org.bitcoinj.store.BlockStoreException;
-import org.bitcoinj.store.SPVBlockStore;
-import org.bitcoinj.utils.BtcFormat;
-import org.bitcoinj.utils.ListenerRegistration;
-import org.bitcoinj.utils.MonetaryFormat;
-import org.bitcoinj.utils.Threading;
-import org.bitcoinj.wallet.DeterministicSeed;
-import org.bitcoinj.wallet.Wallet;
-import org.json.JSONArray;
-
-import java.io.File;
-import java.math.BigDecimal;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-import com.subgraph.orchid.encoders.Hex;
-
-public class CoinInstance {
- public static class CoinError {
- public enum CoinErrorCode {
- BADPASSWORD, UNSUPPORTEDCOIN, BADMNEMONIC, CHANGEPASSWORDFAILED
- }
- private final CoinErrorCode code;
- private final String msg;
- public CoinError(String msg, CoinErrorCode code) {
- this.msg = msg;
- this.code = code;
- }
- public CoinErrorCode getCode() {
- return this.code;
- }
- public String getMessage() {
- return this.msg;
- }
- }
-
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private static final int MINIMUM_UTXO_UPDATE_INTERVAL = 500;
-
- private static final int FORWARD_ADDRESS_COUNT = 0;
-
- private static ArrayList coinInstances = new ArrayList<>();
- private static CoinInstance activeCurrency;
- private static CoinTicker activeBlocknetNetwork = null;
- private static CopyOnWriteArrayList> activeCoinChangedListeners = new CopyOnWriteArrayList<>();
- private static HashMap blockCounts = new HashMap<>();
- private static HashMap relayFees = new HashMap<>();
-
- private ConfigHelper configHelper;
- private WalletHelper walletHelper = null;
- private CoinTicker ticker;
- private ConcurrentHashMap transactionList = new ConcurrentHashMap<>();
- private ArrayList addressKeyPairs = new ArrayList<>();
- private ArrayList transactionObservableList = new ArrayList<>();
- private BlocknetPeerGroup blocknetPeerGroup;
- private BlocknetParameters blocknetNetworkParameters;
- private NetworkParameters networkParameters;
- private Wallet wallet;
- private BlockChain chain;
- private boolean hasXRouter = false;
- private KeyHandler keyHandler;
- private XRouterPacketManager xRouterPacketManager = null;
- private int rpcPort = -1;
- private boolean testnet = false;
- private JSONRPCServer coinRPCServer = null;
- private long lastUtxoUpdate = 0;
- private int updateFailures = 0;
- private int generatedAddressCount;
- private AddressDiscoveryService discoveryService = null;
- private static boolean addressDiscoveryEnabled = true;
-
- private CoinInstance(CoinTicker ticker) {
- this.ticker = ticker;
- this.configHelper = new ConfigHelper(CoinTickerUtils.tickerToString(getTicker()));
-
- addBlockCount(ticker, 0);
-
- if (isBlocknetNetwork())
- setActiveCurrency(this);
- }
-
- /**
- * Return mnemonic seed from wallet stored on disk. Correct passphrase required.
- * Returns empty string on error or failure to retrieve mnemonic (or if mnemonic
- * doesn't exist).
- * @param pw String
- * @return String
- */
- public static String getMnemonicForPw(String pw) {
- if (!KeyHandler.existsBaseECKeyFromLocal())
- return "";
-
- List seed = KeyHandler.getBaseSeed(pw);
- if (seed == null)
- return "";
-
- return Joiner.on(" ").join(seed);
- }
- public static int getBlockCountByTicker(CoinTicker ticker) {
- if (!blockCounts.containsKey(ticker)) {
- return -1;
- }
-
- return blockCounts.get(ticker).get();
- }
-
- public static double getRelayFeeByTicker(CoinTicker ticker) {
- if (!relayFees.containsKey(ticker)) {
- return -1;
- }
-
- return relayFees.get(ticker).get();
- }
-
- public static ArrayList getCoinInstances() {
- return coinInstances;
- }
-
- public boolean isBlocknetNetwork() {
- return getTicker() == CoinTicker.BLOCKNET || getTicker() == CoinTicker.BLOCKNET_TESTNET5;
- }
-
- public static void setActiveCurrency(CoinInstance newActiveCurrency) {
- activeCurrency = newActiveCurrency;
-
- for (ListenerRegistration registration : activeCoinChangedListeners) {
- if (registration.executor == Threading.SAME_THREAD) {
- registration.listener.onActiveCoinChanged(activeCurrency);
- }
- }
- }
-
- public XRouterPacketManager getXRouterPacketManager() {
- return xRouterPacketManager;
- }
-
- public static void addActiveCoinChangedListener(ActiveCoinChangedEventListener listener) {
- activeCoinChangedListeners.add(new ListenerRegistration<>(listener, Threading.SAME_THREAD));
- }
-
- public static CoinInstance getActiveCurrency() {
- return activeCurrency;
- }
-
- public AddressBalance getAddress(String addressB58) {
- for (AddressBalance address : addressKeyPairs) {
- if (address.getAddress().toBase58().equals(addressB58))
- return address;
- }
-
- return null;
- }
-
- public AddressBalance generateAddress(boolean updateConfig) {
- AddressBalance addressKeyPair = getWalletHelper().generateAddress();
- Address address = addressKeyPair.getAddress();
- DumpedPrivateKey privateKey = addressKeyPair.getPrivateKey();
- addressKeyPairs.add(addressKeyPair);
- LOGGER.log(Level.FINER, "[wallet] DEBUG: Generated new address, have " + addressKeyPairs.size() + ": " + address.toBase58() + ", private key: " + privateKey.toBase58() + " (hex: " + privateKey.getKey().getPrivateKeyAsHex() + ")");
-
- if (updateConfig) {
- configHelper.setAddressCount(configHelper.getAddressCount() + 1);
- configHelper.writeConfig();
- generatedAddressCount = configHelper.getAddressCount();
- }
-
- return addressKeyPair;
- }
-
- public void importPrivateKey(String privKey) {
- AddressBalance addressKeyPair = getWalletHelper().generateFromPrivateKey(privKey);
- AddressBalance addrExists = addressKeyPairs.stream()
- .filter(e -> e.getAddress().equals(addressKeyPair.getAddress())).findAny().orElse(null);
-
- if (addrExists == null)
- addressKeyPairs.add(addressKeyPair);
- }
-
- public CoinTicker getTicker() {
- return ticker;
- }
-
- private static CoinInstance getInstanceByTicker(CoinTicker ticker) {
- for (CoinInstance instance : coinInstances) {
- if (instance.getTicker() == ticker) {
- return instance;
- }
- }
-
- return null;
- }
-
- public static CoinTicker getActiveBlocknetNetwork() {
- return activeBlocknetNetwork;
- }
-
- public static CoinInstance getInstance(CoinTicker ticker) {
- CoinInstance instance = getInstanceByTicker(ticker);
- if (getActiveBlocknetNetwork() != null && (ticker == CoinTicker.BLOCKNET || ticker == CoinTicker.BLOCKNET_TESTNET5)) {
- return getInstanceByTicker(activeBlocknetNetwork);
- }
-
- if (ticker == CoinTicker.BLOCKNET || ticker == CoinTicker.BLOCKNET_TESTNET5) {
- activeBlocknetNetwork = ticker;
- LOGGER.log(Level.FINER, "[coin] Initialized active Blocknet network: " + ticker.toString());
- LOGGER.log(Level.FINER, "[coin] All subsequent calls to this function requesting a Blocknet network will return the above regardless of testnet or mainnet status.");
- }
-
- if (instance == null) {
- instance = new CoinInstance(ticker);
- if (ticker == CoinTicker.BLOCKNET)
- coinInstances.add(0, instance);
- else
- coinInstances.add(instance);
- }
-
- return instance;
- }
-
- /**
- * Change the password. Recreates the wallet file and encrypts with new password.
- * @param oldPassword
- * @param newPassword
- * @return Error or null
- */
- public static CoinError changePassword(String oldPassword, String newPassword) {
- if (!KeyHandler.existsBaseECKeyFromLocal()) {
- LOGGER.log(Level.FINER, "[wallet] Unable to change the password: Wallet not found on disk");
- return new CoinError("Unable to change the password: Wallet not found on disk",
- CoinError.CoinErrorCode.CHANGEPASSWORDFAILED);
- }
-
- List baseSeed = KeyHandler.getBaseSeed(oldPassword);
- if (baseSeed == null) {
- LOGGER.log(Level.FINER, "[wallet] Unable to change the password: Incorrect password");
- return new CoinError("Unable to change the password: Incorrect password",
- CoinError.CoinErrorCode.CHANGEPASSWORDFAILED);
- }
-
- // Get current wallet seed
- DeterministicSeed seed = new DeterministicSeed(baseSeed, null, "", System.currentTimeMillis() / 1000);
- List mnemonic = seed.getMnemonicCode();
-
- if (!KeyHandler.importFromMnemonic(mnemonic, newPassword)) {
- LOGGER.log(Level.FINER, "[wallet] Unable to change the password: Failed to create new wallet file");
- return new CoinError("Unable to change the password: Failed to create new wallet file",
- CoinError.CoinErrorCode.CHANGEPASSWORDFAILED);
- }
-
- return null;
- }
-
- public NetworkParameters getNetworkParameters() {
- return networkParameters;
- }
-
- private BlocknetParameters getBlocknetNetworkParameters() {
- return blocknetNetworkParameters;
- }
-
- public boolean hasXRouter() {
- return hasXRouter;
- }
-
- public void deinit() {
- if (getTicker() == getActiveBlocknetNetwork() && blocknetPeerGroup != null) {
- blocknetPeerGroup.stop();
- }
-
- if (coinRPCServer != null) {
- try {
- coinRPCServer.deinit();
- coinRPCServer.join();
- } catch (Exception e) {
- LOGGER.log(Level.FINER, "[coin] ERROR: Error while deinitializing coin RPC server!");
- e.printStackTrace();
- }
- }
- }
-
- public CoinError init(String pw, String userMnemonic, boolean isMnemonic) {
- return init(pw, userMnemonic, isMnemonic, false);
- }
-
- public CoinError init(String pw, String userMnemonic, boolean isMnemonic, boolean xliteRPC) {
- switch (ticker) {
- case BLOCKNET: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Blocknet main network.");
- blocknetNetworkParameters = new BlocknetNetworkParameters();
- networkParameters = blocknetNetworkParameters;
- hasXRouter = true;
- rpcPort = 41419;
- break;
- }
- case BLOCKNET_TESTNET5: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Blocknet test network v5.");
- blocknetNetworkParameters = new BlocknetTestnet5NetworkParameters();
- networkParameters = blocknetNetworkParameters;
- hasXRouter = true;
- rpcPort = 41419;
- testnet = true;
- break;
- }
- case BITCOIN: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Bitcoin main network.");
- networkParameters = MainNetParams.get();
- rpcPort = 8332;
- break;
- }
- // case BITCOIN_CASH: {
- // LOGGER.log(Level.FINER, "[coin] Initializing for BitcoinCash main network.");
- // networkParameters = new BitcoinCashNetworkParameters();
- // rpcPort = 48332;
- // break;
- // }
- case LITECOIN: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Litecoin main network.");
- networkParameters = new LitecoinNetworkParameters();
- rpcPort = 9332;
- break;
- }
- case DASHCOIN: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Dashcoin main network.");
- networkParameters = new DashcoinNetworkParameters();
- rpcPort = 9998;
- break;
- }
- // case DIGIBYTE: {
- // LOGGER.log(Level.FINER, "[coin] Initializing for Digibyte main network.");
- // networkParameters = new DigibyteNetworkParameters();
- // rpcPort = 14022;
- // break;
- // }
- case DOGECOIN: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Dogecoin main network.");
- networkParameters = new DogecoinNetworkParameters();
- rpcPort = 22555;
- break;
- }
- case SYSCOIN: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Syscoin main network.");
- networkParameters = new SyscoinNetworkParameters();
- rpcPort = 8370;
- break;
- }
- // case TREZARCOIN: {
- // networkParameters = new TrezarcoinNetworkParameters();
- // rpcPort = 17299;
- // break;
- // }
- // case BITBAY: {
- // networkParameters = new BitbayNetworkParameters();
- // rpcPort = 19915;
- // break;
- // }
- case PIVX: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Pivx main network.");
- networkParameters = new PivxNetworkParameters();
- rpcPort = 9951;
- break;
- }
- case UNOBTANIUM: {
- LOGGER.log(Level.FINER, "[coin] Initializing for Unobtanium main network.");
- networkParameters = new UnobtaniumNetworkParameters();
- rpcPort = 65111;
- break;
- }
- // case ALQOCOIN: {
- // LOGGER.log(Level.FINER, "[coin] Initializing for Alqo main network.");
- // networkParameters = new AlqocoinNetworkParameters();
- // rpcPort = 55000;
- // break;
- // }
- // case POLISCOIN: {
- // LOGGER.log(Level.FINER, "[coin] Initializing for Polis main network.");
- // networkParameters = new PoliscoinNetworkParameters();
- // rpcPort = 24127;
- // break;
- // }
- // case PHORECOIN: {
- // LOGGER.log(Level.FINER, "[coin] Initializing for Phore main network.");
- // networkParameters = new PhorecoinNetworkParameters();
- // rpcPort = 11772;
- // break;
- // }
- // case RAVENCOIN: {
- // LOGGER.log(Level.FINER, "[coin] Initializing for Ravencoin main network.");
- // networkParameters = new RavencoinNetworkParameters();
- // rpcPort = 8766;
- // break;
- // }
- default: {
- LOGGER.log(Level.FINER, "[coin] ERROR: Invalid/unsupported network: " + ticker.toString());
- return new CoinError("Unsupported coin", CoinError.CoinErrorCode.UNSUPPORTEDCOIN);
- }
- }
-
- if (xliteRPC) {
- rpcPort = rpcPort + 1;
-
- configHelper.setRpcPort(rpcPort);
- configHelper.writeConfig();
- }
-
- Context.propagate(new Context(networkParameters));
-
- List baseSeed;
- boolean existsOnDisk = false;
-
- if (isMnemonic) {
- baseSeed = Arrays.asList(pw.split(" "));
- } else {
- if (KeyHandler.existsBaseECKeyFromLocal())
- existsOnDisk = true;
- else if (userMnemonic != null) {
- if (!KeyHandler.importFromMnemonic(Arrays.asList(new String(userMnemonic).split(" ")), pw)) {
- LOGGER.log(Level.FINER, "[wallet] Unable to create wallet from mnemonic");
- return new CoinError("Unable to create wallet from mnemonic", CoinError.CoinErrorCode.BADMNEMONIC);
- }
- }
-
- baseSeed = KeyHandler.getBaseSeed(pw);
- }
-
- if (baseSeed == null) {
- LOGGER.log(Level.FINER, "[wallet] Possible Bad password: Unable to import or create base seed!");
- return new CoinError("Bad password", CoinError.CoinErrorCode.BADPASSWORD);
- }
-
- // In-memory wallet only
- DeterministicSeed seed = new DeterministicSeed(baseSeed, null, "", System.currentTimeMillis() / 1000);
- wallet = Wallet.fromSeed(networkParameters, seed);
- if (isBlocknetNetwork()) {
- String mnemonic = getMnemonic();
- // LOGGER.log(Level.FINE, "[wallet] Mnemonic = " + mnemonic);
- }
-
- // RUN ADDRESS DISCOVERY ONLY DURING WALLET INITIALIZATION
- // This ensures discovery runs once at wallet startup in ANY case
- if (addressDiscoveryEnabled) {
- LOGGER.log(Level.INFO, "[coin] Running address discovery");
- runAddressDiscovery();
- } else {
- LOGGER.log(Level.INFO, "[coin] Address discovery disabled");
- }
-
- // Make sure wallet addresses are available
- generateForwardAddresses(true);
-
- if (configHelper.getRpcPort() == -1000) {
- configHelper.setRpcPort(rpcPort);
- configHelper.writeConfig();
- } else {
- rpcPort = configHelper.getRpcPort();
- }
-
- if (configHelper.isRpcEnabled() && configHelper.validAuth() && rpcPort != -1) {
- coinRPCServer = JSONRPCController.getRPCServer(this);
- LOGGER.log(Level.INFO, "[rpc] Starting JSON-RPC server for coin " + CoinTickerUtils.tickerToString(getTicker()) + " on port " + getRPCPort());
-
- if (coinRPCServer.isAlive())
- coinRPCServer.deinit();
-
- coinRPCServer.start();
- }
-
- // Blocknet Network / XRouter not used (Dec 10)
-// if (isBlocknetNetwork() && hasXRouter()) {
-// XRouterMessageSerializer xRouterMessageSerializer = (getBlocknetNetworkParameters()).getXRouterMessageSerializer(false);
-// xRouterPacketManager = new XRouterPacketManager(xRouterMessageSerializer, blocknetNetworkParameters);
-// LOGGER.log(Level.FINER, "[coin] This network is a Blocknet network and supports XRouter. Our packet version is " + Integer.toString(XRouterPacketManager.getXRouterPacketVersion(), 16));
-// } else {
-// LOGGER.log(Level.FINER, "[coin] WARNING: This network (" + CoinTickerUtils.tickerToString(getTicker()) + ") does not support XRouter.");
-// }
-//
-// if (isBlocknetNetwork()) {
-// String userHome = ConfigHelper.getLocalDataDirectory();
-// Preconditions.checkNotNull(userHome);
-//
-// File spvDat = new File(userHome,"spv-" + CoinTickerUtils.tickerToString(getTicker()) + ".dat");
-// try {
-// chain = new BlockChain(networkParameters, getWallet(), new SPVBlockStore(networkParameters, spvDat));
-// } catch (BlockStoreException e) {
-// try {
-// chain = new BlockChain(networkParameters, getWallet(), new SPVBlockStore(networkParameters, spvDat));
-// } catch (BlockStoreException ex) {
-// LOGGER.log(Level.FINER, "Error while initializing blockchain object!");
-// ex.printStackTrace();
-// return false;
-// }
-// }
-//
-// LOGGER.log(Level.INFO, "[coin] Connecting to the (" + getTicker().toString() + ") network.");
-//
-// if (getAddressKeyPairs().size() == 0) {
-// LOGGER.log(Level.FINER, "[peer] Have no addresses. Generating forward addresses.");
-//
-// generateForwardAddresses(true);
-// }
-//
-// AddressBalance blockProofAddress = getAddressKeyPairs().get(0);
-//
-// keyHandler = new KeyHandler(blockProofAddress.getPrivateKey().getKey());
-//
-// blocknetPeerGroup = new BlocknetPeerGroup(this, (BlocknetNetworkParameters) blocknetNetworkParameters, chain);
-// connectToBlocknetNetwork();
-// }
-
- return null;
- }
-
- private void generateForwardAddresses(boolean fromStartup) {
- int configAddressCount = configHelper.getAddressCount();
- boolean updateConfig = false;
- if (configAddressCount < FORWARD_ADDRESS_COUNT) { // minimum starting addresses
- configAddressCount = FORWARD_ADDRESS_COUNT;
- updateConfig = true;
- }
-
- LOGGER.log(Level.FINER, "[wallet] Generating " + configAddressCount + " forward addresses for network " + getTicker().toString() + ".");
-
- // Ensure that internal HD wallet pointer matches the count we're expecting.
- // Required because wallet doesn't remember last HD wallet address prior to
- // reboot.
- if (fromStartup) {
- for (int i = 0; i < generatedAddressCount; i++) {
- getWalletHelper().generateAddress();
- }
- }
- for (int i = generatedAddressCount; i < configAddressCount; i++) {
- generateAddress(false);
- }
- generatedAddressCount = configAddressCount;
-
- if (updateConfig) {
- configHelper.setAddressCount(configAddressCount);
- configHelper.writeConfig();
- }
- }
-
- private void connectToBlocknetNetwork() {
- try {
- blocknetPeerGroup.start();
- } catch (Exception e) {
- LOGGER.log(Level.FINER, "Error while initializing blocking client object!");
- e.printStackTrace();
- return;
- }
-
- LOGGER.log(Level.FINER, "[coin] This network is connecting/connected.");
- }
-
- public Wallet getWallet() {
- return wallet;
- }
-
- public String getMnemonic() {
- return Joiner.on(" ").join(Objects.requireNonNull(getWallet().getKeyChainSeed().getMnemonicCode()));
- }
-
- public double getAllBalances() {
- double balance = 0;
-
- for (AddressBalance inst : getAddressKeyPairs()) {
- balance += inst.getBalanceProp();
- }
-
- return balance;
- }
-
- public String getAllBalancesFormatted() {
- BtcFormat f = BtcFormat.getInstance(BtcFormat.COIN_SCALE);
- return f.format(Coin.valueOf((long) (getAllBalances() * Coin.COIN.value)));
- }
-
- public void sendXrGetTransaction(BlocknetPeer blocknetPeer, String txid) {
- String currentCurrency = CoinTickerUtils.tickerToString(getTicker());
-
- HashMap body = new HashMap<>();
- body.put("currency", currentCurrency);
- body.put("txid", txid);
-
- getInstance(activeBlocknetNetwork).sendXrMessage(blocknetPeer, "xrGetTransaction", body);
- }
-
- public void sendXrGetBlockCount(BlocknetPeer blocknetPeer) {
- String currentCurrency = CoinTickerUtils.tickerToString(getTicker());
-
- HashMap body = new HashMap<>();
- body.put("currency", currentCurrency);
-
- getInstance(activeBlocknetNetwork).sendXrMessage(blocknetPeer, "xrGetBlockCount", body);
- }
-
- public void sendXrGetUtxos(BlocknetPeer blocknetPeer) {
- if (System.currentTimeMillis() - lastUtxoUpdate < MINIMUM_UTXO_UPDATE_INTERVAL) {
- LOGGER.log(Level.FINER, "[coin] Aborting UTXO checking as the list was updated less than 1 second ago.");
- return;
- }
-
- String currentCurrency = CoinTickerUtils.tickerToString(getTicker());
-
- HashMap body = new HashMap<>();
- body.put("currency", currentCurrency);
- body.put("command", "xrmgetutxos");
- body.put("params", getInstance(CoinTickerUtils.stringToTicker(currentCurrency)).getUTXOParams());
-
- getInstance(activeBlocknetNetwork).sendXrMessage(blocknetPeer, "xrService", body);
- }
-
- public String sendXrMessage(BlocknetPeer blocknetPeer, String command, HashMap params) {
- return sendXrMessage(blocknetPeer, UUID.randomUUID().toString(), command, params);
- }
-
- public String sendXrMessage(BlocknetPeer blocknetPeer, String uuid, String command, HashMap params) {
- XRouterMessage message = null;
-
- if (blocknetPeer == null || !blocknetPeer.getHaveConfig().get()) {
- LOGGER.log(Level.FINER, "[sendXrMessage] Config not received yet");
- return null;
- }
-
- switch (command) {
- case "xrGetBlockCount": {
- String currency = (String) params.get("currency");
-
- message = getXRouterPacketManager().getXrGetBlockCount(
- blocknetPeer,
- uuid,
- currency,
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- case "xrService": {
- String xrCustomCmd = (String) params.get("command");
-
- ArrayList paramsList = (ArrayList) params.get("params");
-
- message = getXRouterPacketManager().getXrService(
- blocknetPeer,
- uuid,
- xrCustomCmd,
- paramsList,
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- case "xrSendTransaction": {
- String feePayment = (String) params.get("feetx");
- String transaction = (String) params.get("transaction");
- String currency = (String) params.get("currency");
-
- message = getXRouterPacketManager().getXrSendTransaction(
- blocknetPeer,
- uuid,
- feePayment,
- currency,
- transaction,
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- case "xrGetBlockHash": {
- String feePayment = (String) params.get("feetx");
- String blockIndex = (String) params.get("blockIndex");
- String currency = (String) params.get("currency");
-
- message = getXRouterPacketManager().getXrGetBlockHash(
- blocknetPeer,
- uuid,
- feePayment,
- currency,
- blockIndex,
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- case "xrGetBlock": {
- String feePayment = (String) params.get("feetx");
- String blockHash = (String) params.get("blockHash");
- String currency = (String) params.get("currency");
-
- message = getXRouterPacketManager().getXrGetBlock(
- blocknetPeer,
- uuid,
- feePayment,
- currency,
- blockHash,
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- case "xrGetTransaction": {
- String txid = (String) params.get("txid");
- String currency = (String) params.get("currency");
-
- message = getXRouterPacketManager().getXrGetTransaction(
- blocknetPeer,
- uuid,
- currency,
- txid,
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- case "xrGetConfig": {
- message = getXRouterPacketManager().getXrGetConfig(
- blocknetPeer,
- uuid,
- "self",
- keyHandler.getBaseECKey(),
- keyHandler.getPublicKey());
- break;
- }
- default: {
- LOGGER.log(Level.FINER, "[coin] ERROR: Unknown XRouter Message! Command: " + command);
- uuid = null;
- break;
- }
- }
-
- if (message != null)
- blocknetPeerGroup.sendMessage(blocknetPeer, message);
-
- return uuid;
- }
-
- public JsonArray getAllUTXOS() {
- MonetaryFormat PLAIN_FORMAT = MonetaryFormat.BTC.minDecimals(8).repeatOptionalDecimals(1, 0).noCode();
-
- JsonArray unspentTxsJSON = new JsonArray();
- for (AddressBalance addressBalance : getAddressKeyPairs()) {
- for (UTXO utxo : addressBalance.getUtxos()) {
- if (utxo.isSpent())
- continue;
-
- org.bitcoinj.core.UTXO bUtxo = utxo.createUTXO();
-
- JsonObject utxoJSON = new JsonObject();
- utxoJSON.addProperty("txid", bUtxo.getHash().toString());
- utxoJSON.addProperty("vout", bUtxo.getIndex());
- utxoJSON.addProperty("address", bUtxo.getAddress());
-
- Monetary monetary = new Monetary() {
- @Override
- public int smallestUnitExponent() {
- return 8;
- }
-
- @Override
- public long getValue() {
- return utxo.getValue();
- }
-
- @Override
- public int signum() {
- if (this.getValue() == 0)
- return 0;
- return this.getValue() < 0 ? -1 : 1;
- }
- };
-
- BigDecimal amountDecimal = new BigDecimal(PLAIN_FORMAT.format(monetary).toString());
-
- utxoJSON.addProperty("amount", amountDecimal);
- utxoJSON.addProperty("scriptPubKey", new String(Hex.encode(bUtxo.getScript().getProgram())));
- utxoJSON.addProperty("spendable", true);
-
- int totalBlocks = CoinInstance.getBlockCountByTicker(getTicker());
- int confirmations = (totalBlocks - bUtxo.getHeight()) + 1;
- if (bUtxo.getHeight() == 0)
- confirmations = 0;
-
- utxoJSON.addProperty("confirmations", confirmations);
-
- unspentTxsJSON.add(utxoJSON);
- }
- }
-
- return unspentTxsJSON;
- }
-
- public JsonArray getAllTransactions() {
- JsonArray transactionsJSON = new JsonArray();
- for (Transaction tx : transactionList.values()) {
- JsonObject txJSON = new JsonObject();
- txJSON.addProperty("category", tx.getCategory());
- txJSON.addProperty("txid", tx.getTxid());
- txJSON.addProperty("blockhash", tx.getBlockhash());
- txJSON.addProperty("vout", tx.getVout());
- txJSON.addProperty("address", tx.getAddress());
-
- txJSON.addProperty("amount", tx.getValue());
- txJSON.addProperty("fee", tx.getFee());
- txJSON.addProperty("trusted", false);
- txJSON.addProperty("blocktime", tx.getBlocktime());
- txJSON.addProperty("time", tx.getBlocktime());
-
- txJSON.addProperty("confirmations", tx.getConfirmations());
-
- transactionsJSON.add(txJSON);
- }
-
-
- return transactionsJSON;
- }
-
- public ArrayList getUTXOParams() {
- ArrayList params = new ArrayList<>();
- params.add(CoinTickerUtils.tickerToString(getTicker()));
-
- JSONArray utxoAddresses = new JSONArray();
- for (AddressBalance addressBalance : getAddressKeyPairs()) {
- utxoAddresses.put(addressBalance.getAddress().toBase58());
- }
-
- params.add(utxoAddresses.toString());
-
- return params;
- }
-
- public AddressBalance getAddressBalance(String address) {
- for (AddressBalance addressBalance : getAddressKeyPairs()) {
- if (addressBalance.getAddress().toBase58().equals(address)) {
- return addressBalance;
- }
- }
-
- return null;
- }
-
- public ConfigHelper getConfigHelper() {
- return this.configHelper;
- }
-
- public WalletHelper getWalletHelper() {
- if (this.walletHelper == null)
- this.walletHelper = new WalletHelper(this);
-
- return this.walletHelper;
- }
-
- public void addBlockCount(CoinTicker ticker, Integer blockCount) {
- if (blockCounts.containsKey(ticker)) {
- if (blockCounts.get(ticker).get() > blockCount)
- return;
-
- blockCounts.get(ticker).set(blockCount);
- return;
- }
-
- blockCounts.put(ticker, new AtomicInteger(blockCount));
- }
-
- public void addRelayFee(CoinTicker ticker, Double relayFee) {
- if (relayFees.containsKey(ticker)) {
- relayFees.get(ticker).set(relayFee);
- return;
- }
-
- configHelper.setFee(relayFee);
-
- relayFees.put(ticker, new AtomicDouble(relayFee));
- }
-
- public void addCloudTransaction(CloudTransaction cloudTransaction) {
- if (transactionObservableList.isEmpty()) {
- transactionObservableList.add(cloudTransaction);
- return;
- }
-
- CloudTransaction tx = transactionObservableList.stream()
- .filter(e -> e.getTxHash().equals(cloudTransaction.getTxHash())).findAny().orElse(null);
-
- if (tx == null) {
- transactionObservableList.add(cloudTransaction);
- }
- }
-
- public void processUtxos(List utxoList) {
- // first lets clear UTXOs out of each address
- for (UTXO utxo : utxoList) {
- AddressBalance addressBalance = getAddress(utxo.getAddress());
- addressBalance.clearUtxos();
- }
-
- // now let's add them back
- for (UTXO utxo : utxoList) {
- AddressBalance addressBalance = getAddress(utxo.getAddress());
-
- if (addressBalance == null) {
- LOGGER.log(Level.FINER, "[utxo-parser] Warning: Encountered non-tracked address in reply: " + utxo.getAddress());
- continue;
- }
-
- boolean isNewUtxo = addressBalance.addUtxo(utxo);
-
- if (isNewUtxo) {
- addCloudTransaction(new CloudTransaction(utxo));
- LOGGER.log(Level.FINER, "[utxo-parser] Added new UTXO, address: " + utxo.getAddress() + " value: " + utxo.getAmount());
- }
- }
-
- setLastUtxoUpdate(System.currentTimeMillis());
- }
-
- public void processHistoryTxs(List transactions) {
- for (Transaction tx : transactions)
- transactionList.put(tx.uid(), tx);
- }
-
- private void setLastUtxoUpdate(long newUtxoTime) {
- lastUtxoUpdate = newUtxoTime;
- }
-
- public void reloadConfig() {
- this.configHelper.loadConfig();
- rpcPort = configHelper.getRpcPort();
- if (configHelper.getAddressCount() != generatedAddressCount)
- generateForwardAddresses(false);
-
- if (coinRPCServer == null)
- return; // no rpc available, skip
-
- JSONRPCController.removeRPCServer(this);
-
- coinRPCServer = JSONRPCController.getRPCServer(this);
-
- LOGGER.log(Level.INFO, "[rpc] Starting JSON-RPC server for coin " + CoinTickerUtils.tickerToString(getTicker()) + " on port " + getRPCPort());
- coinRPCServer.start();
- }
-
- public KeyHandler getKeyHandler() {
- return keyHandler;
- }
-
- public BlocknetPeerGroup getBlocknetPeerGroup() {
- return blocknetPeerGroup;
- }
-
- public BlocknetPeer getBestBlocknetPeer(String currency) {
- if (blocknetPeerGroup == null)
- return null;
-
- return blocknetPeerGroup.getBestBlocknetPeer(currency);
- }
-
- public ArrayList getAddressKeyPairs() {
- return addressKeyPairs;
- }
-
- public ArrayList getTransactionList() {
- return transactionObservableList;
- }
-
- public static AtomicInteger getBlockCount(CoinTicker ticker) {
- return blockCounts.putIfAbsent(ticker, new AtomicInteger(0));
- }
-
- public static HashMap getBlockCounts() {
- return blockCounts;
- }
-
- public int getRPCPort() {
- return rpcPort;
- }
-
- public boolean isTestnet() {
- return testnet;
- }
-
- public static String getVersionString() {
- return Version.SUBVERSION;
- }
-
- public void incrementUpdateFailures() {
- updateFailures += 1;
- }
-
- public void resetUpdateFailures() {
- updateFailures = 0;
- }
-
- public void runAddressDiscovery() {
- String currency = CoinTickerUtils.tickerToString(this.getTicker());
-
- if (discoveryService == null) {
- discoveryService = new AddressDiscoveryService(this);
- LOGGER.log(Level.INFO, "[coin-" + currency + "] AddressDiscoveryService created");
- }
-
- int discoveredCount = discoveryService.discoverAddressCount();
- int currentCount = configHelper.getAddressCount();
-
- if (discoveredCount > currentCount) {
- LOGGER.log(Level.INFO, "[coin-" + currency + "] Address discovery found " +
- discoveredCount + " addresses (was " + currentCount + ")");
-
- // Update config and generate missing addresses
- configHelper.setAddressCount(discoveredCount);
- configHelper.writeConfig();
-
- LOGGER.log(Level.INFO, "[coin-" + currency + "] Updated address count to " +
- discoveredCount);
- } else {
- LOGGER.log(Level.INFO, "[coin-" + currency + "] No new addresses discovered, " +
- "keeping current count: " + currentCount);
- }
- }
-
- public boolean isInstanceRunning() {
- return updateFailures < 5;
- }
-
- public static void setAddressDiscoveryEnabled(boolean enabled) {
- addressDiscoveryEnabled = enabled;
- }
-
- public static boolean isAddressDiscoveryEnabled() {
- return addressDiscoveryEnabled;
- }
-}
diff --git a/src/main/java/io/cloudchains/app/net/CoinTicker.java b/src/main/java/io/cloudchains/app/net/CoinTicker.java
deleted file mode 100644
index 391cc84..0000000
--- a/src/main/java/io/cloudchains/app/net/CoinTicker.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package io.cloudchains.app.net;
-
-import java.util.Arrays;
-import java.util.List;
-
-public enum CoinTicker {
- BLOCKNET,
- BLOCKNET_TESTNET5,
-
- BITCOIN,
- BITCOIN_CASH,
- LITECOIN,
- DASHCOIN,
- DIGIBYTE,
- DOGECOIN,
- TREZARCOIN,
- SYSCOIN,
- PIVX,
- ALQOCOIN,
- POLISCOIN,
- PHORECOIN,
- RAVENCOIN,
- BITBAY,
- UNOBTANIUM
- ;
-
- /**
- * List of supported coins.
- * @return Supported coins
- */
- public static List coins() {
- return Arrays.asList(
- BLOCKNET,
- BLOCKNET_TESTNET5,
- BITCOIN,
-// BITCOIN_CASH, - not support on backend
- LITECOIN,
- DASHCOIN,
-// DIGIBYTE, - not support on backend
- DOGECOIN,
-// TREZARCOIN, - not support on backend
- SYSCOIN,
- PIVX,
- UNOBTANIUM
-// ALQOCOIN, - not support on backend
-// POLISCOIN, - not support on backend
-// PHORECOIN, - not support on backend
-// RAVENCOIN
-// BITBAY - not support on backend
- );
- }
-}
diff --git a/src/main/java/io/cloudchains/app/net/CoinTickerUtils.java b/src/main/java/io/cloudchains/app/net/CoinTickerUtils.java
deleted file mode 100644
index bfe13af..0000000
--- a/src/main/java/io/cloudchains/app/net/CoinTickerUtils.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package io.cloudchains.app.net;
-
-import com.google.common.collect.HashBiMap;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-public class CoinTickerUtils {
- private static HashBiMap tickers;
-
- static {
- tickers = HashBiMap.create();
-
- tickers.put(CoinTicker.BLOCKNET, "BLOCK");
- tickers.put(CoinTicker.BLOCKNET_TESTNET5, "TBLOCK");
- tickers.put(CoinTicker.BITCOIN, "BTC");
- tickers.put(CoinTicker.LITECOIN, "LTC");
- tickers.put(CoinTicker.DASHCOIN, "DASH");
- tickers.put(CoinTicker.DOGECOIN, "DOGE");
- tickers.put(CoinTicker.SYSCOIN, "SYS");
- tickers.put(CoinTicker.PIVX, "PIVX");
-
- // TODO Temporarily disable until supported
-// tickers.put(CoinTicker.DIGIBYTE, "DGB");
-// tickers.put(CoinTicker.BITCOIN_CASH, "BCH");
-// tickers.put(CoinTicker.RAVENCOIN, "RVN");
-
- tickers.put(CoinTicker.ALQOCOIN, "XLQ");
- // TODO Temporarily disable PHORE and POLIS until supported
-// tickers.put(CoinTicker.POLISCOIN, "POLIS");
-// tickers.put(CoinTicker.PHORECOIN, "PHR");
- tickers.put(CoinTicker.TREZARCOIN, "TZC");
- tickers.put(CoinTicker.BITBAY, "BAY");
- tickers.put(CoinTicker.UNOBTANIUM, "UNO");
-
- }
-
- public static String tickerToString(CoinTicker ticker) {
- return tickers.get(ticker);
- }
-
- public static CoinTicker stringToTicker(String string) {
- return tickers.inverse().get(string);
- }
-
- public static Set getNetworkTickers() {
- return new HashSet<>(Arrays.asList(CoinTicker.BLOCKNET, CoinTicker.BLOCKNET_TESTNET5));
- }
-
- public static CoinTicker[] getActiveTickers() {
- return new CoinTicker[] {
- CoinTicker.BLOCKNET,
- CoinTicker.BITCOIN,
- CoinTicker.LITECOIN,
- CoinTicker.DASHCOIN,
- CoinTicker.DOGECOIN,
- CoinTicker.SYSCOIN,
- CoinTicker.PIVX,
-
- // TODO Temporarily disable until supported
-// CoinTicker.DIGIBYTE,
-// CoinTicker.BITCOIN_CASH,
-// CoinTicker.RAVENCOIN,
-
- CoinTicker.ALQOCOIN,
- // TODO Temporarily disable PHORE and POLIS until supported
-// CoinTicker.POLISCOIN,
-// CoinTicker.PHORECOIN,
- CoinTicker.TREZARCOIN,
- CoinTicker.BITBAY,
- CoinTicker.UNOBTANIUM,
- };
- }
-
- public static boolean tickerExists(String string) {
- return tickers.inverse().containsKey(string);
- }
-
- public static boolean isActiveTicker(CoinTicker ticker) {
- for (CoinTicker t : getActiveTickers()) {
- if (ticker == t) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/src/main/java/io/cloudchains/app/net/api/JSONRPCController.java b/src/main/java/io/cloudchains/app/net/api/JSONRPCController.java
deleted file mode 100644
index 4cbfd06..0000000
--- a/src/main/java/io/cloudchains/app/net/api/JSONRPCController.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package io.cloudchains.app.net.api;
-
-import io.cloudchains.app.net.CoinInstance;
-import io.cloudchains.app.util.ConfigHelper;
-
-import java.util.HashMap;
-
-public class JSONRPCController {
-
- private static final HashMap servers = new HashMap<>();
- private static JSONRPCMasterServer masterServer = new JSONRPCMasterServer(new ConfigHelper("master").getMasterRpcPort());
-
- public static JSONRPCMasterServer getMasterServer() {
- return masterServer;
- }
-
- public static JSONRPCServer getRPCServer(CoinInstance coinInstance) {
- if (coinInstance == null || coinInstance.getRPCPort() == -1) {
- throw new IllegalArgumentException("Bad coin instance");
- }
-
- if (!servers.containsKey(coinInstance)) {
- servers.put(coinInstance, new JSONRPCServer(coinInstance, coinInstance.getRPCPort()));
- }
-
- return servers.get(coinInstance);
- }
-
- public static void removeRPCServer(CoinInstance coinInstance) {
- if (coinInstance == null || coinInstance.getRPCPort() == -1) {
- throw new IllegalArgumentException("Bad coin instance");
- }
-
- JSONRPCServer server = servers.get(coinInstance);
- if (server == null)
- return;
-
- if (server.isAlive())
- server.deinit();
-
- servers.remove(coinInstance);
- }
-}
diff --git a/src/main/java/io/cloudchains/app/net/api/JSONRPCMasterServer.java b/src/main/java/io/cloudchains/app/net/api/JSONRPCMasterServer.java
deleted file mode 100644
index a301328..0000000
--- a/src/main/java/io/cloudchains/app/net/api/JSONRPCMasterServer.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package io.cloudchains.app.net.api;
-
-import io.cloudchains.app.App;
-import io.cloudchains.app.net.api.http.master.HTTPServerInitializer;
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.buffer.PooledByteBufAllocator;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelOption;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
-
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class JSONRPCMasterServer extends Thread {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private final int port;
- private boolean stopping = false;
-
- private Channel channel;
-
- JSONRPCMasterServer(int port) {
- this.port = port;
- }
-
- public void run() {
- EventLoopGroup workerGroup = new NioEventLoopGroup(2);
- try {
- LOGGER.log(Level.INFO, "[rpc] Starting master RPC server on port " + port + ".");
-
- ServerBootstrap bootstrap = new ServerBootstrap();
- bootstrap.group(workerGroup)
- .option(ChannelOption.SO_BACKLOG, 128)
- .option(ChannelOption.SO_REUSEADDR, true)
- .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
- .channel(NioServerSocketChannel.class)
- .childHandler(new HTTPServerInitializer());
-
- channel = bootstrap.bind(port).sync().channel();
-
- channel.closeFuture().sync();
- } catch (Exception e) {
- if (!stopping) {
- LOGGER.log(Level.FINER, "[json-rpc-server] ERROR: Error during server operation! (master RPC)");
- e.printStackTrace();
- }
- }
- }
-
- public void deinit() {
- stopping = true;
- LOGGER.log(Level.FINER, "[json-rpc-server] Interrupting server.");
-
- if (channel != null && channel.isOpen()) {
- channel.close();
- } else {
- LOGGER.log(Level.FINER, "[json-rpc-server] Channel is null or not open during deinitialization.");
- }
- }
-}
diff --git a/src/main/java/io/cloudchains/app/net/api/JSONRPCServer.java b/src/main/java/io/cloudchains/app/net/api/JSONRPCServer.java
deleted file mode 100644
index 4469092..0000000
--- a/src/main/java/io/cloudchains/app/net/api/JSONRPCServer.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package io.cloudchains.app.net.api;
-
-import io.cloudchains.app.net.CoinInstance;
-import io.cloudchains.app.net.CoinTickerUtils;
-import io.cloudchains.app.net.api.http.server.HTTPServerInitializer;
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.buffer.PooledByteBufAllocator;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelOption;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
-
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class JSONRPCServer extends Thread {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private final CoinInstance coin;
- private final int port;
- private boolean stopping = false;
-
- private Channel channel;
-
- JSONRPCServer(CoinInstance coin, int port) {
- this.coin = coin;
- this.port = port;
- }
-
- public void run() {
- EventLoopGroup workerGroup = new NioEventLoopGroup(5);
- try {
- ServerBootstrap bootstrap = new ServerBootstrap();
- bootstrap.group(workerGroup)
- .option(ChannelOption.SO_BACKLOG, 128)
- .option(ChannelOption.SO_REUSEADDR, true)
- .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
- .channel(NioServerSocketChannel.class)
- .childHandler(new HTTPServerInitializer(coin));
-
- channel = bootstrap.bind(port).sync().channel();
-
- LOGGER.log(Level.FINER, "[rpc] Starting RPC server for " + CoinTickerUtils.tickerToString(coin.getTicker()) + " on port " + port + ".");
-
- channel.closeFuture().sync();
- } catch (Exception e) {
- if (!stopping) {
- LOGGER.log(Level.FINER, "[json-rpc-server] ERROR: Error during server operation! (" + CoinTickerUtils.tickerToString(coin.getTicker()) + ")");
- e.printStackTrace();
- }
- }
- }
-
- public void deinit() {
- stopping = true;
- LOGGER.log(Level.FINER, "[json-rpc-server] Interrupting server.");
-
- channel.close();
- }
-}
diff --git a/src/main/java/io/cloudchains/app/net/api/http/client/EXRServer.java b/src/main/java/io/cloudchains/app/net/api/http/client/EXRServer.java
deleted file mode 100644
index a0b581a..0000000
--- a/src/main/java/io/cloudchains/app/net/api/http/client/EXRServer.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package io.cloudchains.app.net.api.http.client;
-
-import io.cloudchains.app.net.CoinTicker;
-import org.apache.http.client.config.RequestConfig;
-import java.io.IOException;
-import java.net.URI;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class EXRServer {
- private final static LogManager LOGMANAGER = LogManager.getLogManager();
- private final static Logger LOGGER = LOGMANAGER.getLogger(Logger.GLOBAL_LOGGER_NAME);
-
- private final String endpoint;
- private final EXRWrapper wrapper;
- private volatile boolean healthy;
- private long lastHealthCheck;
- private final java.util.Set supportedCoins;
- private volatile boolean capabilitiesProbed;
-
- // Add constants for configuration
- private static final int HEALTH_CHECK_INTERVAL_MS = 5000;
- private static final int CAPABILITY_PROBE_TIMEOUT_MS = 30000;
-
- public EXRServer(String endpoint) {
- // Store endpoint with trailing slash for consistency
- if (endpoint.endsWith("/")) {
- this.endpoint = endpoint;
- } else {
- this.endpoint = endpoint + "/";
- }
-
- // EXRWrapper expects endpoint without trailing slash
- String wrapperEndpoint = this.endpoint.endsWith("/") ?
- this.endpoint.substring(0, this.endpoint.length() - 1) : this.endpoint;
-
- this.wrapper = new EXRWrapper(wrapperEndpoint);
- this.healthy = true;
- this.lastHealthCheck = 0;
- this.supportedCoins = new java.util.HashSet<>();
- this.capabilitiesProbed = false;
- }
-
- public boolean probeCapabilities() {
- if (capabilitiesProbed) {
- return true;
- }
-
- if (!isHealthy()) {
- return false;
- }
-
- try {
- // CALL HEIGHTS ONCE - not per coin
- com.google.gson.JsonObject result = wrapper.executeGet("heights");
- if (result != null && result.has("result")) {
- com.google.gson.JsonObject heights = result.getAsJsonObject("result");
-
- // Extract ALL supported coins from single response
- // Only include coins that have non-null values (null means not supported)
- for (String coinName : heights.keySet()) {
- com.google.gson.JsonElement heightValue = heights.get(coinName);
-
- if (heightValue.isJsonNull()) {
- continue; // Skip unsupported coins (null values)
- }
-
- try {
- io.cloudchains.app.net.CoinTicker coin = io.cloudchains.app.net.CoinTickerUtils.stringToTicker(coinName);
- if (coin != null) {
- supportedCoins.add(coin);
- }
- } catch (Exception e) {
- LOGGER.log(Level.FINER, "[exr-server] Failed to map coin " + coinName, e);
- }
- }
- }
-
- capabilitiesProbed = true;
- LOGGER.log(Level.INFO, "[exr-server] Probed capabilities for " + endpoint + ", supports: " + supportedCoins.size() + " coins: " +
- supportedCoins.stream().map(io.cloudchains.app.net.CoinTickerUtils::tickerToString)
- .reduce((a, b) -> a + ", " + b).orElse("none"));
- return !supportedCoins.isEmpty();
- } catch (Exception e) {
- LOGGER.log(Level.WARNING, "[exr-server] Failed to probe capabilities for " + endpoint, e);
- return false;
- }
- }
-
- public boolean isHealthy() {
- long now = System.currentTimeMillis();
- if (now - lastHealthCheck < HEALTH_CHECK_INTERVAL_MS) {
- return healthy;
- }
-
- try {
- com.google.gson.JsonObject result = wrapper.executeGet("heights");
- healthy = result != null && !result.isJsonNull();
- } catch (Exception e) {
- healthy = false;
- LOGGER.log(Level.WARNING, "[exr-server] Health check failed for " + endpoint, e);
- }
- lastHealthCheck = now;
- return healthy;
- }
-
- public com.google.gson.JsonObject execute(String method, List