def test_block_initialize(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) block = Block(0, [transaction], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), "0") self.assertIsInstance(block, Block)
def test_block_compute_hash(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) block = Block(0, [transaction.to_json()], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), "0") hash_val = block.compute_hash() self.assertIsInstance(hash_val, str) self.assertGreater(len(hash_val), 0)
def test_block_to_json(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) block = Block(0, [transaction.to_json()], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), "0") content = block.to_json() self.assertIsInstance(content, str) self.assertGreater(len(content), 0)
def test_block_to_dict(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) block = Block(0, [transaction.to_json()], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), "0") content = block.to_dict() self.assertIsInstance(content, dict) self.assertEqual(list(content.keys()), ["index", "timestamp", "previous_hash", "merkle_root", "nonce", "difficulty"])
def create_genesis_block(self, wallet: Wallet): '''method to create and puts the genesis block into the blockchain''' block_reward = Transaction("Block_Reward", wallet.pubkey, "5.0").to_json() genesis_block = Block( 0, [block_reward], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), "0") # Hash of genesis block cannot be computed directly, proof of work is needed genesis_block.hash = self.proof_of_work(genesis_block) self.chain.append(genesis_block.to_json())
def test_blockchain_add_block_wrong_previous_hash(self): wallet = Wallet() blockchain = Blockchain(wallet) transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) block = Block(0, [transaction.to_json()], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), "Wrong") computed_hash = blockchain.proof_of_work(block) result = blockchain.add_block(block, computed_hash) self.assertFalse(result)
def test_blockchain_proof_of_work_bad_block(self): wallet = Wallet() blockchain = Blockchain(wallet) transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) previous_hash = blockchain.last_block["hash"] block = Block(0, [transaction.to_json()], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), previous_hash) delattr(block, "transaction") self.assertRaises(AttributeError, blockchain.proof_of_work, block)
def test_blockchain_proof_of_work(self): wallet = Wallet() blockchain = Blockchain(wallet) transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) previous_hash = blockchain.last_block["hash"] block = Block(0, [transaction.to_json()], datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), previous_hash) computed_hash = blockchain.proof_of_work(block) self.assertGreater(len(computed_hash), 0)
def lightweight(): fullchain = [json.loads(block) for block in blockchain.chain] lightweight = [] for block in fullchain: block_object = Block(block['index'], block['transaction'], block['timestamp'], block['previous_hash']) block_object.merkle_root = block['merkle_root'] block_object.nonce = block['nonce'] block_object.difficulty = block['difficulty'] lightweight.append(block_object.to_dict()) response = { 'chain': json.dumps(lightweight), 'length': len(lightweight) } return jsonify(response), 200
def mine(self, wallet: Wallet) -> Union[Block, bool]: ''' method to generate new blocks and claim the block reward and all the transaction fees It confirms all unconfirmed transactions into blocks by using the proof-of-work method. convert to JSON to store transaction in the blockchain ''' # get all available wallet addresses in the network addresses = self.get_addresses_from_transactions() # create Transaction objects for interest interest_txs = self.create_interest_transactions(addresses) for interest_tx in interest_txs: self.unconfirmed_transactions.insert(0, interest_tx.to_json()) # add up all the transaction fee from all unconfirmed transactions tx_fees = [ float(json.loads(transaction)['fee']) for transaction in self.unconfirmed_transactions ] total_tx_fee = sum(tx_fees) # create and add a transaction into the list of unconfirmed transactions # for sending out a block reward, which also include all of their transaction fees block_reward = Transaction("Block_Reward", wallet.pubkey, str(5.0 + total_tx_fee)) self.unconfirmed_transactions.insert(0, block_reward.to_json()) # if there are no unconfirmed transactions, return False if not self.unconfirmed_transactions: return False new_block = Block( index=self.last_block['index'] + 1, transaction=self.unconfirmed_transactions, timestamp=datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), previous_hash=self.last_block['hash']) new_block.difficulty = self.next_difficulty(self.last_block) proof = self.proof_of_work(new_block) if self.add_block(new_block, proof): self.unconfirmed_transactions = [] return new_block else: return False
def valid_chain(self, chain: List[str]) -> bool: '''check if a blockchain (all blocks) is valid''' current_index = 0 chain = json.loads(chain) # load and check every block from the blockchain while current_index < len(chain): block = json.loads(chain[current_index]) current_block = Block(block['index'], block['transaction'], block['timestamp'], block['previous_hash']) current_block.merkle_root = block['merkle_root'] current_block.nonce = block['nonce'] current_block.difficulty = block['difficulty'] # if the current block is NOT the last block if current_index + 1 < len(chain): # if the hash value of the current block != previous block's hash value of the next block, then reject if current_block.compute_hash() != json.loads( chain[current_index + 1])['previous_hash']: return False # check if the current block is an instance from the list of chain if isinstance(current_block.transaction, list): for transaction in current_block.transaction: transaction = json.loads(transaction) # Skip block reward because it does not have signature if transaction['sender'] == 'Block_Reward': continue current_transaction = Transaction(transaction['sender'], transaction['recipient'], transaction['value']) current_transaction.signature = transaction['signature'] # check if digital signature of each transaction is valid, if not then reject if not current_transaction.verify_transaction_signature(): return False # check if hash value of the current block is not valid, if yes then reject if not self.is_valid_proof(current_block, block['hash']): return False current_index += 1 return True