This class implements a basic blockchain in Java, incorporating fundamental blockchain concepts such as block validation, transactions, and wallet management.

Blockchain in Java
A focused Java implementation of a learning-oriented blockchain, covering blocks, proof of work, wallets, signed transactions, UTXO accounting, and chain validation.
Educational project: this implementation demonstrates blockchain concepts and is not intended for production financial use.
Contents
- Overview
- Architecture
- Key implementation areas
- Running the project
- Testing
- Project structure
- Contributing
- Useful links
Overview
This repository contains:
- a simplified blockchain implementation in Java
- wallet and transaction logic based on public-key cryptography
- UTXO-style accounting with unspent transaction outputs
This project is intentionally defined as a blockchain learning demo. It models the core ideas of linked blocks, UTXO accounting, signatures, and validation without pretending to be a production blockchain node or a secure financial system.
Assumptions and scope
The implementation is intentionally constrained to make the concepts easy to study:
- blocks are linked by a hash chain and a simple proof-of-work target
- transactions are signed with ECDSA keys and checked against wallet ownership
- balances are derived from the ledger’s unspent outputs, not from floating-point state
- transaction and output validation is strict enough to reject malformed inputs, negative values, and impossible spending patterns
- the project does not simulate peer-to-peer networking or consensus across independent nodes
This is a teaching-oriented model, not a real-world blockchain deployment.
Architecture
The main flow connects wallets, signed transactions, blocks, and the UTXO ledger. Validation checks the resulting chain and transaction relationships.
flowchart LR WalletA[Wallet] -->|signs| Transaction[Transaction] WalletB[Wallet] -->|receives| Transaction Transaction -->|included in| Block[Block] Block -->|linked by hash| Blockchain[Blockchain] Blockchain --> Ledger[BlockchainLedger / UTXOs] Ledger -->|calculates| Balance[Wallet balance] Blockchain --> Validator[BlockchainValidator] Validator -->|checks| Block Validator -->|checks| Transaction
The detailed design is documented in BLOCKCHAIN.md.
Project status
- Java toolchain: 21
- Gradle: 8.10.2
- Test framework: JUnit 5.12.2
- Server framework: Spring Boot 4.2.0-M1
- Crypto library: Bouncy Castle 1.85.2
- Serialization: Gson 2.14.0
- Formatting: Spotless 8.10.2
- Dependency update reporting: Gradle Versions Plugin 0.61.0
Project identity and professional quality bar
This repository is intentionally scoped as a blockchain learning demo, not as a production payment network. The professional standard here is clarity, correctness, and explicit boundaries rather than production-grade security guarantees.
The project deliberately keeps the following rules:
- the domain package contains blockchain logic only
- the demo package contains scenario orchestration and examples
- balance rules are derived from UTXOs rather than floating-point state
- validation stays explicit and easy to read
- block and transaction state is private, with read-only views and controlled mutation methods
- transaction inputs and outputs are private value objects with controlled resolution and accessors
- the ledger is read-only outside the blockchain aggregate
- Merkle roots are checked against block transactions
- accepted blocks and transactions are sealed against later mutation
- unsupported production concerns such as networking, persistence, consensus, and wallet storage remain outside scope
This keeps the implementation educational while still behaving like a maintainable Java project.
Key implementation areas
Domain layer
The blockchain engine and core model live under the domain package:
- src/main/java/blockchain/domain/Blockchain.java — blockchain state and block storage
- src/main/java/blockchain/domain/Block.java — individual block model
- src/main/java/blockchain/domain/Transaction.java — transaction logic and validation
- src/main/java/blockchain/domain/TransactionInput.java — spending references
- src/main/java/blockchain/domain/TransactionOutput.java — unspent output representation
- src/main/java/blockchain/domain/Wallet.java — key generation, balance lookup, and fund transfers
- src/main/java/blockchain/domain/BlockchainValidator.java — validation rules for chain integrity
- src/main/java/blockchain/domain/BlockchainLedger.java — UTXO ledger and balance calculations
- src/main/java/blockchain/domain/StringUtil.java — hashing and string helpers
- src/main/java/blockchain/domain/ValidationResult.java — structured validation outcomes
Demo layer
The runnable examples and scenario setup live under the demo package:
- src/main/java/blockchain/demo/SimpleBlockchain.java — main blockchain demo with mining and transfer flow
- src/main/java/blockchain/demo/BlockchainDemo.java — alternate demo/example app
- src/main/java/blockchain/demo/BlockchainScenario.java — reusable demo scenario orchestration
- src/main/java/blockchain/demo/BlockchainCli.java — command-line entry point handling
- src/main/java/blockchain/demo/DemoOptions.java — validated demo parameters
Running the project
Build
./gradlew build
Every push to master and every pull request runs the same Gradle build and Docker image build through GitHub Actions. The workflow uses Java 21 and includes tests, Spotless checks, packaging, and the Dockerfile build.
Run the app
./gradlew run
The demo also accepts a few command-line options so the learning scenario can be explored without changing source code:
./gradlew run --args="--difficulty=2 --initial-balance=150 --first-transfer=35 --second-transfer=10"
./gradlew run --args="--difficulty=2 --minimum-transaction=1"
./gradlew run --args="--help"
The CLI controls only the demo scenario. It does not change the domain rules or turn this project into a production node.
The scenario deliberately includes both successful and rejected transfers. Rejected transfers are reported without terminating the demo, so insufficient funds and validation behavior can be observed safely. Accepted blocks print their hash, parent hash, Merkle root, nonce, and transaction totals. The final summary reports block count, transaction count, UTXO count and details, cumulative proof-of-work, chain tip, and validation status.
Run the HTTP API
Start the Spring Boot server with the in-memory learning chain:
./gradlew bootRun
The first API slice is available under /api/v1:
curl http://localhost:8080/api/v1/blockchain/info
curl http://localhost:8080/api/v1/blocks
curl http://localhost:8080/api/v1/utxos
curl http://localhost:8080/api/v1/blockchain/validate
curl http://localhost:8080/api/v1/wallets
curl http://localhost:8080/api/v1/transactions
curl -X POST http://localhost:8080/api/v1/blocks/mine
Register a client public key with POST /api/v1/wallets, then submit a client-signed transaction with POST /api/v1/transactions. The request contains Base64-encoded EC public keys, input output IDs, and a Base64-encoded ECDSA signature; private keys are never accepted by this server. State-changing requests are protected by a cookie-backed CSRF token. Submitted transactions remain pending until POST /api/v1/blocks/mine is called.
This server is single-node. The default profile is in-memory for quick learning; the postgres profile persists and restores chain state and pending transactions. The versioned HTTP contract is maintained in openapi.yaml.
The persistence foundation is included for local development. Start PostgreSQL with:
docker compose up -d postgres
The postgres Spring profile enables the database connection and Flyway migrations:
SPRING_PROFILES_ACTIVE=postgres ./gradlew bootRun
Flyway applies V1__create_blockchain_schema.sql, which creates tables for chain metadata, blocks, transactions, inputs, outputs, wallets, and pending transactions. Add future schema changes as ordered migrations beneath src/main/resources/db/migration. The PostgreSQL profile loads existing state on startup, validates the reconstructed chain, and persists accepted state changes atomically.
Run the complete API and database stack with Docker:
docker compose up --build
The API container exposes /actuator/health and waits for PostgreSQL before starting. The image uses a deterministic java-blockchain.jar artifact and includes curl for its container healthcheck.
Then query the API:
curl http://localhost:8080/api/v1/blockchain/info
curl http://localhost:8080/actuator/health
The endpoint contract is available at openapi.yaml. It documents request/response shapes, CSRF-protected state-changing operations, and the current single-node semantics. Generated Swagger UI is intentionally not included yet.
Invalid domain requests, validation failures, and malformed JSON return RFC 7807-style Problem Details with a stable type property for client-side handling.
Stop the stack while preserving the database volume with docker compose down. Use docker compose down -v when you want a clean learning chain.
Run tests
./gradlew test
Check for dependency updates
The Gradle Versions Plugin reports available updates without changing project files:
./gradlew dependencyUpdates
The report is written to build/dependencyUpdates/report.txt. Review each candidate before changing build.gradle, then run the complete verification workflow:
./gradlew clean build --refresh-dependencies
/home/karol/.local/share/snyk-ls/snyk-linux test
/home/karol/.local/share/snyk-ls/snyk-linux code test
Security-sensitive dependencies should be upgraded individually so regressions are easy to identify. Bouncy Castle must remain on a supported, non-vulnerable release.
Use the Makefile shortcuts
make help
make build
make test
make run
make clean
Testing
The project includes JUnit-based checks for wallet behavior, cryptography, and balance-related logic. The tests live in:
- src/test/java/blockchain/WalletTest.java
- src/test/java/blockchain/BlockTest.java
- src/test/java/blockchain/BlockchainIntegrationTest.java
- src/test/java/blockchain/BlockchainBehaviorContractTest.java
- src/test/java/blockchain/TransactionModelValidationTest.java
- src/test/java/blockchain/DemoOptionsTest.java
- src/test/java/blockchain/BlockchainApiTest.java
Blockchain design notes
This project follows a simplified blockchain model inspired by classic blockchain tutorials, with explicit integrity rules:
- each block contains a hash and a previous hash
- transactions are signed with ECDSA keys
- UTXOs represent available spendable value
- balances are computed from wallet-owned outputs
- funds are sent only when the wallet has enough unspent value
- transaction signatures cover the canonical sender, recipient, amount, and input references
- transaction and output IDs are checked against their canonical contents
- rejected blocks restore both the ledger and transaction object state
- block acceptance returns structured validation results for explainable rejection
- valid chains expose cumulative proof-of-work for chain comparison
Important implementation detail: the project uses BigDecimal for monetary values instead of float/double, which avoids precision problems in transaction arithmetic.
Project structure
java-blockchain/
├── README.md
├── BLOCKCHAIN.md
├── AGENT.md
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── Makefile
├── build.gradle
├── settings.gradle
├── gradlew
├── src/
│ ├── main/java/
│ │ └── blockchain/
│ │ ├── BlockchainApplication.java
│ │ ├── api/
│ │ ├── config/
│ │ ├── demo/
│ │ ├── domain/
│ │ ├── persistence/
│ │ └── service/
│ ├── main/resources/
│ │ ├── application.yml
│ │ ├── application-postgres.yml
│ │ ├── openapi.yaml
│ │ └── db/migration/
│ │ └── V1__create_blockchain_schema.sql
│ └── test/java/
│ └── blockchain/
└── gradle/
Domain vs demo
The repository intentionally separates the two concerns:
- the domain package contains the blockchain engine itself: blocks, transactions, wallets, validation, and the ledger
- the demo package contains scenario setup and console examples that exercise the domain model
This keeps the educational project easy to read: the engine stays focused on blockchain rules, while the demo layer remains a thin, human-friendly entry point.
The runtime path is:
flowchart LR CLI[BlockchainCli] --> Options[DemoOptions] Options --> Scenario[BlockchainScenario] Scenario --> Domain[blockchain.domain] Domain --> Output[Console output]
Notes
This repository is a focused learning-oriented Java blockchain implementation. It emphasizes clear state modeling, dependency hygiene, explicit validation, and a clean separation between the blockchain domain and its runnable demo. Repository-specific working conventions are documented in AGENT.md.
Production-readiness note
This project is intentionally not production-ready. It is designed for learning, experimentation, and code review. It does not include peer-to-peer networking, consensus across multiple nodes, secure key storage, persistent storage, mempool management, or production-grade security hardening. Values should never be treated as real currency or transferred outside a controlled educational environment.
Contributing
Small, focused improvements are welcome. Before opening a change:
- Keep the example-oriented scope of the project in mind.
- Add or update tests for behavioral changes.
- Run
./gradlew testand./gradlew buildlocally. - Update the relevant markdown documentation when public behavior or project structure changes.
- Keep the code readable and easy to reason about for learners; favor clear names and obvious validation over clever shortcuts.
Please do not treat the demo cryptography, key handling, or transaction model as production-ready security code.
For a short project workflow, see CONTRIBUTING.md.
Changelog
The project history is tracked in CHANGELOG.md.
Useful links
- BLOCKCHAIN.md — blockchain architecture notes
- https://gradle.org — Gradle build system
- https://www.bouncycastle.org — cryptography provider
Attribution
The blockchain examples were originally inspired by publicly available Java blockchain tutorials. The current repository includes additional refactoring, tests, and project-specific examples.
