Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSet 2 server implementation with difficulty adjustment and forking #23

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Copyright 2018 Avery Lamp

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

115 changes: 115 additions & 0 deletions projects/pset2.0_Forkable_Difficulty_Adjusting/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# pset2.0 (forkable, difficulty adjusting, simulation ready)
Made by Avery Lamp and Faraz Nadeem

In this folder there is a backend server that functions similarly to the backend used for pset 2,
with many added features detailed below.

We have also added a visualizer for the blockchain created by mining on the server.

Finally we have included a couple different implementations of different miners to use on the server
or explore.

### Quick Redirects

[Python Server](server-python/)

[Visualization Code (Python)](blockchain-visualizer/)

[Multicore Miner (Golang)](multicore-miner-go/)

[Singlecore Miner (Python)](singlecore-miner-python/)

[Multicore Miner (Python)](multicore-miner-python/)

[Cuda Miner (c, cu)](gpu-cuda-miner-cu/)


## Server

Built with python, this flask server supports a centralized blockchain with a couple of basic features.

A full readme for the server [can be found here](server-python/)

### Server Endpoints

**Add Block -** `/addblock/<Previous Hash>/<name>/<nonce>`
> @param `Previous Hash` - The string hash to mine off of
> @param `name` - The string miner name
> @param `nonce` - The varied nonce
> Blocks are hashed as the string sha256("<Previous Hash> <name> <nonce>")
> - Attempts to add a block pointing to Previous Hash.
> - Will return error messages for different kinds of invalid blocks

**Get Tip -** `/getlatest/`
> - Returns the tip of the main chain

**Get Block -** `/getblock/<Block Hash>/`
> @param `Block Hash` - The string hash of the block to return
> - Returns the information included in the block specified

**Get All Blocks -** `/getallblocks/`
> - Returns a list of all blocks in the system, sorted by timestamp (most recent)

**Get All Tips -** `/getalltips/`
> - Returns a list of all blocks that do not have any other block pointing to it, sorted by height
> - Allows a user to find all existing chains

**Get Main Chain -** `/getchain/`
> - Returns the main chain in a simplified format

**Get Specific Chain -** `/getchain/<Block Hash>`
> @param `Block Hash` - The string hash of the chain to return
> - Returns the chain from the orign to the specified block with `Block Hash`

#### Block Difficulty

To modify how the server adjust block difficulty, modify the function `calculate_target_work_for_block` (line 165)
that gets called after every new block. There are two sample implementations `monero_difficulty` and `bitcoin_difficulty` that can be used to test out monero-like and bitcoin-like difficulty adjustments with different parameters.

(note) - difficulty is in number of leading zeros plus the geometric sum of the next 10 inverted bits multiplied by their term in the geometric series `1 / 2^n`.

The complete implementation of difficulty checking can be found in the `hash_block_information` function (line 286).

#### Forkable

To fork a chain, simply add blocks pointing to any hash that already exists in the chain.

## Multicore Golang Miner

The full readme for the miner [can be found here](multicore-miner-go/)

The multi-core Golang miner that we made to test out the server utilizes many different configurations with command-line arguments to run.

The multi-core Golang miner utilizes channels to efficiently use all processing power on a computer and schedule multi-threaded programming tasks

#### Installation

`go build main.go miner.go client.go`

The buildscript will create an executable file `main`, that can be run with the command line arguments

#### Usage

(note) - default miner name is specified in code and needs to be updated

Mine from the main tip single core (query for new tips continuously)

`./main`

Mine from the main tip multi-core (<num of cores>)

`./main <num of cores>`

Mine from specified block

`./main <Block Hash> <Target Difficulty> <Miner Name> <num of cores>`

> @params `Block Hash` - The hash of the block to start mining from

> @params `Target Difficulty` - The target difficulty of the block (must match with the server's target difficulty)

> @params `Miner Name` - Specify a different miner name

> @params `num of cores` - The number of cores to mine with


Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Visualization

The blockchain visualizer allows you to create graphs and plots of different blockchains/chains and their growth in height over time.

The blockchain visuzlizer uses the pickle file that is serialized by the server every minute for data to be saved.

The path to the pickle file *must be updated* to the directory of the server to graph the latest data.

To graph a secondary chain (attacker chain), the tip of the chain must be added to the variable forked_block before running the script
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pickle
from datetime import datetime
from datetime import timedelta
import matplotlib.pyplot as plt
import graphviz

def mine_rate_info(endpoint_block, origin_block, block_information, time_interval):
endpoint_dt = datetime.fromtimestamp(highest_block[0]['timestamp'])
origin_dt = datetime.fromtimestamp(block_information[origin_block]['timestamp'])
block_hash = endpoint_block

num_buckets = int((endpoint_dt - origin_dt).total_seconds() / time_interval) + 5
mined_buckets = [0]*num_buckets
times_list = [origin_dt + timedelta(seconds=x*time_interval) for x in range(0, num_buckets)]
assert len(times_list) == len(mined_buckets)

while block_hash != '':
block_info = block_information[block_hash]
timestamp = block_information[block_hash]['timestamp']
dt = datetime.fromtimestamp(timestamp)
bucket_ind = int((dt - origin_dt).total_seconds() / time_interval)
mined_buckets[bucket_ind] += 1

block_hash = block_info['blockInformation']['previousHash']

return times_list, mined_buckets

def aggregate_info(mined_buckets):
num_buckets = len(mined_buckets)
aggregate_buckets = [0]*num_buckets

for i in range(num_buckets):
if i == 0:
aggregate_buckets[0] = mined_buckets[0]
else:
aggregate_buckets[i] = aggregate_buckets[i-1] + mined_buckets[i]
return aggregate_buckets

def generate_graphviz(block_information):
g = graphviz.Digraph('G', filename='block_information.gv')
g.node("origin", "")
for block_hash in block_information:
g.node(block_hash, "")
prev_hash = block_information[block_hash]['blockInformation']['previousHash']
if prev_hash == '':
prev_hash = "origin"
g.edge(prev_hash, block_hash)
g.view()

block_information = pickle.load(open("../server-python/block_information.pickle", 'rb'))
highest_block = pickle.load(open("../server-python/highest_block.pickle", 'rb'))

print("Creating graphviz...")
# generate_graphviz(block_information)
print("Done.")
# exit()

# block height 0: 6c179f21e6f62b629055d8ab40f454ed02e48b68563913473b857d3638e23b28
origin_block = "6c179f21e6f62b629055d8ab40f454ed02e48b68563913473b857d3638e23b28"
forked_block = "00001d87846888b85e4b9b757b59a936b0ff33d8128518c78efaa092572efbfd" # Put the hash of another tip here to graph it as well
endpoint_block = highest_block[0]['blockHash']

print(endpoint_block)

time_interval = 0.5 # seconds

times_list, mined_buckets = mine_rate_info(endpoint_block, origin_block, block_information, time_interval)
forked_times_list, forked_mined_buckets = mine_rate_info(forked_block, origin_block, block_information, time_interval)
aggregate_buckets = aggregate_info(mined_buckets)
forked_aggregate_buckets = aggregate_info(forked_mined_buckets)
print("Plotting data...")
# line1, = plt.plot(times_list, mined_buckets, label="blocks mined / {}s".format(time_interval))
line2, = plt.plot(times_list, aggregate_buckets, label="total blocks mined")
# line3, = plt.plot(times_list, forked_mined_buckets, label="attacker blocks mined / {}s".format(time_interval))
line4, = plt.plot(times_list, forked_aggregate_buckets, label="attacker total blocks mined")
plt.legend(handles=[line2, line4])
plt.show()
print("Done")
Loading