This project is a simple example of Proof of work (PoW) protected TCP server. It implements challenge-response protocol and uses hashcash algorithm.
The server and client communicate using an internal messaging protocol. Each message ends with the \n
character. It's used to separeate messages from each other.
A message consists of a command and a payload. They are separated by the :
character. The payload can be any string without \n
character. It's not very convenient in real life, but in this project all payloads are fixed and don't contain \n
character.
Supported commands:
0
-Error
(server -> client);1
-RequestPuzzle
(client -> server);2
-ResponsePuzzle
(server -> client);3
-RequestResource
(client -> server);4
-ResponseResource
(server -> client).
A messaging is implemented in the message
package.
PoW is implemented with a challenge-response protocol:
-
The client establishes a tcp connection with the server. The server starts to listening to client messages.
-
The client sends the
RequestPuzzle
command to receive a puzzle from server.Message:
1:\n
. -
The server generates a new puzzle using a hashcash algorithm, stores a puzzle in the cache with some TTL and sends the
ResponsePuzzle
command with this puzzle to the client.Message:
2:puzzle\n
. -
The client receives a puzzle and tries to compute a puzzle hash with enough number of zero bits in the beggining. Than the client requests a resource sending a solved puzzle in the
RequestResource
command.Message:
3:solved-puzzle\n
. -
The server receives the solved puzzle, checks TTL and sends
ResponseResource
command with some resource if that puzzle was solved correctly.Message:
4:some-resource\n
.
Implementation:
- Go 1.21+;
- Docker and docker-compose.
# Run client and server just for demo
$ docker-compose up
# Run server listening on 8080 port
$ docker-compose up -d server
$ make help
Usage: make [command]
Commands:
build-server Build server app
build-client Build client app
run-server Run server app
run-client Run client app
test Run tests
fmt Format code
# Build server
$ go build -o ./bin/server ./cmd/server/*.go
# Build client
$ go build -o ./bin/client ./cmd/client/*.go
# Run server
$ ./bin/server
# Run client
$ ./bin/client
Server and client applications support configuration from .yaml
or .env
files or from environment variables. Applications use default configuration if a custom configuration not passed.
Server
# Run server passing yaml config file
$ ./bin/server --config config.yaml
# Run server passing env config file
$ ./bin/server --config config.env
# Or run server using environment variables
$ ./bin/server
Client
# Run client passing yaml config file
$ ./bin/client --config config.yaml
# Run client passing env config file
$ ./bin/client --config config.env
# Or run client using environment variables
$ ./bin/client
Templates are available in the config folder.