A Java 17+ Maven library for building extensible, permission (private) blockchain networks.
Plug in your own consensus logic, transaction types, and storage backend — ship in minutes.
<!-- Minimum viable set: core + one consensus engine + one storage backend -->
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-core</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-consensus</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-storage</artifactId>
<version>1.0.0</version>
</dependency>// 1. Build and start
BlockchainNode node = BlockchainConfig.builder()
.consensusEngine(new ProofOfWorkEngine(4)) // 4 leading zero bits
.storage(new LevelDBStorage("/data/chain")) // swap for InMemoryStorage in tests
.chainId("my-chain")
.build();
node.
start();
// 2. Inspect
BlockchainNode.NodeStatus status = node.status();
System.out.
println("Height: "+status.chainHeight()); // 1 (genesis)
System.out.
println("Engine: "+status.consensusEngine()); // "ProofOfWork"
System.out.
println("Mempool: "+status.mempoolSize()); // 0
// 3. Stop
node.
stop();Extend Transaction and annotate with @JsonCreator / @JsonProperty for automatic
JSON round-trips — no registry, no registration step required.
public final class PaymentTx extends Transaction {
private final String currency; // included in ECDSA signature
@JsonCreator
public PaymentTx(
@JsonProperty("id") UUID id,
@JsonProperty("senderAddress") String senderAddress,
@JsonProperty("receiverAddress") String receiverAddress,
@JsonProperty("amount") BigDecimal amount,
@JsonProperty("timestamp") Instant timestamp,
@JsonProperty("metadata") Map<String, Object> metadata,
@JsonProperty("currency") String currency) {
super(id, senderAddress, receiverAddress, amount, timestamp, metadata);
this.currency = Objects.requireNonNull(currency);
}
// Bind currency into the signature — prevents post-signing substitution
@Override
public byte[] toSignableBytes() {
byte[] base = super.toSignableBytes();
byte[] ext = ("|" + currency).getBytes(StandardCharsets.UTF_8);
byte[] out = new byte[base.length + ext.length];
System.arraycopy(base, 0, out, 0, base.length);
System.arraycopy(ext, 0, out, base.length, ext.length);
return out;
}
public String getCurrency() {
return currency;
}
}// Create wallets
WalletManager wm = new WalletManager();
Wallet alice = wm.createWallet();
Wallet bob = wm.createWallet();
// Build, sign, and submit a transaction
PaymentTx tx = new PaymentTx(
UUID.randomUUID(), alice.getAddress(), bob.getAddress(),
new BigDecimal("50.00"), Instant.now(), null, "USD");
alice.
sign(tx);
node.
submitTransaction(tx); // validates → mempool → TransactionSubmittedEventList<Transaction> pending = node.getMempool().getTopN(100);
Block mined = node.getChain()
.getConsensusEngine()
.mineBlock(pending, node.getChain().getLatestBlock());
node.
getChain().
addBlock(mined); // validates → persists → BlockAddedEvent
System.out.
println("Block #"+mined.getIndex() +": "+mined.
getHash());
System.out.
println("Chain valid: "+node.getChain().
isChainValid());// Proof of Authority: only these addresses may produce blocks
Set<String> authorizedMiners = Set.of(aliceAddress, bobAddress);
BlockchainNode node = BlockchainConfig.builder()
.consensusEngine(new ProofOfAuthorityEngine(authorizedMiners))
.storage(new LevelDBStorage("/data/chain"))
.build();
node.
start();Or inject a fully custom engine:
public class VotingEngine implements ConsensusEngine {
@Override
public boolean validateBlock(Block b, Blockchain c) { /* quorum check */ }
@Override
public Block mineBlock(List<Transaction> txs, Block prev) { /* assemble */ }
@Override
public String engineName() {
return "VotingEngine";
}
}
BlockchainNode node = BlockchainConfig.builder()
.consensusEngine(new VotingEngine())
.build();Add blockchain-spring and two lines of application.yml:
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-spring</artifactId>
<version>1.0.0</version>
</dependency>blockchain:
enabled: true
difficulty: 4
chain-id: my-spring-chainBlockchainNode is created, started, and injected as a Spring bean automatically.
Override any behavior by declaring your own ConsensusEngine or BlockchainStorage bean.
// Export full chain as JSON (FR-SER-02)
String json = ChainExporter.toJson(node.getChain());
// Restore into a fresh backend — e.g. for fast node bootstrap
InMemoryStorage fresh = new InMemoryStorage();
ChainExporter.
fromJson(json, fresh);
// Export as CSV for audit / analytics (FR-SER-03)
String csv = ChainExporter.toCsv(node.getChain());
// columns: blockIndex, blockHash, txId, sender, receiver, amount, timestamp, txTypeAll modules share version 1.0.0 and group ID com.privatechain.
| Artifact ID | Depends on | Key external dep |
|---|---|---|
blockchain-core |
JDK only | — |
blockchain-crypto |
blockchain-core |
Bouncy Castle 1.84 |
blockchain-consensus |
blockchain-core, blockchain-crypto |
— |
blockchain-storage |
blockchain-core |
LevelDB JNI, RocksDB JNI |
blockchain-wallet |
blockchain-core, blockchain-crypto |
— |
blockchain-access |
blockchain-core, blockchain-crypto |
— |
blockchain-network |
blockchain-core, blockchain-crypto, blockchain-access |
Netty 4.2.13 |
blockchain-spring |
all above | Spring Boot 4.0.6 |
Core + consensus + in-memory storage (tests / prototyping)
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-core</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-consensus</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-storage</artifactId>
<version>1.0.0</version>
</dependency>Full stack with wallets and P2P networking
<!-- Wallets, crypto, consensus, LevelDB/RocksDB, Netty P2P -->
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-network</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-wallet</artifactId>
<version>1.0.0</version>
</dependency>Spring Boot application
<!-- Brings in all modules transitively -->
<dependency>
<groupId>com.privatechain</groupId>
<artifactId>blockchain-spring</artifactId>
<version>1.0.0</version>
</dependency>private-blockchain/
│
├── .github/
│ ├── workflows/
│ │ ├── build.yml # Compiles, tests, and lints on every push and pull request
│ │ ├── release.yml # Publishes artifacts to GitHub Packages on a version tag
│ │ ├── javadoc.yml # Publishes aggregate Javadoc to GitHub Pages on main push
│ │ └── qodana_code_quality.yml # Runs JetBrains Qodana static analysis
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.yml # Structured form for reporting bugs
│ └── feature_request.yml # Structured form for proposing new features
│
├── docs/
│ ├── requirements.md # Functional and non-functional requirements
│ ├── design.md # Architecture, data-flow, and class diagrams
│ ├── tasks.md # Milestone breakdown with task IDs and priorities
│ └── decisions/ # Architecture Decision Records
│ └── ADR-001-transport.md # Transport layer selection rationale
│
├── blockchain-core/ # Core library module — zero mandatory runtime dependencies
│ ├── pom.xml
│ └── src/
│ ├── main/java/com/privatechain/core/
│ │ ├── model/ # Block, BlockHeader, Transaction (abstract)
│ │ ├── spi/ # ConsensusEngine, TransactionValidator, BlockchainStorage
│ │ ├── event/ # BlockchainEventBus, BlockchainEvent (sealed), listener
│ │ ├── exception/ # BlockchainException hierarchy (all unchecked)
│ │ ├── mempool/ # TransactionMempool, FeeBasedPrioritizer, TimestampPrioritizer
│ │ ├── network/ # NodeServerLifecycle, PeerManagerLifecycle, ChainSyncer
│ │ └── builder/ # BlockchainConfig, BlockchainNode, Blockchain, GenesisBlockFactory
│ └── test/java/com/privatechain/core/
│ ├── BlockTest.java PerformanceTest.java (+ 9 other test classes)
│
├── blockchain-crypto/ # Cryptographic primitives (Bouncy Castle / secp256k1)
├── blockchain-consensus/ # PoW, PoA, PBFT, RoundRobin engines + DifficultyAdjuster
├── blockchain-storage/ # InMemory, LevelDB, RocksDB, FileSystem + ChainExporter
├── blockchain-network/ # Netty TCP P2P: PeerManager, SyncManager, GossipProtocol
├── blockchain-wallet/ # Wallet, WalletManager, KeystoreSerializer
├── blockchain-access/ # AllowlistManager, PermissionManager, InvitationService
├── blockchain-spring/ # BlockchainAutoConfiguration + BlockchainProperties
│
├── examples/
│ ├── simple-chain/ # SimpleChainDemo + MoneyTransferTransaction (T-074)
│ ├── custom-consensus/ # VotingConsensusEngine + VotingConsensusDemo (T-074)
│ └── spring-boot-demo/ # REST API over blockchain-spring autoconfiguration (T-075)
│
├── pom.xml # Parent POM; manages dependency versions and build profiles
├── qodana.yaml # JetBrains Qodana static analysis configuration
├── README.md
├── CHANGELOG.md
├── CONTRIBUTING.md
└── LICENSE # Apache License 2.0
| Engine | Class | Use case |
|---|---|---|
| Proof of Work | ProofOfWorkEngine |
General-purpose; configurable difficulty |
| Proof of Authority | ProofOfAuthorityEngine |
Permissioned networks; authorized signer set |
| PBFT | PBFTEngine |
Byzantine fault tolerance; 2f+1 quorum |
| Round-Robin | RoundRobinEngine |
Deterministic slot-based; dev/test use |
| Custom | implements ConsensusEngine |
Any logic — inject via BlockchainConfig.builder() |
| Backend | Class | Notes |
|---|---|---|
| In-memory | InMemoryStorage |
Ephemeral; for testing |
| LevelDB | LevelDBStorage |
Persistent; crash-safe |
| RocksDB | RocksDBStorage |
High write-throughput |
| File System | FileSystemStorage |
One JSON file per block; no native dep |
The blockchain-access module provides a three-layer private network gate:
Inbound TCP message
│
▼
AllowlistManager ──[DENY]──► drop + log (FR-AC-01)
│ [ALLOW]
▼
PermissionManager ──[INSUFFICIENT ROLE]──► return error
│ [AUTHORIZED] (FR-AC-02)
▼
Message handler (block / transaction / peer-discover)
Roles: NODE_ADMIN, NODE_MINER, NODE_OBSERVER.
New nodes join via a time-limited ECDSA-signed InvitationToken issued by an admin.
Register any number of BlockchainEventListener implementations at runtime:
node.getEventBus().
register(event ->{
switch(event){
case
BlockchainEvent.BlockAddedEvent e ->
System.out.
println("Block added: #"+e.block().
getIndex());
case
BlockchainEvent.TransactionSubmittedEvent e ->
System.out.
println("TX submitted: "+e.transaction().
getId());
case
BlockchainEvent.PeerConnectedEvent e ->
System.out.
println("Peer connected: "+e.peer().
nodeId());
default ->{ /* handle ForkDetectedEvent, PeerDisconnectedEvent */ }
}
});All events are delivered asynchronously from a daemon thread pool — listener execution never delays the operation that published the event.
| Document | Location | Description |
|---|---|---|
| Requirements | docs/requirements.md |
Functional and non-functional requirements (FR-01 … NFR-10) |
| Tasks | docs/tasks.md |
Milestone plan, task IDs (T-001 … T-081), priorities |
| Design | docs/design.md |
Architecture, data-flow, and class diagrams |
| ADR-001 | docs/decisions/ADR-001-transport.md |
Transport layer decision (TCP vs gRPC) |
| Javadoc | GitHub Pages | Aggregate API reference for all modules |
| Examples | examples/ |
Three runnable demos — mvn exec:java -pl examples/<name> |
# Clone
git clone https://github.com/dhruv0306/private-blockchain.git
cd private-blockchain
# Full build (compile + test + SpotBugs + JaCoCo)
mvn clean verify
# Run the simple-chain demo
mvn exec:java -pl examples/simple-chain
# Run the custom-consensus demo
mvn exec:java -pl examples/custom-consensus
# Run the Spring Boot REST demo
mvn spring-boot:run -pl examples/spring-boot-demo
# Then: curl http://localhost:8080/api/status
# curl -X POST http://localhost:8080/api/mine
# Generate aggregate Javadoc
mvn javadoc:aggregate -pl .
open target/site/apidocs/index.html
# Run OWASP Dependency-Check (requires NVD API key)
NVD_API_KEY=your-key mvn -Psecurity verifySee CONTRIBUTING.md. All pull requests must pass:
mvn clean verify(compile + test + SpotBugs + JaCoCo ≥ 80%)- Checkstyle (Google Java Style, max line 120)
- Qodana static analysis (zero new findings at ERROR severity)
Apache License 2.0 — see LICENSE.