def test_transaction_add_signature(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) self.assertTrue(hasattr(transaction, "signature"))
def test_transaction_to_json(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) content = transaction.to_json() self.assertIsInstance(content, str) self.assertGreater(len(content), 0)
def test_transaction_verify_transaction_signature_too_large_signature(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) transaction.add_signature("3379cc2f08e4cde5d24af02611c32693b18f406d4b58fbcd2bbd0acc67b1d") verify = transaction.verify_transaction_signature() self.assertFalse(verify)
def test_transaction_to_dict(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) content = transaction.to_dict() self.assertIsInstance(content, dict) self.assertEqual(list(content.keys()), ["sender", "recipient", "value", "fee"])
def test_transaction_verify_transaction_signature(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) verify = transaction.verify_transaction_signature() self.assertTrue(verify)
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_blockchain_add_new_transaction_not_enough_balance(self): wallet = Wallet() blockchain = Blockchain(wallet) transaction = Transaction(wallet.pubkey, "CityU", 100) signature = wallet.sign_transaction(transaction) transaction.add_signature(signature) blockchain.add_new_transaction(transaction) self.assertEqual(len(blockchain.unconfirmed_transactions), 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 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_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_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 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 add_new_transaction(self, transaction: Transaction) -> bool: ''' add a new transaction to the block after checking balance ''' if transaction.verify_transaction_signature(): # Check balance and fee before confirming a transaction total_charge = float(transaction.value) + float(transaction.fee) if transaction.sender != "Block_Reward" and \ self.check_balance(transaction.sender) >= total_charge: self.unconfirmed_transactions.append(transaction.to_json()) return True return False
def new_transaction_signed(): values = request.form required = ['sender', 'recipient_address', 'value', 'signature'] # Check that the required fields are in the POST data if not all(k in values for k in required): return 'Missing values', 400 transaction = Transaction(values['sender'], values['recipient_address'], values['value']) transaction.signature = values['signature'] transaction_result = blockchain.add_new_transaction(transaction) if transaction_result: response = {'message': 'Transaction will be added to Block'} return jsonify(response), 201 else: response = {'message': 'Invalid Transaction!'} return jsonify(response), 406
def merkle_path(): values = request.form required = ['sender', 'recipient', 'value'] # Check that the required fields are in the POST data if not all(k in values for k in required): return 'Missing values', 400 transaction = Transaction(values.get('sender'), values.get('recipient'), values.get('value')) if values.get('signature'): transaction.signature = values.get('signature') path = blockchain.merkle_path(transaction) if len(path) > 0: root = path[-1] path = path[:-1] return jsonify(path), 200
def partial_validation(): values = request.form required = ['root', 'path', 'sender', 'recipient', 'value'] # Check that the required fields are in the POST data if not all(k in values for k in required): return 'Missing values', 400 root = values.get('root') path = json.loads(values.get('path')) transaction = Transaction(values.get('sender'), values.get('recipient'), values.get('value')) if values.get('signature'): transaction.signature = values.get('signature') h = sha256(str(transaction.to_json()).encode()).hexdigest() new_root = blockchain.partialValidation(path, h) result = root == new_root return jsonify(result), 200
def search_block_with_transaction(self, transactionHash): '''return block that matches given transaction hash''' fullchain = [json.loads(block) for block in self.chain] for block in fullchain[::-1]: for trans in block['transaction']: trans = json.loads(trans) new_trans = Transaction(trans['sender'], trans['recipient'], trans['value']) if 'signature' in trans.keys(): new_trans.signature = trans['signature'] new_transHash = sha256(str( new_trans.to_json()).encode()).hexdigest() if transactionHash == new_transHash: return block return False
def test_wallet_sign_transaction_valid(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) signature = wallet.sign_transaction(transaction) self.assertIsInstance(signature, str) self.assertGreater(len(signature), 0)
def new_transaction(): '''This API adds transactions to the transaction pool''' values = request.form required = ['recipient_address', 'value'] # Check that the required fields are in the POST data if not all(k in values for k in required): return 'Missing values', 400 transaction = Transaction(myWallet.pubkey, values['recipient_address'], values['value']) transaction.add_signature(myWallet.sign_transaction(transaction)) transaction_result = blockchain.add_new_transaction(transaction) if transaction_result: response = {'message': 'Transaction will be added to Block'} return jsonify(response), 201 else: response = {'message': 'Invalid Transaction!'} return jsonify(response), 406
def create_interest_transactions(self, addresses) -> List[Transaction]: '''check balance of each address and generate interest transaction''' interest_txs = [] for address in addresses: balance = self.check_balance(address) interest = balance * self.INTEREST interest_tx = Transaction("Block_Reward", address, str(interest)) interest_txs.append(interest_tx) return interest_txs
def sign_transaction(self, transaction: Transaction) -> str: ''' method to return the signature that is coming from the actual owner in a transaction implemented in the Wallet class instead of Transaction class to protect the private key from illegal access ''' signer = PKCS1_v1_5.new(self._private_key) payload = str(transaction.to_dict()).encode('utf-8') h = SHA.new(payload) return binascii.hexlify(signer.sign(h)).decode('ascii')
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 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 merkle_path(self, transaction: Transaction): '''return merkle path of given transaction''' path = [] transactionHash = sha256(str( transaction.to_json()).encode()).hexdigest() block = self.search_block_with_transaction(transactionHash) leaves = [] if block: for trans in block['transaction']: trans = json.loads(trans) new_trans = Transaction(trans['sender'], trans['recipient'], trans['value']) if 'signature' in trans.keys(): new_trans.signature = trans['signature'] new_transHash = sha256(str( new_trans.to_json()).encode()).hexdigest() leaves.append(new_transHash) path = self._merklePath(leaves, transactionHash, []) path.append(block['merkle_root']) return path
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
def test_transaction_verify_transaction_signature_no_signature(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) verify = transaction.verify_transaction_signature() self.assertFalse(verify)
def test_transaction_initialize(self): wallet = Wallet() transaction = Transaction(wallet.pubkey, "CityU", 1) self.assertIsInstance(transaction, Transaction)