Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
# Database
# Database (Spring Boot reads DB_URL / DB_USERNAME / DB_PASSWORD)
DB_URL=jdbc:mysql://127.0.0.1:3306/wallet_db?allowMultiQueries=true&useSSL=false&characterEncoding=UTF-8&autoReconnect=true
DB_USERNAME=root
DB_PASSWORD=your_secure_password

# RabbitMQ
RABBITMQ_HOST=127.0.0.1
RABBITMQ_PORT=5672
RABBITMQ_USERNAME=guest
RABBITMQ_PASSWORD=guest
# Server
SERVER_PORT=8080

# XXL-Job
XXL_JOB_ADMIN_ADDRESSES=http://127.0.0.1:8099/xxl-job-admin
XXL_JOB_PORT=9999
XXL_JOB_ACCESS_TOKEN=your_access_token

# HSM Keystore
# Keystore (tokencore / WalletManager)
KEYSTORE_DIR=/data/keystores
KEYSTORE_PASSWORD=your_keystore_password
WALLET_KEYSTORE_PASSWORD=your_keystore_password

# Encryption
WALLET_CRYPTO_PUSH_KEY=your_32_char_encryption_key_here
# Optional: EOS deposit address for EOS flows
EOS_DEPOSIT_ADDRESS=

# Tokencore signing chain ids (H2 testnet example: ETH_SIGN_CHAIN_ID=11155111, BTC_SIGN_CHAIN_ID=1)
ETH_SIGN_CHAIN_ID=1
BTC_SIGN_CHAIN_ID=0

# External signer (when wallet_chain_config.signing_backend = EXTERNAL)
EXTERNAL_SIGNER_BASE_URL=

# Optional ETH gas price HTTP fallback (JSON shape must match ETH_GAS_LEVEL keys)
ETH_GAS_FALLBACK_URL=

# Logging
LOG_LEVEL=INFO

# Encryption (if used by your deployment)
WALLET_CRYPTO_PUSH_KEY=your_32_char_encryption_key_here
32 changes: 13 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,63 +13,57 @@ jobs:
build:
name: Build & Test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref != 'refs/heads/master' }}

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build
run: ./gradlew build -x test --no-daemon

- name: Run tests
run: ./gradlew test --no-daemon
continue-on-error: true

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: |
wallet-webapi/build/libs/*.jar
wallet-task/build/libs/*.jar
wallet-hsm/build/libs/*.jar
build/libs/*.jar
retention-days: 7

docker:
name: Docker Build
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/master'

strategy:
matrix:
service: [wallet-webapi, wallet-task, wallet-hsm]


steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./${{ matrix.service }}/Dockerfile
file: ./Dockerfile
push: false
tags: ${{ matrix.service }}:${{ github.sha }}
tags: wallet-app:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
66 changes: 8 additions & 58 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,75 +19,25 @@ build:
stage: build
script:
- chmod +x ./gradlew
- ./gradlew wallet-webapi:bootJar --no-daemon -x test
- ./gradlew wallet-task:bootJar --no-daemon -x test
- ./gradlew wallet-hsm:bootJar --no-daemon -x test
- ./gradlew bootJar --no-daemon -x test
artifacts:
paths:
- wallet-webapi/build/libs/*.jar
- wallet-hsm/build/libs/*.jar
- wallet-task/build/libs/*.jar
- build/libs/*.jar
expire_in: 1 hour

deploy wallet-webapi:
deploy wallet-app:
image: docker:latest
services:
- docker:dind
tags:
- wallet-dev
stage: deploy
script:
- docker stop wallet-webapi || true
- docker rm wallet-webapi || true
- docker rmi wallet-webapi || true
- docker build -t wallet-webapi -f wallet-webapi/Dockerfile .
- docker run -d --name wallet-webapi
--restart unless-stopped
-p 10001:10001
-v /etc/localtime:/etc/localtime:ro
--env-file /etc/wallet/webapi.env
wallet-webapi
when: manual

deploy wallet-task:
image: docker:latest
services:
- docker:dind
tags:
- wallet-dev
stage: deploy
script:
- docker stop wallet-task || true
- docker rm wallet-task || true
- docker rmi wallet-task || true
- docker build -t wallet-task -f wallet-task/Dockerfile .
- docker run -d --name wallet-task
--restart unless-stopped
-p 10033:10033
-v /etc/localtime:/etc/localtime:ro
--env-file /etc/wallet/task.env
wallet-task
when: manual

deploy wallet-hsm:
image: docker:latest
services:
- docker:dind
tags:
- wallet-dev
stage: deploy
script:
- docker stop wallet-hsm || true
- docker rm wallet-hsm || true
- docker rmi wallet-hsm || true
- docker build -t wallet-hsm -f wallet-hsm/Dockerfile .
- docker run -d --name wallet-hsm
--restart unless-stopped
-p 10888:10888
-v /etc/localtime:/etc/localtime:ro
-v /mnt/wallets:/data/keystores
--env-file /etc/wallet/hsm.env
wallet-hsm
- docker stop wallet-app || true
- docker rm wallet-app || true
- docker rmi wallet-app || true
- docker build -t wallet-app -f Dockerfile .
- docker run -d --name wallet-app --restart unless-stopped -p 8080:8080 -v /etc/localtime:/etc/localtime:ro -v /mnt/wallets:/data/keystores --env-file /etc/wallet/wallet.env wallet-app
when: manual

after_script:
Expand Down
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Single-module Spring Boot app (sources under ./src)
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY gradlew gradlew.bat settings.gradle build.gradle gradle.properties ./
COPY gradle ./gradle
COPY src ./src
RUN chmod +x gradlew && ./gradlew bootJar --no-daemon -x test

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN addgroup -S wallet && adduser -S wallet -G wallet
COPY --from=builder /app/build/libs/*.jar /app/app.jar
USER wallet
EXPOSE 8080
ENV SERVER_PORT=8080
ENTRYPOINT ["java","-XX:+UseContainerSupport","-XX:MaxRAMPercentage=75.0","-jar","/app/app.jar"]
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ It provides unified wallet management, blockchain synchronization, deposit detec

This repository is converging to a **single-service architecture** to make deployment and operations simpler:

- One startup service (`wallet-webapi` runtime)
- One Spring Boot service (sources under [`src/`](src/); root Gradle module)
- No RabbitMQ dependency
- No xxl-job dependency
- HSM capabilities invoked in-process
- Unified tokencore dependency: `com.github.galaxyscitech:tokencore:2.0.0`
- HSM / signing invoked in-process via [tokencore](https://github.com/GalaxySciTech/tokencore) `com.github.galaxyscitech:tokencore:2.0.1`
- Optional **external HTTP signer** when `wallet_chain_config.signing_backend` = `EXTERNAL` (see `wallet.external-signer` in [`application.yml`](src/main/resources/application.yml))

This means fewer moving parts, easier troubleshooting, and faster onboarding for developers and DevOps teams.

Expand Down Expand Up @@ -67,9 +67,27 @@ Services:
### Option B: Run locally with Gradle

```bash
./gradlew :wallet-webapi:bootRun
./gradlew bootRun
```

Initialize the database from [`db/wallet_db.sql`](db/wallet_db.sql). If you already have `wallet_chain_config`, apply [`db/002_wallet_chain_signing_backend.sql`](db/002_wallet_chain_signing_backend.sql) for the `signing_backend` column.

### Configuration highlights

| Area | Notes |
|------|--------|
| JDBC | `DB_URL`, `DB_USERNAME`, `DB_PASSWORD` (see [`.env.example`](.env.example)) |
| Keystore | `KEYSTORE_DIR`, `WALLET_KEYSTORE_PASSWORD` |
| Testnet signing | `ETH_SIGN_CHAIN_ID` (e.g. `11155111` for Sepolia), `BTC_SIGN_CHAIN_ID` (`0` mainnet / `1` testnet) |
| Metrics | Actuator: `/actuator/health`, `/actuator/prometheus` |
| Gas fallback | Optional `ETH_GAS_FALLBACK_URL` in `sys_config` if the node does not expose `eth_gasPrice` |

### H2 (testnet on-chain smoke)

1. Point `ETH_RPC_URL` in `config` (or env) to a **Sepolia** (or other testnet) HTTP endpoint.
2. Set `ETH_SIGN_CHAIN_ID=11155111` and fund a test hot wallet.
3. Run `./gradlew bootRun` and exercise withdraw/collect paths from Swagger or your integration client.

---

## First-time user path (10-minute onboarding)
Expand Down
15 changes: 9 additions & 6 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
项目正在收敛为**单体 Spring Boot 钱包服务**。

## 当前变化
- 统一单服务启动(以 `wallet-webapi` 作为应用运行时)
- 不再依赖 RabbitMQ
- 不再依赖 xxl-job
- HSM 能力改为进程内调用
- tokencore 统一为 `com.github.galaxyscitech:tokencore:2.0.0`
- **单模块**:源码仅在仓库根目录 [`src/`](src/),Gradle 在根目录 [`build.gradle`](build.gradle)
- 不再依赖 RabbitMQ / xxl-job
- 签名与地址:**tokencore** `com.github.galaxyscitech:tokencore:2.0.1`(进程内)
- 可选 **外部 HTTP 签名**:表 `wallet_chain_config.signing_backend = EXTERNAL`,配置 `wallet.external-signer.base-url`

## 快速开始
### 1)Docker Compose 启动
Expand All @@ -21,9 +20,13 @@ docker compose up -d --build

### 2)手动启动
```bash
./gradlew :wallet-webapi:bootRun
./gradlew bootRun
```

数据库初始化见 [`db/wallet_db.sql`](db/wallet_db.sql)。若已有库,需增加 `signing_backend` 列时执行 [`db/002_wallet_chain_signing_backend.sql`](db/002_wallet_chain_signing_backend.sql)。

环境变量示例:[`.env.example`](.env.example)。测试网链上冒烟:配置 Sepolia 等 `ETH_RPC_URL`,设置 `ETH_SIGN_CHAIN_ID=11155111`,准备测试资金后走 Swagger 提现/归集相关接口。

## 接口
- 钱包接口:`/wallet/v1`
- 链接口:`/block_chain/v1`
Expand Down
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ dependencies {
implementation 'wf.bitcoin:bitcoin-rpc-client:1.2.4'
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
implementation 'com.github.sealedtx:bitcoin-cash-converter:1.0'
implementation 'com.github.galaxyscitech:tokencore:2.0.0'
implementation 'com.github.galaxyscitech:tokencore:2.0.1'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
implementation 'commons-codec:commons-codec:1.16.0'
implementation 'org.bitcoinj:bitcoinj-core:0.14.7'
Expand Down
10 changes: 5 additions & 5 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
./gradlew bootRepackage
rm -rf build/*
mv wallet-webapi/build/libs/*.jar build/
mv wallet-task/build/libs/*.jar build/
mv wallet-hsm/build/libs/*.jar build/
set -e
./gradlew bootJar --no-daemon -x test
rm -rf build/dist
mkdir -p build/dist
cp build/libs/*.jar build/dist/
5 changes: 5 additions & 0 deletions db/002_wallet_chain_signing_backend.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Optional migration: per-chain signing backend (TOKENCORE | EXTERNAL)
ALTER TABLE `wallet_chain_config`
ADD COLUMN `signing_backend` varchar(32) NOT NULL DEFAULT 'TOKENCORE'
COMMENT 'TOKENCORE=in-process tokencore; EXTERNAL=wallet.external-signer HTTP'
AFTER `withdraw_enabled`;
1 change: 1 addition & 0 deletions db/wallet_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ CREATE TABLE `wallet_chain_config` (
`enabled` tinyint(1) DEFAULT 1,
`deposit_scan_enabled` tinyint(1) DEFAULT 1,
`withdraw_enabled` tinyint(1) DEFAULT 0,
`signing_backend` varchar(32) NOT NULL DEFAULT 'TOKENCORE' COMMENT 'TOKENCORE|EXTERNAL',
`confirmations` int(11) DEFAULT 12,
`start_block` bigint(20) DEFAULT 0,
`current_block` bigint(20) DEFAULT 0,
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ services:
app:
build:
context: .
dockerfile: wallet-webapi/Dockerfile
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/wallet/entity/domain/WalletChainConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class WalletChainConfig {
private Boolean enabled;
private Boolean depositScanEnabled;
private Boolean withdrawEnabled;
/** TOKENCORE (default) or EXTERNAL — see application.yml wallet.signing */
@Column(name = "signing_backend", length = 32)
private String signingBackend = "TOKENCORE";
private Integer confirmations;
private Long startBlock;
private Long currentBlock;
Expand All @@ -31,6 +34,8 @@ public class WalletChainConfig {
public void setDepositScanEnabled(Boolean depositScanEnabled) { this.depositScanEnabled = depositScanEnabled; }
public Boolean getWithdrawEnabled() { return withdrawEnabled; }
public void setWithdrawEnabled(Boolean withdrawEnabled) { this.withdrawEnabled = withdrawEnabled; }
public String getSigningBackend() { return signingBackend; }
public void setSigningBackend(String signingBackend) { this.signingBackend = signingBackend; }
public Integer getConfirmations() { return confirmations; }
public void setConfirmations(Integer confirmations) { this.confirmations = confirmations; }
public Long getStartBlock() { return startBlock; }
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/com/wallet/WebApiApplication.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.wallet

import com.wallet.biz.config.WalletProperties
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties

/**

* Created by pie on 2019-04-11 15: 53.

*/
@SpringBootApplication
@EnableConfigurationProperties(WalletProperties::class)
open class WebApiApplication

fun main() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.wallet.webapi.config
package com.wallet.api.config

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.wallet.webapi.config
package com.wallet.api.config

import com.wallet.webapi.config.interceptor.RequestInterceptor
import com.wallet.api.config.interceptor.RequestInterceptor
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
Expand Down
Loading
Loading