Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build/
go.sum
wallet_*
simpleBlockchain_*.db
blockchain/
22 changes: 22 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "build",
"command": "go build cmd/cli/main.go && mkdir -p build && mv main build/main",
"problemMatcher": [
"$go"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"presentation": {
"reveal": "silent",
"revealProblems": "onProblem",
"close": true
}
}
]
}
95 changes: 81 additions & 14 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,95 @@
Simple Blockchain
------
# Simple Blockchain

This repository contains the golang code of a simple blockchain implementation.

This blockchain consists of three parts:
- A simple wallet that you can get address, scan utxos, sign transaction.
- A simple blockchain can sync block from other known nodes, mining new block, send transaction and broadcast to other node.
- A simple restful server you can query blocks and utxos from blockchain.
- This project only supports two nodes.

There are many part are not like real blockchain because it's just simple implementation, still
insecure and incomplete. you can learn the basic operation of the blockchain through this project.

## changes from zweieuro (author):
- The peers were saved in an array, but there are only ever 2
- reduced to a single string, making management easier
- removed file that wrote the peers down, no reason to have this in this case, looks like it has no other function, removed dead code
- Make the peers' and owns' address with respect to port or address:port arguments
- Same functionality as before when only the port is specified


### Docker compose users:

I re-implemented some of the addressing (really it was mostly arg parsing) so that the node and its peer
can have independent base addresses. This means they are now docker container compatible.

The folder `docker_compose` shows how this might be used in your `docker-compose.yml` file.
The `docker-compose.yml` expects `existingWallets/` to have two files:
- `alice_wallet`
- `bob_wallet`

WARNING: If the wallet files are missing docker will attempt to bind directories instead! This will also crash. Additionally docker might make them write-protected. Remove them at your own discretion.


Of course you can rename the files as you want if you change their occurrences.

These wallets are copied to the individual container before they start up, they will instantly crash without them.


When binding, in order to retain DB information, both DB files are linked back into a folder for each node. This is mostly due to docker volume limitations as dual binding is not really a good idea (binding the same folder of the host to two different sub-containers), especially since we _want_ separate files.



#### Docker compose address resolution
Containers that run in docker and are declared inside a `docker-compose.yml` have a built-in hostname resolution that can make them find other containers in the same service file. They are automatically available to any container. This is what this example files uses to route data between the two node instances.


## Running commands without compiling your own version:
Since you already have a node running inside docker containers, it seems a bit backwards to then compile again just to talk to it.
You can either: Start a shell inside your container and run it from there, which i find quite cumbersome.

alternatively: You can run the commands raw. The commands are specified in `servercmd.go` and their respective endpoint can be found in `conn.go`.
This has the nice effect of letting us craft our own requests.
Here we also see the main security problem, the server is very unprotected, these requests have no kind of confirmation.

One raw curl request might look like this:
```shell
curl -i -X POST \
-H "Content-Type:application/json" \
-d \
'{
"To": "1LHroFft5WAxZTqXizQJjBJrPwkVQFAcsa",
"Amount": 200
}' \
'http://localhost:7080/wallet/send'

```

Which read quite easily. Transfer 200 coins to the given public key.

Mining is now a simple get request:
```shell
curl -i -X GET \
'http://localhost:7080/chain/mining'
```



# How to run

How to run
------

## Build

Install deps:


```shell script
go mod tidy
```

build it:

```shell script
go build ./cmd/cli
```
Expand All @@ -28,11 +101,11 @@ go build ./cmd/cli
./cli wallet create -walletname "bob"
```

### Start two node
### Start two nodes/start server commands

```shell script
./cli server start -nodeport 3000 -apiport 8080 -walletname "alice" -ismining=true
./cli server start -nodeport 3001 -apiport 8081 -walletname "bob" -ismining=true
./cli server start -nodeaddress 3000 -peernodeaddress 3001 -apiport 8080 -walletname "alice" -ismining=true
./cli server start -nodeaddress 3001 -peernodeaddress 3000 -apiport 8081 -walletname "bob" -ismining=true
```

### Mining empty block to get block reward
Expand All @@ -45,7 +118,7 @@ go build ./cmd/cli
./cli server sendtransaction --apiport 8080 --to "172wJyiJZxXWyBW7CYSVddsR5e7ZMxtja9" -amount 100000
```

threr are still have other blockchain command, you can find out by type `./cli server`.
There are still have other blockchain command, you can find out by type `./cli server`.


Example
Expand All @@ -57,12 +130,6 @@ Example
./cli wallet create -walletname "alice"
```

### Start blockchain server

```shell script
./cli server start -nodeport 3000 -apiport 8080 -walletname "alice" -ismining=true
```

### Get blocks

```shell script
Expand Down
19 changes: 16 additions & 3 deletions blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/boltdb/bolt"
"log"
"math/big"
"os"
"sync"

"github.com/boltdb/bolt"
)

var genesisBlock = &Block{
Expand Down Expand Up @@ -70,9 +72,9 @@ var genesisBlock = &Block{
},
}

var dbFolderName = "blockchain"


var dbSigName = "simpleBlockchain_%d.db"
var dbSigName = dbFolderName + "/simpleBlockchain_%d.db"

type BlockChain struct {
db *bolt.DB
Expand Down Expand Up @@ -128,6 +130,17 @@ func NewBlockChain(address string, port int, isMining bool) *BlockChain {

func CreateBlockChain(address string, port int, isMining bool) *BlockChain {
dbName := fmt.Sprintf(dbSigName, port)

// create the folder if it does not exist
stat, err := os.Stat(dbFolderName)

if err != nil || stat == nil || stat.IsDir() == false {
err := os.Mkdir(dbFolderName, 0755)
if err != nil {
panic(err)
}
}

db, err := bolt.Open(dbName, 0600, nil)
if err != nil {
panic(err)
Expand Down
15 changes: 10 additions & 5 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import (
)

var (
nodeportFlag = &cli.IntFlag{
Name: "nodeport",
Usage: "nodeport",
Value: 3000,
peernodeaddressFlag = &cli.StringFlag{
Name: "peernodeaddress",
Usage: "partner node address, either addr:port or just port",
Required: true,
}
nodeaddressFlag = &cli.StringFlag{
Name: "nodeaddress",
Usage: "what address is this node reachable under, addr:port or port (localhost)",
Required: true,
}
apiportFlag = &cli.IntFlag{
Name: "apiport",
Expand All @@ -31,7 +36,7 @@ var (
isminingFlag = &cli.BoolFlag{
Name: "ismining",
Usage: "ismining",
Required: true,
Value: false,
}
toFlag = &cli.StringFlag{
Name: "to",
Expand Down
25 changes: 20 additions & 5 deletions cmd/servercmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,44 @@ package cmd

import (
"fmt"
"os"
"strconv"

"github.com/tn606024/simpleBlockchain"
"github.com/urfave/cli/v2"
"os"
)

var (
startSubCommand = &cli.Command{
Name: "start",
Usage: "start blockchain server",
Description: "start blockchain server",
ArgsUsage: "<nodeport><apiport><walletname><ismining>",
ArgsUsage: "<nodeaddress> <peernodeaddressflag> <walletname> <apiport = 8080> <ismining = false>",
Flags: []cli.Flag{
nodeportFlag,
nodeaddressFlag,
peernodeaddressFlag,
apiportFlag,
walletnameFlag,
isminingFlag,
},
Action: func(c *cli.Context) error {
nodeport := c.Int("nodeport")
// check if peerNodeAddressFlag is just a number (port)
// if so, prepend "localhost:" to it
peerNodeAddress := c.String("peernodeaddress")
if _, err := strconv.Atoi(peerNodeAddress); err == nil {
peerNodeAddress = "localhost:" + peerNodeAddress
}

nodeaddress := c.String("nodeaddress")
if _, err := strconv.Atoi(nodeaddress); err == nil {
nodeaddress = "localhost:" + nodeaddress
}


apiport := c.Int("apiport")
walletname := c.String("walletname")
ismining := c.Bool("ismining")
server := simpleBlockchain.NewServer(nodeport, apiport, walletname, ismining)
server := simpleBlockchain.NewServer(nodeaddress, peerNodeAddress, apiport, walletname, ismining)
server.StartServer()
return nil
},
Expand Down
1 change: 1 addition & 0 deletions docker_compose/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
blockchain_*/
13 changes: 13 additions & 0 deletions docker_compose/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM golang:1.23

WORKDIR /app

RUN git clone https://github.com/ZweiEuro/simpleBlockchain


WORKDIR /app/simpleBlockchain
# download dependencies

RUN go mod tidy

RUN go build ./cmd/cli
29 changes: 29 additions & 0 deletions docker_compose/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
services:
blockchain_alice:
build:
context: ./
dockerfile: ./Dockerfile
container_name: blockchain_alice
expose:
- "3000" # nodeaddress, only needed between nodes
ports:
- "7080:8080" # apiport, accessed from your command line
volumes:
- ./blockchain_alice/:/app/simpleBlockchain/blockchain/
- ./existingWallets/wallet_alice:/app/simpleBlockchain/wallet_alice
command: ./cli server start -nodeaddress blockchain_alice:3000 -peernodeaddress blockchain_bob:3000 -walletname alice -ismining=true
blockchain_bob:
build:
context: ./
dockerfile: ./Dockerfile
container_name: blockchain_bob
expose:
- "3000" # nodeaddress, only needed between nodes
ports:
- "7081:8080" # apiport, accessed from your command line
volumes:
- ./blockchain_bob/:/app/simpleBlockchain/blockchain/
- ./existingWallets/wallet_bob:/app/simpleBlockchain/wallet_bob
command: ./cli server start -nodeaddress blockchain_bob:3000 -peernodeaddress blockchain_alice:3000 -walletname bob


Empty file.
Loading