ChainForge is a distributed blockchain network built from scratch in Go.
It implements the core components of a blockchain system, including Proof of Work mining, cryptographically signed transactions, wallet generation, balance validation, persistent storage, REST APIs, peer-to-peer synchronization, and multi-node deployment with Docker.
The project was built to explore how blockchain systems work below the framework level—from block hashing and transaction validation to distributed node communication and chain synchronization.
- SHA-256 block hashing
- Deterministic genesis block
- Proof of Work mining
- Nonce-based block validation
- Blockchain integrity verification
- Tamper detection
- Structured transactions
- Deterministic transaction IDs
- ECDSA wallet generation
- Digital transaction signatures
- Signature verification
- Balance tracking
- Overspending protection
- Coinbase/funding transactions
- Persistent blockchain storage with BoltDB
- Persistent wallet storage
- Command-line interface
- REST API
- Peer discovery and registration
- Blockchain synchronization
- Block propagation between nodes
- Multi-node Docker deployment
- Docker volumes for persistent node data
- Health checks
- Automated Go test suite
- GitHub Actions CI
ChainForge
|
+----------------+----------------+
| |
Blockchain Wallets
| |
+------+------+ +------+------+
| | | |
Blocks Transactions ECDSA Addresses
| |
| +----+----+
| | |
| Signatures Balances
|
Proof of Work
|
SHA-256 Mining
|
Persistent Storage
|
BoltDB
|
REST / P2P Layer
|
+---+-------------------+
| |
Node A Node B
:8080 :8081
| |
+------ Block Sync -----+
Each node maintains its own persistent blockchain database and communicates with peers through HTTP.
The included Docker Compose configuration launches two independent ChainForge nodes.
+------------------------+
| Node A |
| |
| HTTP: localhost:8080 |
| DB: /data/node-a.db |
+-----------+------------+
|
| block propagation
| chain synchronization
|
+-----------+------------+
| Node B |
| |
| HTTP: localhost:8081 |
| DB: /data/node-b.db |
+------------------------+
Each node:
- maintains its own blockchain
- stores data independently
- tracks peers
- validates incoming blocks
- exposes REST endpoints
- synchronizes blockchain state
- persists data across container restarts
A ChainForge block contains:
type Block struct {
Timestamp int64
Transactions []Transaction
PrevBlockHash []byte
Hash []byte
Nonce int64
}Blocks are cryptographically linked through PrevBlockHash.
Changing transaction data in an existing block invalidates its Proof of Work and breaks blockchain integrity.
ChainForge implements a SHA-256 based Proof of Work algorithm.
For each block, the miner searches for a nonce such that:
SHA256(
previous block hash +
transaction hashes +
timestamp +
difficulty +
nonce
)
produces a value below the configured target.
Conceptually:
hash < target
The resulting nonce and hash are stored in the block.
During blockchain validation, the Proof of Work is recalculated to ensure that the block has not been modified.
All ChainForge nodes begin from the same deterministic genesis block.
This is important in a distributed blockchain because independently generated genesis blocks would have different hashes and therefore represent different chains.
A shared genesis block allows independently started nodes to validate and synchronize blocks from the same network.
Transactions represent transfers between blockchain addresses.
type Transaction struct {
ID string
Sender string
Receiver string
Amount float64
PublicKey []byte
Signature []byte
}Each transaction receives a deterministic SHA-256 identifier.
ChainForge validates transaction rules including:
- sender must exist
- receiver must exist
- sender and receiver cannot be identical
- transfer values must be valid
- sender must have sufficient balance
- signed transactions must pass cryptographic verification
ChainForge generates wallets using ECDSA with the P-256 elliptic curve.
Each wallet contains:
Private Key
|
v
Public Key
|
v
Blockchain Address
Before a transaction is submitted, the sender signs the transaction using their private key.
The blockchain verifies the signature using the corresponding public key.
This prevents another wallet from authorizing transactions on behalf of the sender.
Balances are derived from blockchain transaction history.
For an address:
Balance =
incoming transactions
- outgoing transactions
Before accepting a transaction, ChainForge verifies that the sender has enough funds.
Example:
Alice balance: 50
Alice -> Bob: 10
Alice balance: 40
Bob balance: 10
An attempted transfer greater than the sender's available balance is rejected.
ChainForge uses BoltDB (bbolt) for blockchain persistence.
Each node stores blockchain data in its own database.
Examples:
chainforge.db
node-a.db
node-b.db
Docker nodes use persistent volumes:
node-a-data
node-b-data
This allows blockchain state to survive process or container restarts.
ChainForge provides a command-line interface for interacting with the blockchain.
go run . createwalletExample:
Wallet created successfully.
Address: cd57509d75b3ad744b6ea9bbcafce0820ac7bf5d
go run . balance --address ADDRESSgo run . fund --address ADDRESS --amount 50go run . send \
--from SENDER_ADDRESS \
--to RECEIVER_ADDRESS \
--amount 10go run . printchaingo run . startnode --port 8080 --db node-a.dbA second node can be started with:
go run . startnode --port 8081 --db node-b.dbgo run . sendnode \
--node http://localhost:8080 \
--from SENDER_ADDRESS \
--to RECEIVER_ADDRESS \
--amount 10Running nodes expose an HTTP API.
GET /healthExample response:
{
"blocks": 2,
"peers": 1,
"status": "ok"
}GET /chainReturns the blockchain maintained by the node.
GET /peersReturns registered peers.
A peer can be registered through:
POST /peersExample body:
{
"peer": "http://node-b:8081"
}POST /syncRequests blockchain synchronization with known peers.
POST /fundExample:
{
"address": "WALLET_ADDRESS",
"amount": 10
}ChainForge also supports submitting signed transactions to a running node through its transaction API, which is used by the sendnode CLI command.
Nodes maintain a list of known peers.
In Docker, peers communicate using service names:
node-a:8080
node-b:8081
while the host machine accesses them through:
localhost:8080
localhost:8081
When new blockchain state is produced, peer nodes can validate and synchronize the chain.
A successful propagation test results in:
Node A blocks: 2
Node B blocks: 2
without requiring both nodes to share the same database.
- Docker Desktop
- Docker Compose
docker compose up --build -dCheck container status:
docker compose psExpected result:
chainforge-node-a Up (healthy)
chainforge-node-b Up (healthy)
http://localhost:8080/health
http://localhost:8081/health
docker compose downTo remove the node data volumes as well:
docker compose down -vCreate Wallet
|
v
Fund Wallet
|
v
Node A receives transaction
|
v
Validate transaction
|
v
Check sender balance
|
v
Verify ECDSA signature
|
v
Mine block using Proof of Work
|
v
Persist block to BoltDB
|
v
Broadcast / synchronize
|
v
Node B validates blockchain state
|
v
Both nodes converge
Run the complete test suite with:
go test ./... -vThe tests cover areas including:
- genesis block creation
- blockchain initialization
- block creation
- blockchain validation
- tamper detection
- Proof of Work
- transaction validation
- deterministic transaction IDs
- transaction signatures
- invalid signature detection
- wallet generation
- balance behavior
- persistent blockchain reopening
- BoltDB storage
ChainForge uses GitHub Actions to automatically validate pushes and pull requests.
The CI pipeline performs:
Checkout
|
v
Set up Go
|
v
Download Dependencies
|
v
Formatting Check
|
v
Run Tests
|
v
Build ChainForge
Workflow:
.github/workflows/tests.yml
ChainForge/
|
|-- .github/
| `-- workflows/
| `-- tests.yml
|
|-- internal/
| `-- blockchain/
| |-- balance.go
| |-- block.go
| |-- blockchain.go
| |-- peer.go
| |-- proof_of_work.go
| |-- server.go
| |-- storage.go
| |-- transaction.go
| |-- wallet.go
| |-- wallet_store.go
| |
| |-- blockchain_test.go
| |-- persistence_test.go
| |-- proof_of_work_test.go
| |-- signature_test.go
| |-- storage_test.go
| |-- transaction_test.go
| `-- wallet_test.go
|
|-- .dockerignore
|-- .gitignore
|-- compose.yml
|-- Dockerfile
|-- go.mod
|-- go.sum
|-- LICENSE
|-- main.go
`-- README.md
| Area | Technology |
|---|---|
| Language | Go |
| Hashing | SHA-256 |
| Digital Signatures | ECDSA P-256 |
| Persistence | BoltDB / bbolt |
| Networking | Go net/http |
| API | REST |
| Containers | Docker |
| Multi-node orchestration | Docker Compose |
| Testing | Go testing |
| CI | GitHub Actions |
ChainForge demonstrates several foundational distributed-system and blockchain concepts:
- cryptographic hashing
- immutable hash-linked data structures
- Proof of Work
- deterministic network genesis state
- asymmetric cryptography
- digital signatures
- transaction authorization
- state derived from transaction history
- persistent storage
- peer communication
- distributed state synchronization
- containerized multi-node deployment
ChainForge is an educational implementation intended to demonstrate blockchain and distributed-systems engineering concepts. It is not intended for production cryptocurrency or financial use.
Potential extensions include:
- Merkle trees
- UTXO-based transaction accounting
- transaction mempool
- configurable mining difficulty
- mining rewards
- richer peer discovery
- fork resolution and chain-selection policies
- more advanced consensus mechanisms
- authenticated node communication
- observability and metrics
This project is licensed under the terms provided in the repository's LICENSE file.