-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Robert Kelly
authored and
Robert Kelly
committed
Jul 16, 2019
1 parent
0babc56
commit 358c27f
Showing
10 changed files
with
415 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.DS_Store | ||
__pycache__ | ||
.python-version |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Oops, something went wrong.