Skip to content

Commit

Permalink
added server and refactored classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Kelly authored and Robert Kelly committed Jul 16, 2019
1 parent 0babc56 commit 358c27f
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 68 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
__pycache__
.python-version
92 changes: 92 additions & 0 deletions block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from hashlib import sha256
import json
import pickle
import time

from transaction import Coinbase, Transaction, generate_test_transaction


class Block(object):
def __init__(self, creator: str, prev_block_header_hash: bytes=None):
self.nonce = 0
self.transactions = [Coinbase(creator)]
self.previous_block_header_hash = prev_block_header_hash
self.time = bytes(int(time.time()))
self.block_header_hash = None
self.merkle_root = None
self.header_hash = None

def add_transaction(self, transaction: Transaction):
self.transactions.append(transaction)
self.header_hash = None
self.merkle_root = None

def increment_nonce(self):
self.nonce += 1

def set_time(self, current_time: bytes):
self.time = current_time

def get_header_hash(self):
m = sha256()
m.update(self.merkle_root)
m.update(self.time)
m.update(self.previous_block_header_hash)
m.update(bytes(self.nonce))
self.header_hash = m.digest().hex()
return self.header_hash

def get_merkle_root(self):
# This is a simple implementation of finding the merkle root for the transactions
transaction_strings = list(sorted([t.dumps() for t in self.transactions]))
if len(transaction_strings) % 2 != 0:
transaction_strings.append(b'')
while True:
temp = []
for i in range(0, len(transaction_strings), 2):
transaction_strings[i]
transaction_strings[i + 1]
m = sha256()
m.update(transaction_strings[i] + transaction_strings[i + 1])
temp.append(m.digest())
transaction_strings = temp
temp = []
if len(transaction_strings) == 1: break
self.merkle_root = transaction_strings[0]
return self.merkle_root

def dumps(self):
return pickle.dumps(self)

@staticmethod
def loads(data):
return pickle.loads(data)

class GenesisBlock(Block):
def __init__(self):
super(GenesisBlock, self).__init__(b'Noone', b'Nothing')
self.block_header_hash = b'fafa'

def get_merkle_root(self):
self.merkle_root = b'this is the beginning'
return self.merkle_root


def test():
t = generate_test_transaction(random=False)
b = Block('3a1bcfa12a29a7ed68b1c70743a104cc37e6ae8e2c94653fcc1093707a62c448f98ac599df92d392a9f56c2a46bca5a375b9996f321c490b2e92c7c71cf1e134', b'')
b.add_transaction(t)
b.get_merkle_root()
b.set_time(b'10000000')
assert b.get_header_hash() == '66133b9317522b44b4a0acce652b8efb561b0e892f7a1b0e4ba603848f3f2ac1'
c = b.dumps()
b = Block.loads(c)
assert b.get_header_hash() == '66133b9317522b44b4a0acce652b8efb561b0e892f7a1b0e4ba603848f3f2ac1'

gb = GenesisBlock()
gb.get_merkle_root()
gb.get_header_hash()


if __name__ == '__main__':
test()
47 changes: 47 additions & 0 deletions blockchain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import Dict, List

from block import Block, GenesisBlock
from transaction import Transaction


class Blockchain(object):
accounts: Dict[str, int] = {}
contracts: Dict[str, int] = {}
blocks: List[Block] = []
difficulty = '00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'

def __init__(self, creator: str, prev_blocks: List[Block]=None):
if prev_blocks is None:
self.prev_blocks = [GenesisBlock()]
else:
self.prev_blocks = [prev_blocks]
self.current_block = Block(creator, self.prev_blocks[-1].block_header_hash)
self.prev_block = self.prev_blocks[-1]

def verify_transaction(self, transaction: Transaction) -> bool:
return transaction.sender in self.accounts and transaction.amount < self.accounts[transaction.sender]

def verify_block(self) -> bool:
for transaction in self.current_block.transactions:
assert transaction.verify()
assert block.get_merkle_root == block.merkle_root
assert int(block.get_header_hash(), 16) < int(difficulty, 16)
return True

def mine_block(self) -> bool:
self.current_block.get_merkle_root()
block_hash = self.current_block.get_header_hash()
if int(block_hash, 16) < int(self.difficulty, 16):
# meets difficulty requirement!!
self.prev_blocks.append(self.current_block)
self.current_block = Block()
return True
return False


def test():
b = Blockchain('3a1bcfa12a29a7ed68b1c70743a104cc37e6ae8e2c94653fcc1093707a62c448f98ac599df92d392a9f56c2a46bca5a375b9996f321c490b2e92c7c71cf1e134')
b.mine_block()

if __name__ == '__main__':
test()
71 changes: 3 additions & 68 deletions contract.py
Original file line number Diff line number Diff line change
@@ -1,76 +1,11 @@
from typing import Dict, List
from hashlib import sha256
import json
import time

from opcodes import *


class Transaction(object):
def __init__(
self,
sender,
receiver,
amount,
):
self.sender = sender
self.receiver = receiver
self.amount = amount

def dumps(self):
return {
'from': self.sender,
'to': self.receiver,
'amount': self.amount,
}


class Block(object):
def __init__(self, prev_block_hash):
self.nonce = 0
self.transactions = []
self.previous_block_hash = prev_block_hash
self.block_hash = None

def generate_json_string(self):
return json.dumps({
'previous_block_hash': self.previous_block_hash,
'nonce': self.nonce,
'transactions': [t.dumps() for t in self.transactions]
}).encode('utf-8')

def add_transaction(self, transaction: Transaction):
self.transactions.append(transaction)

def increment_nonce(self):
self.nonce += 1

def hash(self):
return sha256(self.generate_json_string()).hexdigest()


class Blockchain(object):
accounts: Dict[str, int] = {}
contracts: Dict[str, int] = {}
blocks: List[Block] = []
difficulty = '000'

def __init__(self):
self.current_block = Block()

def verify_transaction(self, transaction: Transaction) -> bool:
return transaction.sender in self.accounts and transaction.amount < self.accounts[transaction.sender]

def verify_block(self) -> bool:
for transaction in self.current_block.transactions:
if self.sender not in accounts or accounts[self.sender] < self.amount:
return False
return True

def mine_block(self):
block_hash = self.current_block.hash()
if block_hash[:len(difficulty)] != difficulty:
return False
return True
from signatures import *
from transaction import Transaction


class SmartContract(object):
Expand Down
67 changes: 67 additions & 0 deletions miner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from multiprocessing import Process
import json

from flask import Flask, Response
import requests

from blockchain import Blockchain


app = Flask(__name__)

@app.route('/ping')
def home():
return 'I am awake still'

@app.route('/add_peer', methods=['PUT'])
def add_peer():
data = json.dumps(request.json)
peer = data['peer']

if __name__ == "__main__":
app.run(debug=True)


class Miner(object):
def __init__(self, blockchain: Blockchain):
self.peers = []
self.blockchain = blockchain
self.paused = False

def toggle(self):
self.toggle = not self.toggle

def add_peer(self, peer):
self.peers.append(peer)

def validate_peer(self, peer):
r = requests.get(peer + '/ping')
if r.status_code == 200:
return True
return False

def validate_peers(self):
self.peers = list(filter(lambda x: self.validate_peer(x), self.peers))

def send_block_to_peers(self):
for peer in self.peers:
r = requests.put(peer + '/block', json=self.blockchain.prev_blocks[-1])

def mine(self):
while True:
print('iter')
if self.toggle:
print('TEST!!!')
# This can be used to periodically trigger things
self.toggle()

x = self.blockchain.mine_block()
if x == True:
print('FOUND BLOCK!!!!')
self.validate_peers()
self.send_block_to_peers(self, self.blockchain.prev_blocks[-1])



if __name__ == '__main__':
m = Miner(Blockchain())
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ecdsa==0.13.2
Flask==1.1.1
requests==2.22.0
32 changes: 32 additions & 0 deletions server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from multiprocessing import Process
import json

from flask import Flask, Response
import requests

from blockchain import Blockchain
from miner import Miner


app = Flask(__name__)
m = Miner(Blockchain('3a1bcfa12a29a7ed68b1c70743a104cc37e6ae8e2c94653fcc1093707a62c448f98ac599df92d392a9f56c2a46bca5a375b9996f321c490b2e92c7c71cf1e134'))
p = Process(target=m.mine)

@app.route('/ping')
def home():
return 'I am awake still'

@app.route('/add_peer', methods=['PUT'])
def add_peer():
data = json.dumps(request.json)
peer = data['peer']
m.add_peer(peer)

@app.route('/toggle')
def toggle():
m.toggle()


if __name__ == "__main__":
p.start()
app.run(debug=True)
42 changes: 42 additions & 0 deletions signatures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import binascii

import ecdsa


def generate_keypair():
sk = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
vk = sk.get_verifying_key()
return sk.to_string().hex(), vk.to_string().hex()


def sign_message(message, sk_string):
try:
sk_bytes = binascii.unhexlify(sk_string)
sk = ecdsa.SigningKey.from_string(sk_bytes, curve=ecdsa.SECP256k1)
except:
raise Exception('Invalid Signing Key. Try again with Hex!')
sig = sk.sign(message)
return sig.hex()


def verify(message, sig, vk_string):
vk_bytes = binascii.unhexlify(vk_string)
vk = ecdsa.VerifyingKey.from_string(vk_bytes, curve=ecdsa.SECP256k1)
sig_bytes = binascii.unhexlify(sig)
try:
# Good Signature
return vk.verify(sig_bytes, message) # True
except ecdsa.keys.BadSignatureError:
# Bad Signature
return False


def test():
sk, vk = generate_keypair()
sig = sign_message(b'test', sk)
assert verify(b'test', sig, vk) == True
print('Tests Passed')


if __name__ == '__main__':
test()
Loading

0 comments on commit 358c27f

Please sign in to comment.