├── .gitignore ├── LICENSE ├── README.md ├── chain.py ├── client.py ├── config.py ├── funcs.py ├── gen_transaction.py ├── gen_transfer.py ├── lazyClientServer.js ├── miner.py ├── public ├── index.html └── js │ └── data.js ├── requirements.txt ├── tracker.py └── user.py /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | *.pyc 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Param Singh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain for lazycoin 2 | 3 | An implementation of a blockchain used to manage transactions in a new cryptocurrency called lazycoin. 4 | 5 | The blockchain uses RSA public and private keys for signing transactions and SHA-256 for hashing transactions and blocks. 6 | 7 | Signed transactions and blocks are broadcasted as objects using pickle over the network. 8 | 9 | The blockchain is completely autonomous except for a tracker for broadcasting objects among peers. 10 | 11 | Redis is used to maintain the block chain in all peers and hash pointers to verify transaction. 12 | 13 | Miners compete over a Hash Puzzle which is made using SHA-256 Hash digest of outstanding transactions to find the nonce. 14 | 15 | This was a project for a 2 day Hackathon that took place in NITH during Nimbus 2017. 16 | 17 | 18 | ## Authors 19 | 20 | LazyLeaves 21 | 22 | * [Param Singh](https://github.com/paramsingh) 23 | * [Abhishek Rastogi](https://github.com/Princu7) 24 | * [Shikhar Srivastava](https://github.com/shikharsrivastava) 25 | -------------------------------------------------------------------------------- /chain.py: -------------------------------------------------------------------------------- 1 | from redis import Redis 2 | import rsa 3 | from hashlib import sha256 4 | import json 5 | from config import * 6 | 7 | 8 | class Transaction(object): 9 | 10 | def __init__(self, prev_hash, transaction_type, sender, receiver): 11 | """ Constructor for creating new transaction 12 | """ 13 | self.prev_hash = prev_hash 14 | self.transaction_type = transaction_type 15 | self.sender = sender 16 | self.receiver = receiver 17 | 18 | 19 | def add_signature(self, signature): 20 | """ Add signature to the transaction """ 21 | self.signature = signature 22 | 23 | @property 24 | def message(self): 25 | return json.dumps(self.to_dict(), sort_keys=True).encode('utf-8') 26 | 27 | @property 28 | def hash(self): 29 | return sha256(json.dumps(self.to_dict(), sort_keys=True).encode('utf-8')).hexdigest() 30 | 31 | def to_dict(self): 32 | """ 33 | Converts the transaction data into a serializable json format. 34 | """ 35 | return { 36 | 'prev_hash': self.prev_hash, 37 | 'transaction_type': self.transaction_type, 38 | 'sender': { 39 | 'n': self.sender.n, 40 | 'e': self.sender.e, 41 | }, 42 | 'receiver': { 43 | 'n': self.receiver.n, 44 | 'e': self.receiver.e, 45 | }, 46 | } 47 | 48 | 49 | def to_redis(self): 50 | """ 51 | Converts the entire transaction into a json format that can be put into redis 52 | """ 53 | 54 | return { 55 | 'data': self.to_dict(), 56 | } 57 | 58 | def write_to_redis(self, r, key): 59 | r.rpush(key, json.dumps(self.to_redis(), sort_keys=True)) 60 | sig_key = "{}{}".format(TRANSACTIONS_SIGNATURE, self.hash) 61 | print("signature key for transaction = " + sig_key) 62 | r.set(sig_key, self.signature) 63 | 64 | 65 | def verify(self): 66 | """ Verifies the signature of transaction 67 | """ 68 | try: 69 | rsa.verify(self.message, self.signature, self.sender) 70 | except VerificationError: 71 | print("Verification failed", file=sys.stderr) 72 | return False 73 | 74 | return True 75 | 76 | 77 | @classmethod 78 | def from_redis(cls, redis, payload): 79 | """ Factory to create a Transaction object from redis 80 | """ 81 | print("in from redis") 82 | obj = cls( 83 | prev_hash=payload['data']['prev_hash'], 84 | transaction_type=payload['data']['transaction_type'], 85 | sender=rsa.key.PublicKey(payload['data']['sender']['n'], payload['data']['sender']['e']), 86 | receiver=rsa.key.PublicKey(payload['data']['receiver']['n'], payload['data']['receiver']['e']), 87 | ) 88 | key = '{}{}'.format(TRANSACTIONS_SIGNATURE, obj.hash) 89 | print(key) 90 | obj.add_signature(redis.get(key)) 91 | print("Hello") 92 | return obj 93 | 94 | 95 | class Block(object): 96 | 97 | def __init__(self, prev_hash): 98 | self.prev_hash = prev_hash 99 | self.transactions = [] 100 | self.signatures = [] 101 | 102 | 103 | def full(self): 104 | return len(self.transactions) >= TRANSACTIONS_IN_BLOCK 105 | 106 | 107 | def add_transaction(self, transaction): 108 | self.transactions.append(transaction) 109 | self.signatures.append(transaction.signature) 110 | 111 | def to_json(self): 112 | payload = { 113 | 'nonce': self.nonce, 114 | 'prev_hash': self.prev_hash, 115 | 'transactions': [], 116 | } 117 | for t in self.transactions: 118 | payload['transactions'].append(t.to_redis()) 119 | return payload 120 | 121 | 122 | def add_nonce(self, nonce): 123 | self.nonce = nonce 124 | 125 | def to_redis(self): 126 | data = self.to_json() 127 | data['hash'] = self.hash 128 | return data 129 | 130 | 131 | @property 132 | def hash(self): 133 | return sha256(json.dumps(self.to_json(), sort_keys=True).encode('utf-8')).hexdigest() 134 | 135 | 136 | @classmethod 137 | def from_json(cls, payload): 138 | obj = cls(payload['prev_hash']) 139 | for transaction in payload['transactions']: 140 | obj.transactions.append(Transaction.from_json(transaction)) 141 | obj.add_nonce(payload['nonce']) 142 | return obj 143 | 144 | def verify(self): 145 | acc = '' 146 | for t in self.transactions: 147 | acc += str(t.hash) 148 | 149 | return int(sha256((str(self.nonce)+acc).encode('utf-8')).hexdigest()[0:6],16) < GAMER_BAWA 150 | 151 | 152 | if __name__ == '__main__': 153 | pass 154 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import funcs 3 | import threading 4 | import json 5 | from redis import Redis 6 | import time 7 | import sys 8 | from chain import Transaction, Block 9 | import chain 10 | from user import LazyUser 11 | from config import * 12 | from miner import Miner 13 | import pickle 14 | 15 | 16 | prev_hash = 'block hash of genesis' 17 | redis_connection = Redis() 18 | 19 | 20 | def miner_thread(sock, User): 21 | miner = Miner(redis_connection, User) 22 | 23 | while True: 24 | print("trying to mine") 25 | block = miner.mine() 26 | if block == None: 27 | redis_connection.set("StopMining","No") 28 | print("Puzzle solved by other node") 29 | continue 30 | 31 | print("have a block, broadcasting") 32 | #funcs.send_message(sock, block.to_redis()) 33 | #funcs.send_object(block) 34 | serial = pickle.dumps(block) 35 | print("Serialized block = ") 36 | print(serial) 37 | print(json.dumps(block.to_json(), indent=4)) 38 | #print(json.dumps(bl.to_json(),indent = 4)) 39 | funcs.send_message(sock,"Block") 40 | funcs.send_bytes(sock,serial) 41 | 42 | 43 | def send_transaction(sock,User): 44 | while True: 45 | print("waiting for transaction to be sent") 46 | payload = json.loads(redis_connection.blpop(SEND_TRANSACTIONS_QUEUE_KEY)[1].decode('utf-8')) 47 | transaction = Transaction.from_redis(redis_connection, payload) 48 | print("try to send the received transaction") 49 | serial = pickle.dumps(transaction) 50 | funcs.send_message(sock,"Transaction") 51 | funcs.send_bytes(sock,serial) 52 | 53 | 54 | def stop_mining(): 55 | redis_connection.set("StopMining","Yes") 56 | 57 | def handle_receive(sock, User): 58 | """ Thread receives broadcasted data """ 59 | while True: 60 | 61 | tp = funcs.receive_message(sock) 62 | 63 | data = funcs.receive_bytes(sock) 64 | obj = pickle.loads(data) 65 | if tp == "Transaction": 66 | # load transaction into a transaction object 67 | transaction = obj 68 | # verify transaction and if it is valid, put it into the 69 | # redis queue of transactions that need to be mined 70 | 71 | # TODO (param): verify if the sender has the money to send 72 | if transaction.verify(): 73 | print("new transaction added to queue") 74 | transaction.write_to_redis(redis_connection, TRANSACTION_QUEUE_KEY) 75 | else: 76 | print("Invalid transaction received from tracker", file=sys.stderr) 77 | print("json of transaction: ", file=sys.stderr) 78 | print(json.dumps(payload, indent=4), file=sys.stderr) 79 | 80 | elif tp == "Block": 81 | 82 | # load block into a block object and verify if it is valid 83 | # if it is valid, put it into redis and update the prev_hash key 84 | # and remove the transactions done from the pending transactions queue 85 | block = obj 86 | if block.verify(): 87 | # add block to redis 88 | print("Received block is verified") 89 | print("Stopping current mining") 90 | stop_mining() 91 | 92 | key = "{}{}".format(BLOCK_KEY_PREFIX, block.hash) 93 | print("adding block to key: {}".format(key)) 94 | redis_connection.set(key, json.dumps(block.to_redis(), sort_keys=True)) 95 | 96 | # make the prev_hash field used by local miner to be the hash of the block 97 | # we just added 98 | print("prev hash key set to {}".format(block.hash)) 99 | redis_connection.set(PREV_HASH_KEY, block.hash) 100 | 101 | else: 102 | print(type(obj)) 103 | print("Invalid block received from tracker", file=sys.stderr) 104 | print("json of transaction: ", file=sys.stderr) 105 | print(json.dumps(payload, indent=4), file=sys.stderr) 106 | 107 | 108 | if __name__ == '__main__': 109 | # the user managing this client 110 | # TODO (param): manage this guy's stuff that should be in redis 111 | User = LazyUser() 112 | 113 | # boradcasting connection to tracker 114 | clientSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 115 | clientSock.connect((HOST,PORT)) 116 | 117 | # The receiving thread for this client 118 | rec_thread = threading.Thread(target = handle_receive, args = [clientSock,User], daemon = True) 119 | rec_thread.start() 120 | 121 | # The broadcasting thread for this client 122 | broadcast_thread = threading.Thread(target = miner_thread, args = [clientSock,User], daemon = True) 123 | broadcast_thread.start() 124 | 125 | th = threading.Thread(target = send_transaction, args = [clientSock,User], daemon = True) 126 | th.start() 127 | 128 | while True: 129 | time.sleep(3) 130 | 131 | 132 | clientSock.close() 133 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # host and port for client and tracker 2 | HOST = '192.168.43.92' 3 | PORT = 9992 4 | 5 | # redis key prefixes 6 | TRANSACTION_QUEUE_KEY = 'transactions.queue' 7 | BLOCK_KEY_PREFIX = 'chain.block.' 8 | PREV_HASH_KEY = 'prev_hash' 9 | SEND_TRANSACTIONS_QUEUE_KEY = 'send.transactions.queue' 10 | TRANSACTIONS_SIGNATURE = 'transactions.signature.' 11 | 12 | # number of transactions in a block 13 | TRANSACTIONS_IN_BLOCK = 1 14 | GAMER_BAWA = 1024 15 | -------------------------------------------------------------------------------- /funcs.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | def send_data(sock,data): 4 | sock.sendall(data) 5 | 6 | def receive_data(sock,size = 4096): 7 | data = bytes() 8 | while size: 9 | recv = sock.recv(size) 10 | if not recv: 11 | raise ConnectionError() 12 | data += recv 13 | size -= len(recv) 14 | return data 15 | 16 | def nDigit(s,size): 17 | s = str(s) 18 | if(len(s) 2 | 3 | 4 | 5 | BlockChains 6 | 7 | 8 |
9 |

BlockChains/LazyCoins

10 |

BlockChain is a decentralized distributed database which powers cryptocurrencies such as bitcoin

11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/js/data.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', function() { 2 | var socket = io(); 3 | var canvas = document.getElementById("myCanvas"); 4 | var ctx = canvas.getContext("2d"); 5 | var mock = [{'nonce':'hello', 'prev_hash':'what', 'transactions':[{'sender': 'ram', 'receiver': 'shyam', 'signature': 'sig'}, {'sender': 'ram', 'receiver': 'shyam', 'signature': 'sig'}]}, {'nonce':'hello', 'prev_hash':'what', 'transactions':[{'sender': 'ram', 'receiver': 'shyam', 'signature': 'sig'}]}, {'noonce':'hello', 'prev_hash':'what', 'transactions':[{'sender': 'ram', 'receiver': 'shyam', 'signature': 'sig'}]}]; 6 | 7 | 8 | socket.on('data', function (msg) { 9 | data = msg.data; 10 | //console.log(data[0]); 11 | var new_arr = []; 12 | data = msg.data 13 | console.log('server send data'); 14 | console.log(data); 15 | //console.log(msg.data); 16 | var cur_hash; 17 | for(var i = 0; i < data.length; i++) { 18 | if(data[i].prev_hash == null) { 19 | new_arr.push(data[i]); 20 | cur_hash = data[i].hash; 21 | break; 22 | } 23 | } 24 | var counter = data.length - 1; 25 | while(counter--) { 26 | for(var i = 0; i < data.length; i++) { 27 | if(data[i].prev_hash == cur_hash) { 28 | console.log(data[i]); 29 | console.log('pushing'); 30 | new_arr.push(data[i]); 31 | console.log(new_arr[new_arr.length-1]); 32 | cur_hash = data[i].hash; 33 | break; 34 | } 35 | } 36 | } 37 | 38 | console.log('fdfdd'); 39 | console.log(new_arr); 40 | drawChain(new_arr); 41 | }); 42 | 43 | var x = 10, y = 0; 44 | 45 | 46 | function drawChain(hello) { 47 | for(var i = 0; i < hello.length; i++) { 48 | ctx.font = '20px Monaco'; 49 | 50 | var data = hello[i]; 51 | 52 | ctx.fillStyle = '#CCCCCC'; 53 | ctx.fillRect(x, y, 250, 420); 54 | var nonce = data.nonce; 55 | var prevHash = data.prev_hash; 56 | ctx.fillStyle = '#006699'; 57 | y = 10; 58 | ctx.fillRect(x, y, 250, 50); 59 | ctx.fillStyle = '#ddd'; 60 | ctx.fillText('Nonce :' + data.nonce, x, y+20); 61 | //ctx.fillText(data.nonce, x, y+00); 62 | ctx.fillStyle = '#006699'; 63 | y = y + 70; 64 | ctx.fillRect(x, y, 250, 50); 65 | ctx.fillStyle = '#ddd'; 66 | if(data.prev_hash != null) 67 | ctx.fillText('Prev Hash :' + data.prev_hash.substr(0, 10), x, y+20); 68 | else 69 | ctx.fillText('Prev Hash : Null', x , y+20); 70 | y = 160; 71 | ctx.lineWidth=10; 72 | if(i != 0) { 73 | ctx.beginPath(); 74 | ctx.moveTo(x, 225); 75 | ctx.lineTo(x - 70, 225); 76 | ctx.stroke(); 77 | } 78 | 79 | var transactions = data.transactions; 80 | //console.log(transactions); 81 | ctx.font = '20px Monaco'; 82 | for(var j = 0; j < transactions.length; j++) { 83 | var tr = transactions[j]['data']; 84 | //console.log("printing tr") 85 | //console.log(tr); 86 | ctx.fillStyle = '#006699'; 87 | if(tr.type == 'CREATE') ctx.fillStyle = 'green'; 88 | ctx.fillRect(x, y, 250, 110); 89 | ctx.fillStyle = '#ddd'; 90 | var senderkey = tr.sender.e.toString(16) + tr.sender.n.toString(16); 91 | senderkey = senderkey.substr(0, 10); 92 | var receiverkey = tr.receiver.e.toString(16) + tr.receiver.n.toString(16); 93 | receiverkey = receiverkey.substr(0, 10); 94 | ctx.fillText('Sender: ' + senderkey, x, y+20); 95 | ctx.fillText('Receiver: ' + receiverkey, x, y+60); 96 | ctx.fillText('Sign: ' + tr.receiver.n.toString(16).substr(0, 15), x, y+100); 97 | y = y + 130; 98 | } 99 | x = x + 320, y = 0; 100 | } 101 | x = 10, y = 0; 102 | return; 103 | } 104 | }); 105 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | redis-py 2 | rsa 3 | -------------------------------------------------------------------------------- /tracker.py: -------------------------------------------------------------------------------- 1 | import funcs 2 | import threading 3 | import queue 4 | from config import HOST, PORT 5 | 6 | send_lock = threading.Lock() 7 | send_queues = [] 8 | 9 | def client_receive(client_socket,client_address,q): 10 | print("connected to " + str(client_socket) + " " ) 11 | with send_lock: 12 | send_queues.append(q) 13 | 14 | th = threading.Thread(target = send_to_client,args = [client_socket,q],daemon = True) 15 | th.start() 16 | 17 | while True: 18 | print("trying to receive") 19 | size = funcs.receive_data(client_socket,5) 20 | message = funcs.receive_data(client_socket,int(size)) 21 | 22 | with send_lock: 23 | for qu in send_queues: 24 | qu.put(size+message) 25 | 26 | 27 | def send_to_client(sock,q): 28 | while True: 29 | data = q.get() 30 | if data == None: 31 | break 32 | send(sock,data) 33 | 34 | 35 | def send(sock,data): 36 | funcs.send_data(sock,data) 37 | 38 | 39 | if __name__ == '__main__': 40 | 41 | listening_socket = funcs.create_listening_socket(HOST,PORT,100) 42 | while True: 43 | print("Waiting") 44 | client_socket,client_address = listening_socket.accept() 45 | q = queue.Queue() 46 | th = threading.Thread(target = client_receive, args = [client_socket,client_address,q], daemon = True) 47 | th.start() 48 | 49 | -------------------------------------------------------------------------------- /user.py: -------------------------------------------------------------------------------- 1 | import rsa 2 | import json 3 | 4 | class LazyUser(object): 5 | 6 | def __init__(self): 7 | self.pub, self.priv = rsa.newkeys(512) 8 | 9 | def sign(self, transaction): 10 | message = json.dumps(transaction.to_dict(), sort_keys=True).encode('utf-8') 11 | signature = rsa.sign(message, self.priv, 'SHA-256') 12 | return (message, signature) 13 | --------------------------------------------------------------------------------