def __mine(self, mempool: Set[Transaction], chain: Chain, wallet: Wallet) -> Block: c_pool = list(copy.deepcopy(mempool)) mlist = self.__calculate_transactions(c_pool) logger.debug(len(mlist)) block_header = BlockHeader( version=consts.MINER_VERSION, height=chain.length, prev_block_hash=dhash(chain.header_list[-1]), merkle_root=merkle_hash(mlist), timestamp=int(time.time()), signature="", ) sign = wallet.sign(dhash(block_header)) block_header.signature = sign block = Block(header=block_header, transactions=mlist) r = requests.post("http://0.0.0.0:" + str(consts.MINER_SERVER_PORT) + "/newblock", data=compress(block.to_json())) if r.text == "Block Received": vjti_chain_relayer = VJTIChainRelayer(wallet) vjti_chain_relayer.new_block(block) logger.info(f"Miner: Mined Block with {len(mlist)} transaction(s)") return block else: logger.info( f"Miner: Could not mine block with {len(mlist)} transaction(s)" ) return None
def render_block_header(hdr): html = "<table>" html += "<tr><th>" + "Height" + "</th>" html += "<td>" + str(hdr.height) + "</td></tr>" html += "<tr><th>" + "Block Hash" + "</th>" html += "<td>" + dhash(hdr) + "</td></tr>" html += "<tr><th>" + "Prev Block Hash" + "</th>" html += "<td>" + str(hdr.prev_block_hash) + "</td></tr>" html += "<tr><th>" + "Merkle Root" + "</th>" html += "<td>" + str(hdr.merkle_root) + "</td></tr>" html += "<tr><th>" + "Timestamp" + "</th>" html += ("<td>" + str( datetime.fromtimestamp(hdr.timestamp).strftime("%d-%m-%Y %H:%M:%S")) + " (" + str(hdr.timestamp) + ")</td></tr>") # get block block = Block.from_json(get_block_from_db(dhash(hdr))).object() html += "<tr><th>" + "Transactions" + "</th>" html += "<td>" + str(len(block.transactions)) + "</td></tr>" # for i, transaction in enumerate(block.transactions): # s = "coinbase: " + str(transaction.is_coinbase) + ", fees: " + str(transaction.fees) # html += "<tr><th>Transaction " + str(i) + "</th><td>" + str(s) + "</td></tr>" html += "</table>" return str(html)
def add_block(self, block: Block, is_genesis: bool) -> bool: if is_genesis or self.is_block_valid(block): self.header_list.append(block.header) self.update_utxo(block) self.length = len(self.header_list) add_block_to_db(block) for tx in block.transactions: pub_key, data = tx.summarize() for address in data: amount = data[address] timestamp = tx.timestamp bhash = dhash(block.header) thash = dhash(tx) message = tx.message history = generate_tx_hist(amount, pub_key, timestamp, bhash, thash, message) self.transaction_history.append(address, history) history = generate_tx_hist(-amount, address, timestamp, bhash, thash, message) self.transaction_history.append(pub_key, history) logger.info("Chain: Added Block " + str(block)) return True return False
def is_block_valid(self, block: Block): # Check if the block is valid -1 local_utxo = copy.deepcopy(self.utxo) if not block.is_valid(): logger.debug("Block is not valid") return False # Ensure the prev block header matches the previous block hash in the Chain -4 if len(self.header_list) > 0 and not dhash( self.header_list[-1]) == block.header.prev_block_hash: logger.debug( "Chain: Block prev header does not match previous block") return False # Validating each transaction in block for t in block.transactions: if self.is_transaction_valid(t): thash = dhash(t) # Remove the spent outputs for tinput in t.vin: so = t.vin[tinput].payout if so: if local_utxo.get(so)[0] is not None: local_utxo.remove(so) else: logger.error( "Chain: Single output missing in UTxO, Transaction invalid" ) return False else: logger.error( "Chain: No Single output, Transaction invalid") return False # Add new unspent outputs for touput in t.vout: local_utxo.set(SingleOutput(txid=thash, vout=touput), t.vout[touput], block.header) else: logger.debug("Chain: Transaction not valid") return False # Validate Authority Signature timestamp = datetime.fromtimestamp(block.header.timestamp) seconds_since_midnight = (timestamp - timestamp.replace( hour=0, minute=0, second=0, microsecond=0)).total_seconds() for authority in authority_rules["authorities"]: if seconds_since_midnight <= authority[ "to"] and seconds_since_midnight >= authority["from"]: blk_hdr = copy.deepcopy(block.header) blk_hdr.signature = "" if Wallet.verify(dhash(blk_hdr), block.header.signature, authority["pubkey"]): return True return False
def __mine(self, mempool: Set[Transaction], chain: Chain, payout_addr: str) -> Block: c_pool = list(copy.deepcopy(mempool)) mlist, fees = self.__calculate_best_transactions(c_pool) # logger.debug(f"Miner: Will mine {len(mlist)} transactions and get {fees} scoins in fees") coinbase_tx_in = { 0: TxIn(payout=None, sig="Receiving some Money", pub_key="Does it matter?") } coinbase_tx_out = { 0: TxOut(amount=chain.current_block_reward(), address=payout_addr), 1: TxOut(amount=fees, address=payout_addr), } coinbase_tx = Transaction( is_coinbase=True, version=consts.MINER_VERSION, fees=0, timestamp=int(time.time()), locktime=-1, vin=coinbase_tx_in, vout=coinbase_tx_out, ) mlist.insert(0, coinbase_tx) block_header = BlockHeader( version=consts.MINER_VERSION, height=chain.length, prev_block_hash=dhash(chain.header_list[-1]), merkle_root=merkle_hash(mlist), timestamp=int(time.time()), target_difficulty=chain.target_difficulty, nonce=0, ) DONE = False for n in range(2**64): block_header.nonce = n bhash = dhash(block_header) if chain.is_proper_difficulty(bhash): block = Block(header=block_header, transactions=mlist) requests.post("http://0.0.0.0:" + str(consts.MINER_SERVER_PORT) + "/newblock", data=compress(block.to_json())) logger.info( f"Miner: Mined Block with {len(mlist)} transactions, Got {fees} in fees and {chain.current_block_reward()} as reward" ) DONE = True break if not DONE: logger.error( "Miner: Exhausted all 2 ** 64 values without finding proper hash" )
def remove_transactions_from_mempool(self, block: Block): """Removes transaction from the mempool based on a new received block Arguments: block {Block} -- The block which is received """ new_mempool = set() for x in self.mempool: DONE = True for t in block.transactions: if dhash(x) == dhash(t): DONE = False if DONE: new_mempool.add(x) self.mempool = new_mempool
def send_block_hashes(): peer_height = int(request.form.get("myheight")) hash_list = [] for i in range(peer_height, ACTIVE_CHAIN.length): hash_list.append(dhash(ACTIVE_CHAIN.header_list[i])) logger.debug(peer_height) return jsonify(hash_list)
def process_new_block(request_data: bytes) -> str: global BLOCKCHAIN block_json = decompress(request_data) if block_json: try: block = Block.from_json(block_json).object() # Check if block already exists if get_block_from_db(dhash(block.header)): logger.info("Server: Received block exists, doing nothing") return "Block already Received Before" if BLOCKCHAIN.add_block(block): logger.info( "Server: Received a New Valid Block, Adding to Chain") logger.debug("Server: Sending new block to peers") # Broadcast block to other peers send_to_all_peers("/newblock", request_data) else: return "Block Received, but was not added, due to some error" except Exception as e: logger.error("Server: New Block: invalid block received " + str(e)) return "Invalid Block Received" # Kill Miner t = Timer(1, miner.stop_mining) t.start() return "Block Received" logger.error("Server: Invalid Block Received") return "Invalid Block"
def build_from_header_list(cls, hlist: List[BlockHeader]): nchain = cls() nchain.header_list = [] for header in hlist: block = Block.from_json(get_block_from_db(dhash(header))).object() nchain.add_block(block) return nchain
def send_block_hashes(): log_ip(request, inspect.stack()[0][3]) peer_height = int(request.forms.get("myheight")) hash_list = [] for i in range(peer_height, BLOCKCHAIN.active_chain.length): hash_list.append(dhash(BLOCKCHAIN.active_chain.header_list[i])) return compress(json.dumps(hash_list)).decode()
def sendinfo(): log_ip(request, inspect.stack()[0][3]) s = ("No. of Blocks: " + str(BLOCKCHAIN.active_chain.length) + "<br>" + dhash(BLOCKCHAIN.active_chain.header_list[-1]) + "<br>" + "Balance " + str(check_balance(MY_WALLET.public_key)) + "<br>Public Key: <br>" + str(get_wallet_from_db(consts.MINER_SERVER_PORT)[1])) return s
def send_block_hashes(): peer_height = int(request.forms.get("myheight")) hash_list = [] for i in range(peer_height, BLOCKCHAIN.active_chain.length): hash_list.append(dhash(BLOCKCHAIN.active_chain.header_list[i])) # logger.debug("Server: Sending Peer this Block Hash List: " + str(hash_list)) return compress(json.dumps(hash_list)).decode()
def add_block(self, block: Block): # if check_block_in_db(dhash(block.header)): # logger.debug("Chain: AddBlock: Block already exists") # return True blockAdded = False for chain in self.chains: if chain.length == 0 or block.header.prev_block_hash == dhash( chain.header_list[-1]): if chain.add_block(block): BlockChain.block_ref_count[dhash(block.header)] += 1 self.update_active_chain() if chain is self.active_chain: # Remove the transactions from MemPool self.remove_transactions_from_mempool(block) blockAdded = True if blockAdded: return True # Check if we need to fork self.chains.sort(key=attrgetter("length"), reverse=True) new_chains = copy.deepcopy(self.chains) for chain in new_chains: hlist = copy.deepcopy(chain.header_list) for h in reversed(hlist): # Check if block can be added for current header if dhash(h) == block.header.prev_block_hash: newhlist = [] for hh in chain.header_list: newhlist.append(hh) if dhash(hh) == block.header.prev_block_hash: break nchain = Chain.build_from_header_list(newhlist) if nchain.add_block(block): for header in nchain.header_list: BlockChain.block_ref_count[dhash(header)] += 1 if nchain not in self.chains: self.chains.append(nchain) self.update_active_chain() logger.debug( f"There was a soft fork and a new chain was created with length {nchain.length}" ) return True return False
def update_active_chain(self): self.active_chain = max(self.chains, key=attrgetter("length")) max_length = self.active_chain.length # Try removing old chains new_chains = [] for chain in self.chains: if chain.length > max_length - consts.FORK_CHAIN_HEIGHT: new_chains.append(chain) else: for hdr in chain.header_list: if BlockChain.block_ref_count[dhash(hdr)] == 1: del BlockChain.block_ref_count[dhash(hdr)] remove_block_from_db(dhash(hdr)) else: BlockChain.block_ref_count[dhash(hdr)] -= 1 self.chains = new_chains # Save Active Chain to DB write_header_list_to_db(self.active_chain.header_list)
def sendinfo(): s = ("No. of Blocks: " + str(BLOCKCHAIN.active_chain.length) + "<br>" + dhash(BLOCKCHAIN.active_chain.header_list[-1]) + "<br>" + "Number of chains " + str(len(BLOCKCHAIN.chains)) + "<br>" + "Balance " + str(check_balance()) + "<br>" + "Difficulty: " + str(BLOCKCHAIN.active_chain.target_difficulty) + "<br>Block reward " + str(BLOCKCHAIN.active_chain.current_block_reward()) + "<br>Public Key: <br>" + str(get_wallet_from_db(consts.MINER_SERVER_PORT)[1])) return s
def explorer(): log_ip(request, inspect.stack()[0][3]) prev = int(request.query.prev or 0) if prev < 0: prev = 0 hdr_list = list(reversed(BLOCKCHAIN.active_chain.header_list)) indexes = [i for i in range(prev * 8, (prev + 1) * 8) if i < len(hdr_list)] blocks = [Block.from_json(get_block_from_db(dhash(hdr_list[i]))).object() for i in indexes] transactions = list(BLOCKCHAIN.mempool) return template("explorer.html", blocks=blocks, transactions=transactions, prev=prev)
def update_utxo(self, block: Block): block_transactions: List[Transaction] = block.transactions for t in block_transactions: thash = dhash(t) # Remove the spent outputs for tinput in t.vin: so = t.vin[tinput].payout if so: self.utxo.remove(so) # Add new unspent outputs for toutput in t.vout: self.utxo.set(SingleOutput(txid=thash, vout=toutput), t.vout[toutput], block.header)
def add_block(self, block: Block) -> bool: blockAdded = False chain = self.active_chain is_genesis = chain.length == 0 if is_genesis or block.header.prev_block_hash == dhash(chain.header_list[-1]): if chain.add_block(block, is_genesis): self.update_active_chain() self.remove_transactions_from_mempool(block) blockAdded = True return blockAdded
def visualize_chain(): data = [] start = BLOCKCHAIN.active_chain.length - 10 if BLOCKCHAIN.active_chain.length > 10 else 0 for i, chain in enumerate(BLOCKCHAIN.chains): headers = [] if len(chain.header_list) > 200: for hdr in chain.header_list[:100]: d = {} d["hash"] = dhash(hdr)[-5:] d["time"] = hdr.timestamp d["data"] = render_block_header(hdr) d["height"] = hdr.height headers.append(d) for hdr in chain.header_list[-100:]: d = {} d["hash"] = dhash(hdr)[-5:] d["time"] = hdr.timestamp d["data"] = render_block_header(hdr) d["height"] = hdr.height headers.append(d) data.append(headers) return template("chains.html", data=data, start=start)
def visualize_chain(): log_ip(request, inspect.stack()[0][3]) data = [] start = BLOCKCHAIN.active_chain.length - 10 if BLOCKCHAIN.active_chain.length > 10 else 0 headers = [] hdr_list = BLOCKCHAIN.active_chain.header_list if len(hdr_list) > 200: hdr_list = BLOCKCHAIN.active_chain.header_list[:100] + BLOCKCHAIN.active_chain.header_list[-100:] for hdr in hdr_list: d = {} d["hash"] = dhash(hdr)[-5:] d["time"] = hdr.timestamp d["data"] = render_block_header(hdr) headers.append(d) data.append(headers) return template("chains.html", data=data, start=start)
def is_block_valid(self, block: Block): # Check if the block is valid -1 if not block.is_valid(): logger.debug("Block is not valid") return False # Block hash should have proper difficulty -2 if not block.header.target_difficulty >= self.target_difficulty: logger.debug("Chain: BlockHeader has invalid difficulty") return False if not self.is_proper_difficulty(dhash(block.header)): logger.debug("Chain: Block has invalid POW") return False # Block should not have been mined more than 2 hours in the future -3 difference = get_time_difference_from_now_secs(block.header.timestamp) if difference > consts.BLOCK_MAX_TIME_FUTURE_SECS: logger.debug("Block: Time Stamp not valid") return False # Reject if timestamp is the median time of the last 11 blocks or before -5 if len(self.header_list) > 11: last_11 = self.header_list[-11:] last_11_timestamp = [] for bl in last_11: last_11_timestamp.append(bl.timestamp) med = median(last_11_timestamp) if block.header.timestamp <= med: logger.debug("Chain: Median time past") return False # Ensure the prev block header matches the previous block hash in the Chain -4 if len(self.header_list) > 0 and not dhash( self.header_list[-1]) == block.header.prev_block_hash: logger.debug( "Chain: Block prev header does not match previous block") return False # Validating each transaction in block for tx in block.transactions: if not self.is_transaction_valid(tx): logger.debug("Chain: Transaction not valid") return False # Validate that the first coinbase Transaction has valid Block reward and fees remaining_transactions = block.transactions[1:] fee_total = 0 for tx in remaining_transactions: if not self.is_transaction_valid(tx): logger.debug("Chain: Transaction not valid") return False else: fee_total += tx.fees if not len(block.transactions[0].vout) == 2: logger.debug("Chain: Coinbase vout length != 2") return False if not block.transactions[0].vout[1].amount == fee_total: logger.debug("Chain: Coinbase fee invalid") return False if not block.transactions[0].vout[ 0].amount == self.current_block_reward(): logger.debug("Chain: Coinbase reward invalid") return False return True
def build_utxo(self): for header in self.header_list: block = Block.from_json(get_block_from_db(dhash(header))).object() self.update_utxo(block)
from utils.utils import dhash def is_proper_difficulty(target_difficulty, bhash: str) -> bool: pow = 0 for c in bhash: if not c == "0": break else: pow += 1 if pow < target_difficulty: return False return True print(genesis_block, dhash(genesis_block_header)) for difficulty in range(5, 10): tss = time.time() for n in range(2 ** 64): genesis_block_header.nonce = n genesis_block_header.target_difficulty = difficulty bhash = dhash(genesis_block_header) if is_proper_difficulty(difficulty, bhash): print(f"Timestamp {int(tss)} Nonce {n} hash {bhash}\n Difficulty {difficulty} in {(time.time() - tss)} secs") print(genesis_block_header) DONE = True break if not DONE: print("Miner: Exhausted all 2 ** 64 values without finding proper hash")
def __hash__(self): return int(dhash(self), 16)
def get_block_header_hash(height): return dhash(BLOCKCHAIN.active_chain.header_list[height])
def __repr__(self): return dhash(self.header)
def hash(self): return dhash(self)
def __eq__(self, other): for i, h in enumerate(self.header_list): if dhash(h) != dhash(other.header_list[i]): return False return True
return get_block_from_db(hhash) return "Hash hi nahi bheja LOL" @app.route("/getblockhashes", methods=["POST"]) def send_block_hashes(): peer_height = int(request.form.get("myheight")) hash_list = [] for i in range(peer_height, ACTIVE_CHAIN.length): hash_list.append(dhash(ACTIVE_CHAIN.header_list[i])) logger.debug(peer_height) return jsonify(hash_list) # The singleOutput for first coinbase transaction in genesis block so = SingleOutput(txid=dhash(genesis_block_transaction[0]), vout=0) first_block_transactions = [ Transaction( version=1, locktime=0, timestamp=2, is_coinbase=True, fees=0, vin={0: TxIn(payout=None, sig="", pub_key=consts.WALLET_PUBLIC)}, vout={ 0: TxOut(amount=5000000000, address=consts.WALLET_PUBLIC), 1: TxOut(amount=4000000000, address=consts.WALLET_PUBLIC), }, ), Transaction(
def fetch_peer_list(): r = requests.get(consts.SEED_SERVER_URL) peer_list = json.loads(r.text) return peer_list def get_peer_url(peer: Dict[str, Any]) -> str: return "http://" + str(peer["ip"]) + ":" + str(peer["port"]) if __name__ == "__main__": # The singleOutput for first coinbase transaction in genesis block so = SingleOutput(txid=dhash(first_block_transaction[0]), vout=0) first_transaction = Transaction( version=1, locktime=0, timestamp=3, is_coinbase=False, fees=4000000000, vin={0: TxIn(payout=so, sig="", pub_key=consts.WALLET_PUBLIC)}, vout={0: TxOut(amount=1000000000, address=consts.WALLET_PUBLIC)} ) sign_copy_of_tx = copy.deepcopy(first_transaction) sign_copy_of_tx.vin = {} w = Wallet() w.public_key = consts.WALLET_PUBLIC