Compare commits
15 Commits
5b70c6de89
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5f40eb79c | ||
|
|
3f4349c9d2 | ||
|
|
194fd7357c | ||
|
|
9a644b689a | ||
|
|
9bc861f1d1 | ||
|
|
89e45128b6 | ||
|
|
3c097af03d | ||
|
|
35a73c340c | ||
|
|
1c8fe77a43 | ||
|
|
6d360df21d | ||
|
|
a6021f9523 | ||
|
|
abc8ee7553 | ||
|
|
f46459b687 | ||
|
|
f818b38b3d | ||
|
|
87d094e2f1 |
@@ -1,67 +0,0 @@
|
|||||||
# Multi-stage build for Java 24
|
|
||||||
FROM openjdk:24-jdk-slim AS builder
|
|
||||||
|
|
||||||
# Install required packages
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
unzip \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy gradle wrapper and build files
|
|
||||||
COPY gradle/ gradle/
|
|
||||||
COPY gradlew .
|
|
||||||
COPY gradle.properties .
|
|
||||||
COPY settings.gradle.kts .
|
|
||||||
COPY build.gradle.kts .
|
|
||||||
|
|
||||||
# Make gradlew executable
|
|
||||||
RUN chmod +x gradlew
|
|
||||||
|
|
||||||
# Download dependencies (for better caching)
|
|
||||||
RUN ./gradlew dependencies --no-daemon
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY src/ src/
|
|
||||||
|
|
||||||
# Build the application
|
|
||||||
RUN ./gradlew shadowJar --no-daemon
|
|
||||||
|
|
||||||
# Runtime stage
|
|
||||||
FROM openjdk:24-jdk-slim
|
|
||||||
|
|
||||||
# Install required runtime packages
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Create app user
|
|
||||||
RUN groupadd -r ccoin && useradd -r -g ccoin ccoin
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy the built JAR from builder stage
|
|
||||||
COPY --from=builder /app/build/libs/*.jar app.jar
|
|
||||||
|
|
||||||
# Change ownership to app user
|
|
||||||
RUN chown -R ccoin:ccoin /app
|
|
||||||
|
|
||||||
# Switch to app user
|
|
||||||
USER ccoin
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
|
||||||
CMD curl -f http://localhost:8080/health || exit 1
|
|
||||||
|
|
||||||
# JVM options for production
|
|
||||||
ENV JAVA_OPTS="-Xmx512m -Xms256m -XX:+UseG1GC -XX:+UseStringDeduplication"
|
|
||||||
|
|
||||||
# Run the application
|
|
||||||
CMD ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
|
||||||
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
val ktor_version: String by project
|
|
||||||
val kotlin_version: String by project
|
|
||||||
val logback_version: String by project
|
|
||||||
val exposed_version: String by project
|
|
||||||
val postgresql_version: String by project
|
|
||||||
val hikari_version: String by project
|
|
||||||
val flyway_version: String by project
|
|
||||||
val bouncycastle_version: String by project
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm") version "2.2.21"
|
|
||||||
kotlin("plugin.serialization") version "2.2.21"
|
|
||||||
id("io.ktor.plugin") version "3.3.3"
|
|
||||||
id("com.gradleup.shadow") version "9.3.0"
|
|
||||||
id("org.flywaydb.flyway") version "11.19.0"
|
|
||||||
application
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "org.ccoin"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
application {
|
|
||||||
mainClass.set("org.ccoin.ServerKt")
|
|
||||||
|
|
||||||
val isDevelopment: Boolean = project.ext.has("development")
|
|
||||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// Ktor server
|
|
||||||
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-cors-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-call-logging-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-status-pages-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-compression-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-default-headers-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-host-common-jvm:$ktor_version")
|
|
||||||
implementation("io.ktor:ktor-server-config-yaml:$ktor_version")
|
|
||||||
|
|
||||||
// Database
|
|
||||||
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
|
|
||||||
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
|
|
||||||
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
|
|
||||||
implementation("org.jetbrains.exposed:exposed-java-time:$exposed_version")
|
|
||||||
implementation("org.postgresql:postgresql:$postgresql_version")
|
|
||||||
implementation("com.zaxxer:HikariCP:$hikari_version")
|
|
||||||
implementation("org.flywaydb:flyway-core:$flyway_version")
|
|
||||||
implementation("org.flywaydb:flyway-database-postgresql:$flyway_version")
|
|
||||||
|
|
||||||
// Logging
|
|
||||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
|
||||||
implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
|
|
||||||
|
|
||||||
// Crypto utilities
|
|
||||||
implementation("org.bouncycastle:bcprov-jdk18on:$bouncycastle_version")
|
|
||||||
implementation("org.bouncycastle:bcpkix-jdk18on:$bouncycastle_version")
|
|
||||||
|
|
||||||
// JSON
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
|
|
||||||
|
|
||||||
// Coroutines
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
|
|
||||||
|
|
||||||
// Testing
|
|
||||||
testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version")
|
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
|
|
||||||
testImplementation("io.mockk:mockk:1.13.13")
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
|
|
||||||
testImplementation("org.testcontainers:testcontainers:1.20.3")
|
|
||||||
testImplementation("org.testcontainers:postgresql:1.20.3")
|
|
||||||
testImplementation("org.testcontainers:junit-jupiter:1.20.3")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<Test> {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fat JAR configuration for Docker
|
|
||||||
tasks.jar {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.shadowJar {
|
|
||||||
archiveClassifier.set("")
|
|
||||||
manifest {
|
|
||||||
attributes["Main-Class"] = "org.ccoin.ServerKt"
|
|
||||||
}
|
|
||||||
mergeServiceFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flyway configuration
|
|
||||||
flyway {
|
|
||||||
url = "jdbc:postgresql://localhost:5432/ccoin"
|
|
||||||
user = "ccoin"
|
|
||||||
password = "ccoin"
|
|
||||||
locations = arrayOf("classpath:db/migration")
|
|
||||||
baselineOnMigrate = true
|
|
||||||
validateOnMigrate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Docker build task
|
|
||||||
tasks.register<Exec>("buildDocker") {
|
|
||||||
dependsOn("shadowJar")
|
|
||||||
commandLine("docker", "build", "-t", "ccoin-server:latest", ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Development task
|
|
||||||
tasks.register("dev") {
|
|
||||||
dependsOn("run")
|
|
||||||
doFirst {
|
|
||||||
project.ext.set("development", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
services:
|
|
||||||
# Postgres Database
|
|
||||||
postgres:
|
|
||||||
image: postgres:17-alpine
|
|
||||||
container_name: ccoin-postgres
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: ccoin
|
|
||||||
POSTGRES_USER: ccoin
|
|
||||||
POSTGRES_PASSWORD: ccoin
|
|
||||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
- ./docker/postgres/init:/docker-entrypoint-initdb.d
|
|
||||||
networks:
|
|
||||||
- ccoin-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U ccoin -d ccoin"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
# CCoin server
|
|
||||||
ccoin-server:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
container_name: ccoin-server
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
environment:
|
|
||||||
# Database configuration
|
|
||||||
DATABASE_URL: jdbc:postgresql://postgres:5432/ccoin
|
|
||||||
DATABASE_USER: ccoin
|
|
||||||
DATABASE_PASSWORD: ccoin
|
|
||||||
DATABASE_POOL_SIZE: 20
|
|
||||||
|
|
||||||
# Server configuration
|
|
||||||
SERVER_HOST: 0.0.0.0
|
|
||||||
SERVER_PORT: 8080
|
|
||||||
|
|
||||||
# Mining configuration
|
|
||||||
MINING_DIFFICULTY: 4
|
|
||||||
MINING_REWARD: 50.0
|
|
||||||
BLOCK_TIME_TARGET: 600000
|
|
||||||
|
|
||||||
# Security
|
|
||||||
JWT_SECRET: your-super-secret-jwt-key-change-this-in-production
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
LOG_LEVEL: INFO
|
|
||||||
|
|
||||||
# Development
|
|
||||||
DEVELOPMENT_MODE: false
|
|
||||||
volumes:
|
|
||||||
- ./logs:/app/logs
|
|
||||||
networks:
|
|
||||||
- ccoin-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 60s
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres_data:
|
|
||||||
driver: local
|
|
||||||
|
|
||||||
networks:
|
|
||||||
ccoin-network:
|
|
||||||
driver: bridge
|
|
||||||
ipam:
|
|
||||||
config:
|
|
||||||
- subnet: 172.20.0.0/16
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Ktor
|
|
||||||
ktor_version=3.3.3
|
|
||||||
kotlin_version=2.2.21
|
|
||||||
|
|
||||||
# Database
|
|
||||||
exposed_version=0.56.0
|
|
||||||
postgresql_version=42.7.4
|
|
||||||
hikari_version=6.0.0
|
|
||||||
flyway_version=11.19.0
|
|
||||||
|
|
||||||
# Crypto
|
|
||||||
bouncycastle_version=1.78.1
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
logback_version=1.5.12
|
|
||||||
|
|
||||||
# Gradle
|
|
||||||
kotlin.code.style=official
|
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|
||||||
org.gradle.caching=true
|
|
||||||
org.gradle.parallel=true
|
|
||||||
|
|
||||||
# Application
|
|
||||||
ccoin.version=1.0.0
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package org.ccoin
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
println("CCoin Server Started")
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package org.ccoin.config
|
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariConfig
|
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
|
||||||
import org.ccoin.database.Tables
|
|
||||||
import org.jetbrains.exposed.sql.Database
|
|
||||||
import org.jetbrains.exposed.sql.SchemaUtils
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
object DatabaseConfig {
|
|
||||||
private val logger = LoggerFactory.getLogger(DatabaseConfig::class.java)
|
|
||||||
|
|
||||||
fun init() {
|
|
||||||
logger.info("Initializing database connection...")
|
|
||||||
|
|
||||||
val config = HikariConfig().apply {
|
|
||||||
driverClassName = "org.postgresql.Driver"
|
|
||||||
jdbcUrl = System.getenv("DATABASE_URL") ?: "jbdc:postgresql://localhost:5432/ccoin"
|
|
||||||
username = System.getenv("DATABASE_USER") ?: "ccoin"
|
|
||||||
password = System.getenv("DATABASE_PASSWORD") ?: "ccoin"
|
|
||||||
|
|
||||||
// Connection pool settings
|
|
||||||
maximumPoolSize = (System.getenv("DATABASE_POOL_SIZE")?.toIntOrNull() ?: 20)
|
|
||||||
minimumIdle = 5
|
|
||||||
connectionTimeout = 30000
|
|
||||||
idleTimeout = 600000
|
|
||||||
maxLifetime = 1800000
|
|
||||||
|
|
||||||
// Performance settings
|
|
||||||
isAutoCommit = false
|
|
||||||
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
|
|
||||||
|
|
||||||
// Connection validation
|
|
||||||
connectionTestQuery = "SELECT 1"
|
|
||||||
validationTimeout = 5000
|
|
||||||
|
|
||||||
// Pool name for monitoring
|
|
||||||
poolName = "CCoinPool"
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
val dataSource = HikariDataSource(config)
|
|
||||||
Database.connect(dataSource)
|
|
||||||
|
|
||||||
logger.info("Database connection established successfully")
|
|
||||||
|
|
||||||
// Create tables if they don't exist
|
|
||||||
createTables()
|
|
||||||
} catch(e: Exception) {
|
|
||||||
logger.error("Failed to initialize database connection", e)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTables() {
|
|
||||||
logger.info("Creating database tables if they don't exist...")
|
|
||||||
|
|
||||||
transaction {
|
|
||||||
SchemaUtils.create(
|
|
||||||
Tables.Wallets,
|
|
||||||
Tables.Transactions,
|
|
||||||
Tables.Blocks
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Database tables created/verified successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getConnectionInfo(): Map<String, Any> {
|
|
||||||
return mapOf(
|
|
||||||
"url" to (System.getenv("DATABASE_URL") ?: "jdbc:postgresql://localhost:5432/ccoin"),
|
|
||||||
"user" to (System.getenv("DATABASE_USER") ?: "ccoin_user"),
|
|
||||||
"poolSize" to (System.getenv("DATABASE_POOL_SIZE")?.toIntOrNull() ?: 20)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package org.ccoin.database
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.sql.Table
|
|
||||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
object Tables {
|
|
||||||
object Wallets : Table("wallets") {
|
|
||||||
val address = varchar("address", 64) // Format random_word:random_6_digits
|
|
||||||
val balance = decimal("balance", 20, 8).default(BigDecimal.ZERO)
|
|
||||||
val label = varchar("label", 255).nullable()
|
|
||||||
val createdAt = long("created_at")
|
|
||||||
val lastActivity = long("last_activity").nullable()
|
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(address)
|
|
||||||
|
|
||||||
init {
|
|
||||||
index(false, createdAt)
|
|
||||||
index(false, lastActivity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Transactions : Table("transactions") {
|
|
||||||
val hash = varchar("hash", 64)
|
|
||||||
val fromAddress = varchar("from_address", 64).nullable()
|
|
||||||
val toAddress = varchar("to_address", 64)
|
|
||||||
val amount = decimal("amount", 20, 8)
|
|
||||||
val fee = decimal("fee", 20, 8).default(BigDecimal.ZERO)
|
|
||||||
val memo = text("memo").nullable()
|
|
||||||
val blockHash = varchar("block_hash", 64).nullable()
|
|
||||||
val timestamp = long("timestamp")
|
|
||||||
val status = varchar("status", 20).default("pending")
|
|
||||||
val confirmations = integer("confirmations").default(0)
|
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(hash)
|
|
||||||
|
|
||||||
init {
|
|
||||||
index(false, fromAddress)
|
|
||||||
index(false, toAddress)
|
|
||||||
index(false, blockHash)
|
|
||||||
index(false, timestamp)
|
|
||||||
index(false, status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Blocks : Table("blocks") {
|
|
||||||
val hash = varchar("hash", 64)
|
|
||||||
val previousHash = varchar("previous_hash", 64).nullable()
|
|
||||||
val merkleRoot = varchar("merkle_root", 64)
|
|
||||||
val timestamp = long("timestamp")
|
|
||||||
val difficulty = integer("difficulty")
|
|
||||||
val nonce = long("nonce")
|
|
||||||
val minerAddress = varchar("miner_address", 64)
|
|
||||||
val reward = decimal("reward", 20, 8)
|
|
||||||
val height = integer("height").autoIncrement()
|
|
||||||
val transactionCount = integer("transaction_count").default(0)
|
|
||||||
val confirmations = integer("confirmations").default(0)
|
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(hash)
|
|
||||||
|
|
||||||
init {
|
|
||||||
index(false, height)
|
|
||||||
index(false, minerAddress)
|
|
||||||
index(false, timestamp)
|
|
||||||
index(false, previousHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.ccoin.exceptions
|
|
||||||
|
|
||||||
/** Base exception class for all CCoin-related exceptions */
|
|
||||||
open class CCoinException(
|
|
||||||
message: String,
|
|
||||||
cause: Throwable? = null,
|
|
||||||
val errorCode: String? = null
|
|
||||||
) : Exception(message, cause)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.ccoin.exceptions
|
|
||||||
|
|
||||||
class InsufficientFundsException(
|
|
||||||
val address: String,
|
|
||||||
val requestedAmount: Double,
|
|
||||||
val availableBalance: Double
|
|
||||||
) : CCoinException(
|
|
||||||
message = "Insufficient funds in wallet $address. Requested: $requestedAmount, Available: $availableBalance",
|
|
||||||
errorCode = "INSUFFICIENT_FUNDS"
|
|
||||||
)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.ccoin.exceptions
|
|
||||||
|
|
||||||
class InvalidTransactionException(
|
|
||||||
message: String,
|
|
||||||
val transactionHash: String? = null
|
|
||||||
) : CCoinException(
|
|
||||||
message = message,
|
|
||||||
errorCode = "INVALID_TRANSACTION"
|
|
||||||
)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.ccoin.exceptions
|
|
||||||
|
|
||||||
class WalletNotFoundException(
|
|
||||||
val address: String
|
|
||||||
) : CCoinException(
|
|
||||||
message = "Wallet with address '$address' not found",
|
|
||||||
errorCode = "WALLET_NOT_FOUND"
|
|
||||||
)
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package org.ccoin.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ApiResponse<T>(
|
|
||||||
val success: Boolean,
|
|
||||||
val data: T? = null,
|
|
||||||
val error: String? = null,
|
|
||||||
val timestamp: Long = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ErrorResponse(
|
|
||||||
val error: String,
|
|
||||||
val code: String? = null,
|
|
||||||
val details: Map<String, String>? = null,
|
|
||||||
val timestamp: Long = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class SuccessResponse(
|
|
||||||
val message: String,
|
|
||||||
val timestamp: Long = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class HealthResponse(
|
|
||||||
val status: String,
|
|
||||||
val version: String,
|
|
||||||
val uptime: Long,
|
|
||||||
val database: DatabaseHealth,
|
|
||||||
val blockchain: BlockchainHealth
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class DatabaseHealth(
|
|
||||||
val connected: Boolean,
|
|
||||||
val responseTime: Long,
|
|
||||||
val activeConnections: Int,
|
|
||||||
val maxConnections: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class BlockchainHealth(
|
|
||||||
val latestBlock: Int,
|
|
||||||
val pendingTransactions: Int,
|
|
||||||
val networkHashRate: Double,
|
|
||||||
val averageBlockTime: Long
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class PaginationRequest(
|
|
||||||
val page: Int = 1,
|
|
||||||
val pageSize: Int = 50,
|
|
||||||
val sortBy: String? = null,
|
|
||||||
val sortOrder: SortOrder = SortOrder.DESC
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class PaginatedResponse<T>(
|
|
||||||
val data: List<T>,
|
|
||||||
val pagination: PaginationInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class PaginationInfo(
|
|
||||||
val currentPage: Int,
|
|
||||||
val pageSize: Int,
|
|
||||||
val totalItems: Int,
|
|
||||||
val totalPages: Int,
|
|
||||||
val hasNext: Boolean,
|
|
||||||
val hasPrevious: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
enum class SortOrder {
|
|
||||||
ASC,
|
|
||||||
DESC
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package org.ccoin.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class StartMiningRequest(
|
|
||||||
val minerAddress: String, // Format: random_word:random_6_digits
|
|
||||||
val difficulty: Int? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class SubmitMiningRequest(
|
|
||||||
val minerAddress: String,
|
|
||||||
val nonce: Long,
|
|
||||||
val hash: String,
|
|
||||||
val previousHash: String,
|
|
||||||
val timestamp: Long
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class BlockResponse(
|
|
||||||
val hash: String,
|
|
||||||
val previousHash: String?,
|
|
||||||
val merkleRoot: String,
|
|
||||||
val timestamp: Long,
|
|
||||||
val difficulty: Int,
|
|
||||||
val nonce: Long,
|
|
||||||
val minerAddress: String
|
|
||||||
val reward: Double,
|
|
||||||
val height: Int,
|
|
||||||
val transactionCount: Int,
|
|
||||||
val confirmations: Int = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class MiningJobResponse(
|
|
||||||
val jobId: String,
|
|
||||||
val target: String,
|
|
||||||
val difficulty: Int,
|
|
||||||
val previousHash: String,
|
|
||||||
val height: Int,
|
|
||||||
val timestamp: Long,
|
|
||||||
val expiresAt: Long
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class MiningStatsResponse(
|
|
||||||
val minerAddress: String,
|
|
||||||
val totalBlocksMined: Int,
|
|
||||||
val totalRewardEarned: Double,
|
|
||||||
val lastBlockMined: Long?,
|
|
||||||
val currentDifficulty: Int
|
|
||||||
)
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.ccoin.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class SendTransactionRequest(
|
|
||||||
val fromAddress: String, // Format random_word:random_6_digits
|
|
||||||
val toAddress: String, // Format random_word:random_6_digits
|
|
||||||
val amount: Double,
|
|
||||||
val fee: Double = 0.0,
|
|
||||||
val memo: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class TransactionResponse(
|
|
||||||
val hash: String,
|
|
||||||
val fromAddress: String?,
|
|
||||||
val toAddress: String,
|
|
||||||
val amount: Double,
|
|
||||||
val fee: Double,
|
|
||||||
val memo: String?,
|
|
||||||
val blockHash: String?,
|
|
||||||
val timestamp: Long,
|
|
||||||
val status: TransactionStatus,
|
|
||||||
val confirmations: Int = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class TransactionHistoryResponse(
|
|
||||||
val transactions: List<TransactionResponse>,
|
|
||||||
val totalCount: Int,
|
|
||||||
val page: Int,
|
|
||||||
val pageSize: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
enum class TransactionStatus {
|
|
||||||
PENDING,
|
|
||||||
CONFIRMED,
|
|
||||||
FAILED,
|
|
||||||
CANCELLED
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package org.ccoin.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class CreateWalletRequest(
|
|
||||||
val label: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class WalletResponse(
|
|
||||||
val address: String, // Format random_word:random_6_digits (e.g. "phoenix:123456")
|
|
||||||
val balance: Double,
|
|
||||||
val label: String?,
|
|
||||||
val createdAt: Long,
|
|
||||||
val lastActivity: Long?
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class BalanceResponse(
|
|
||||||
val address: String,
|
|
||||||
val balance: Double
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class UpdateWalletRequest(
|
|
||||||
val label: String?
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user