def _execute_phase_2(self, config, phase_1_info): """ * At this point, any participating processing nodes will have appended signed "approvals" of their respectively owned transactions and signed "validations" of un-owned transactions (see Phase 1 Verification Process). * Processing nodes participating in Phase 2 Verification may be a different set of nodes than the set of nodes participating in Phase 1 Verification. * Processing nodes may be defined for the sole purpose of Phase 2 verification (without any "owned" transactions in the system). * A node participating in Phase 2 verification will verify that all transactions in the prospective block are "approved" by their respective owners and are not declared invalid by a system configured portion (e.g. percentage, plurality, or majority). * Any transactions which are not approved will be taken out of the prospective block and "bumped" to the next block now - (d * T) for Phase 1 processing. * If a non-approved transaction which is older than a system configured time for the transaction type and owner, the transaction will not be "bumped" to the next block, but instead be placed in a system "pool" or "queue" for later handling or alternate processing. * All signed "approval" verification units will be grouped, concatenated, and cryptographically signed. * A "Phase 2 signature structure" is created and appended to the block. """ """ - check block sig - req: tx_type, tx_id, tx_ts, owner (origin - original phase_1 node_id), trans_sig - tx minus the payload """ phase = 2 phase_1_record = phase_1_info["record"] p1_verification_info = phase_1_info["verification_info"] phase_1_record['verification_info'] = p1_verification_info prior_block_hash = self.get_prior_hash(phase_1_record[ORIGIN_ID], phase) # validate phase_1's verification record if validate_verification_record(phase_1_record, p1_verification_info): # storing valid verification record verfication_db.insert_verification(phase_1_record) # updating record phase phase_1_record[PHASE] = phase valid_transactions, invalid_transactions = self.check_tx_requirements(p1_verification_info) verification_info = { 'valid_txs': valid_transactions, 'invalid_txs': invalid_transactions, 'business': self.network.business, 'deploy_location': self.network.deploy_location } lower_hash = phase_1_record[SIGNATURE][HASH] # sign verification and rewrite record block_info = sign_verification_record(self.network.this_node.node_id, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], phase_1_record[BLOCK_ID], phase_1_record[PHASE], phase_1_record[ORIGIN_ID], int(time.time()), verification_info ) # inserting verification info after signing verfication_db.insert_verification(block_info['verification_record']) self.network.send_block(self.network.phase_2_broadcast, block_info, phase_1_record[PHASE]) print "phase_2 executed"
def _execute_phase_4(self, config, phase_3_info): """ external partner notary phase """ phase = 4 phase_3_record = phase_3_info['record'] p3_verification_info = phase_3_info['verification_info'] phase_3_record['verification_info'] = p3_verification_info prior_block_hash = self.get_prior_hash(phase_3_record[ORIGIN_ID], phase) # validate phase_3's verification record if validate_verification_record(phase_3_record, p3_verification_info): # storing valid verification record verfication_db.insert_verification(phase_3_record) # updating record phase phase_3_record[PHASE] = phase lower_phase_hash = phase_3_record[SIGNATURE][HASH] # sign verification and rewrite record block_info = sign_verification_record( self.network.this_node.node_id, prior_block_hash, lower_phase_hash, self.service_config['public_key'], self.service_config['private_key'], phase_3_record[BLOCK_ID], phase_3_record[PHASE], phase_3_record[ORIGIN_ID], int(time.time()), None) # inserting verification info after signing verfication_db.insert_verification( block_info['verification_record']) self.network.send_block(self.network.phase_4_broadcast, block_info, phase) print "phase 4 executed"
def test_validate_verification_record(self): verification_ts = 1479435043 """ testing crypto validate_verification_record """ test_output = crypto.sign_verification_record(SIGNATORY, PRIOR_BLOCK_HASH, LOWER_HASH, PUBLIC_KEY, PRIVATE_KEY, BLOCK_ID, PHASE, ORIGIN_ID, verification_ts, public_transmission=None, verification_info=None) record = test_output['verification_record'] record['verification_ts'] = verification_ts # testing if validate_verification_record passes self.assertEqual(crypto.validate_verification_record(record, None), True) # testing that KeyError is appropriately raised for key in record.keys(): if key is not "verification_info": record.pop(key) self.assertRaises(KeyError, crypto.validate_verification_record, record, None, test_mode=True)
def _execute_phase_3(self, config, phase_2_info): """ * At this point, any participating processing nodes will have appended signed Phase 2 verification proof to the block. * Processing nodes participating in Phase 3 Verification may be a different set of nodes than the set of nodes participating in Phase 1 and Phase 2 Verification processes. * Processing nodes may be defined for the sole purpose of Phase 3 verification (e.g. for independent blockchain verification auditing purposes). * A participating node will verify that no invalid transaction has been included in the set of approved transaction. * A participating node will verify that all "approved" transactions are signed by their respective owner. * A node may perform extra validation steps on all transactions and verification units. * All signed "Phase 3 Signature Structures" will be grouped, concatenated, and cryptographically signed by the node. """ phase = 3 phase_2_record = phase_2_info['record'] p2_verification_info = phase_2_info['verification_info'] phase_2_record['verification_info'] = p2_verification_info prior_block_hash = self.get_prior_hash(phase_2_record[ORIGIN_ID], phase) # validate phase_2's verification record if validate_verification_record(phase_2_record, p2_verification_info): # storing valid verification record verfication_db.insert_verification(phase_2_record) phase_2_records = self.get_sig_records(phase_2_record) signatories, businesses, locations = self.get_verification_diversity(phase_2_records) # checking if passed requirements to move on to next phase if len(signatories) >= P2_COUNT_REQ and len(businesses) >= P2_BUS_COUNT_REQ and len(locations) >= P2_LOC_COUNT_REQ: # updating record phase phase_2_record[PHASE] = phase lower_hashes = [record[SIGNATURE]['signatory'] + ":" + record[SIGNATURE][HASH] for record in phase_2_records] # TODO: add a structure such as a tuple to pair signatory with it's appropriate hash (signatory, hash) # TODO: and store that instead of lower_phase_hashes also add said structure to phase_3_msg in thrift verification_info = { 'lower_hashes': lower_hashes, 'p2_count': len(signatories), 'businesses': list(businesses), 'deploy_locations': list(locations) } lower_hash = str(deep_hash(lower_hashes)) # sign verification and rewrite record block_info = sign_verification_record(self.network.this_node.node_id, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], phase_2_record[BLOCK_ID], phase_2_record[PHASE], phase_2_record[ORIGIN_ID], int(time.time()), verification_info ) # inserting verification info after signing verfication_db.insert_verification(block_info['verification_record']) self.network.send_block(self.network.phase_3_broadcast, block_info, phase) print "phase 3 executed"
def test_sign_verification_record(self): """ testing crypto sign_verification_record """ test_output = crypto.sign_verification_record(SIGNATORY, PRIOR_BLOCK_HASH, LOWER_HASH, PUBLIC_KEY, PRIVATE_KEY, BLOCK_ID, PHASE, ORIGIN_ID, VERIFICATION_TS, public_transmission=None, verification_info=None) # testing that the generated hash from crypto.sign_verification_record is valid test_signature = crypto.validate_signature(test_output['verification_record']['signature']) self.assertTrue(test_signature, True) # testing that an exception is thrown on invalid hashes test_output['verification_record']['signature']['hash'] = 'invalid_hash' self.assertRaises(BadSignatureError, crypto.validate_signature, test_output['verification_record']['signature'])
def test_validate_verification_record(self): """ testing crypto validate_verification_record """ verification_ts = 1479435043 test_output = crypto.sign_verification_record(SIGNATORY, PRIOR_BLOCK_HASH, LOWER_HASH, PUBLIC_KEY, PRIVATE_KEY, BLOCK_ID, PHASE, ORIGIN_ID, verification_ts, public_transmission=None, verification_info=None) record = test_output['verification_record'] record['verification_ts'] = verification_ts # testing if validate_verification_record passes self.assertEqual(crypto.validate_verification_record(record, None), True) # testing that KeyError is appropriately raised for key in record.keys(): if key is not "verification_info": record.pop(key) self.assertRaises(KeyError, crypto.validate_verification_record, record, None, test_mode=True)
def _execute_phase_4(self, config, phase_3_info): """ external partner notary phase """ phase = 4 phase_3_record = phase_3_info['record'] p3_verification_info = phase_3_info['verification_info'] phase_3_record['verification_info'] = p3_verification_info prior_block_hash = self.get_prior_hash(phase_3_record[ORIGIN_ID], phase) # validate phase_3's verification record if validate_verification_record(phase_3_record, p3_verification_info): # storing valid verification record verification_db.insert_verification(phase_3_record) # updating record phase phase_3_record[PHASE] = phase lower_hash = phase_3_record[SIGNATURE][HASH] verification_info = lower_hash # sign verification and rewrite record block_info = sign_verification_record(self.network.this_node.node_id, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], phase_3_record[BLOCK_ID], phase_3_record[PHASE], phase_3_record[ORIGIN_ID], int(time.time()), phase_3_record['public_transmission'], verification_info ) # inserting verification info after signing verification_id = str(uuid.uuid4()) verification_db.insert_verification(block_info['verification_record'], verification_id) # inserting receipt of signed verification for data transfer vr_transfers_db.insert_transfer(phase_3_record['origin_id'], phase_3_record['signature']['signatory'], verification_id) # send block info off for public transmission if configured to do so if phase_3_record['public_transmission']['p4_pub_trans']: self.network.public_broadcast(block_info, phase) print "phase 4 executed"
def test_sign_verification_record(self): """ testing crypto sign_verification_record """ test_output = crypto.sign_verification_record(SIGNATORY, PRIOR_BLOCK_HASH, LOWER_HASH, PUBLIC_KEY, PRIVATE_KEY, BLOCK_ID, PHASE, ORIGIN_ID, VERIFICATION_TS, public_transmission=None, verification_info=None) # testing that the generated hash from crypto.sign_verification_record is valid test_signature = crypto.validate_signature( test_output['verification_record']['signature']) self.assertTrue(test_signature, True) # testing that exception is thrown on invalid hashes test_output['verification_record']['signature'][ 'hash'] = 'invalid_hash' self.assertRaises(BadSignatureError, crypto.validate_signature, test_output['verification_record']['signature'])
def _execute_phase_1(self, config, current_block_id): """ TODO update all EXEC comments/docs * Each node gathers all transactions that may be included in the prospective block and groups them by transaction owner. * All transactions owned (or sourced from) a respective node's business unit or system (owned) are grouped for approval. * All transactions not owned by the node's business unit or system (others) are grouped for validation. * All owned transactions are verified per business rules, configurable, (e.g, existence or non-existence of particular fields, with field value validation logic). * All owned and verified transactions are "approved" by executing the Transaction Verification Signing Process defined below. * Any transactions deemed "unapproved" will be taken out of the prospective block from a node's perspective by non-inclusion in the signing process, and sent to a system "pool" or "queue" for analysis and alternate processing * All other (non-owned) transactions are validated to system wide rules agreed upon for all nodes through business and system processes. * All other (non-owned) transactions are declared "valid" by the node by executing the Transaction Verification Signing Process defined below. * Any transactions deemed "invalid" will be taken out of the prospective block from a node's perspective by non-inclusion in the signing process, and sent to a system "pool" or "queue" for analysis and alternate processing. """ print("Phase 1 Verify Start.") # Group transactions for last 5 seconds into current block id block_bound_lower_ts = get_block_time(current_block_id - BLOCK_FIXATE_OFFSET) print("""Time bounds: %i - %i""" % (block_bound_lower_ts, block_bound_lower_ts + BLOCK_INTERVAL)) transaction_db.fixate_block(block_bound_lower_ts, block_bound_lower_ts + BLOCK_INTERVAL, current_block_id) if 'approve_block' in config: return config['approve_block'](config, current_block_id) transactions = transaction_db.get_all(block_id=current_block_id) # Validate the schema and structure of the transactions valid_transactions, invalid_transactions = self.split_items( valid_transaction_sig, transactions) # Use the custom approval code if configured, otherwise approve all valid transaction rejected_transactions = [] if 'approve_transaction' in config: approved_transactions, rejected_transactions = \ self.split_items(config['approve_transaction'], valid_transactions) else: approved_transactions = valid_transactions if len(approved_transactions) > 0: # update status of approved transactions for tx in approved_transactions: tx["header"]["status"] = "approved" transaction_db.update_transaction(tx) # stripping payload from all transactions before signing self.strip_payload(approved_transactions) phase = 1 # signatory equals origin_id in phase 1 signatory = origin_id = self.network.this_node.node_id prior_block_hash = self.get_prior_hash(origin_id, phase) verification_info = approved_transactions lower_phase_hash = str(deep_hash(0)) # sign approved transactions block_info = sign_verification_record( signatory, prior_block_hash, lower_phase_hash, self.service_config['public_key'], self.service_config['private_key'], current_block_id, phase, origin_id, int(time.time()), verification_info) # store signed phase specific data verfication_db.insert_verification( block_info['verification_record']) # pass block info to network to send it to appropriate phase self.network.send_block(self.network.phase_1_broadcast, block_info, phase) print("Phase 1 signed " + str(len(approved_transactions)) + " transactions") # update status transactions that were rejected if len(rejected_transactions) > 0: for tx in rejected_transactions: tx["header"]["status"] = "rejected" transaction_db.update_transaction(tx)
def _execute_timestamping(self, config): """ verification_info = { 'verification_records': { origin_id: { 'timestamp_id': hash } }, 'blockchain_type': "BTC" } """ hashes = [] verification_info = { 'verification_records': {}, 'blockchain_type': "BTC" } pending_records = timestamp_db.get_pending_timestamp() # returns out of the function if there are no records waiting to be broadcast if not pending_records: return # creates a list of hashes as well as builds the verification_info structure for r in pending_records: hashes.append(r['signature']['hash']) # organizes the verification records by origin_id with a dictionary of timestamp_ids and hashes if r['origin_id'] not in verification_info['verification_records']: verification_info['verification_records'][r['origin_id']] = {} verification_info['verification_records'][r['origin_id']][r['timestamp_id']] = r['signature']['hash'] # takes the list of hashes to be transmitted and hashes with 256 bit to get in form to send transaction_hash = final_hash(hashes, type=256) # normal SHA512 hash for all lower VR contents lower_hash = final_hash(hashes) # sets the hash in the verification_info structure to the hash we just generated verification_info['hash'] = transaction_hash stamper = BitcoinTimestamper(self.service_config['bitcoin_network'], BitcoinFeeProvider()) bitcoin_tx_id = stamper.persist("%s%s" % (LEVEL5_PREFIX, transaction_hash)) bitcoin_tx_id = b2lx(bitcoin_tx_id).encode('utf-8') verification_info['public_transaction_id'] = bitcoin_tx_id prior_block_hash = None # This is the hash of all of the lower elements block_id = None phase = 5 origin_id = None public_transmission = False block_info = sign_verification_record(self.network.this_node.node_id, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], block_id, phase, origin_id, int(time.time()), public_transmission, verification_info ) # inserts a new verification record for the new record created to be sent verification_db.insert_verification(block_info['verification_record']) # sets the timestamp_receipt to true to indicate the records have been sent for origin_id in verification_info['verification_records'].keys(): for verification_id in verification_info['verification_records'][origin_id]: timestamp_db.set_transaction_timestamp_proof(verification_id) # dictionary where the key is origin_id and the value is list of signatories sent from that origin_id unique_vr_transfers = {} # creates a verification_record for each unique origin_id-signatory pair for verification_record in pending_records: if not verification_record['origin_id'] in unique_vr_transfers: unique_vr_transfers[verification_record['origin_id']] = [] # inserts a single record per origin_id if verification_record['signature']['signatory'] not in unique_vr_transfers[verification_record['origin_id']]: vr_transfers_db.insert_transfer(verification_record['origin_id'], verification_record['signature']['signatory'], verification_record['timestamp_id']) unique_vr_transfers[verification_record['origin_id']].append(verification_record['signature']['signatory'])
def _execute_phase_3(self, config, phase_2_info): """ * At this point, any participating processing nodes will have appended signed Phase 2 verification proof to the block. * Processing nodes participating in Phase 3 Verification may be a different set of nodes than the set of nodes participating in Phase 1 and Phase 2 Verification processes. * Processing nodes may be defined for the sole purpose of Phase 3 verification (e.g. for independent blockchain verification auditing purposes). * A participating node will verify that no invalid transaction has been included in the set of approved transaction. * A participating node will verify that all "approved" transactions are signed by their respective owner. * A node may perform extra validation steps on all transactions and verification units. * All signed "Phase 3 Signature Structures" will be grouped, concatenated, and cryptographically signed by the node. """ phase = 3 phase_2_record = phase_2_info['record'] p2_verification_info = phase_2_info['verification_info'] phase_2_record['verification_info'] = p2_verification_info prior_block_hash = self.get_prior_hash(phase_2_record[ORIGIN_ID], phase) # validate phase_2's verification record if validate_verification_record(phase_2_record, p2_verification_info): # storing valid verification record verification_db.insert_verification(phase_2_record) # retrieve all phase 2 records for current block phase_2_records = self.get_sig_records(phase_2_record) signatories, businesses, locations = self.get_verification_diversity(phase_2_records) # checking if passed requirements to move on to next phase if len(signatories) >= P2_COUNT_REQ and len(businesses) >= P2_BUS_COUNT_REQ and len(locations) >= P2_LOC_COUNT_REQ: # updating record phase phase_2_record[PHASE] = phase lower_hashes = [record[SIGNATURE]['signatory'] + ":" + record[SIGNATURE][HASH] for record in phase_2_records] verification_info = { 'lower_hashes': lower_hashes, 'p2_count': len(signatories), 'businesses': list(businesses), 'deploy_locations': list(locations) } lower_hash = str(final_hash(lower_hashes)) # sign verification and rewrite record block_info = sign_verification_record(self.network.this_node.node_id, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], phase_2_record[BLOCK_ID], phase_2_record[PHASE], phase_2_record[ORIGIN_ID], int(time.time()), phase_2_record['public_transmission'], verification_info ) # inserting verification info after signing verification_id = str(uuid.uuid4()) verification_db.insert_verification(block_info['verification_record'], verification_id) # inserting receipt for each phase 2 record received for record in phase_2_records: vr_transfers_db.insert_transfer(record['origin_id'], record['signature']['signatory'], verification_id) # send block info off for public transmission if configured to do so if phase_2_record['public_transmission']['p3_pub_trans']: self.network.public_broadcast(block_info, phase) # send block info for phase 4 validation self.network.send_block(self.network.phase_3_broadcast, block_info, phase) print "phase 3 executed"
def _execute_phase_2(self, config, phase_1_info): """ * At this point, any participating processing nodes will have appended signed "approvals" of their respectively owned transactions and signed "validations" of un-owned transactions (see Phase 1 Verification Process). * Processing nodes participating in Phase 2 Verification may be a different set of nodes than the set of nodes participating in Phase 1 Verification. * Processing nodes may be defined for the sole purpose of Phase 2 verification (without any "owned" transactions in the system). * A node participating in Phase 2 verification will verify that all transactions in the prospective block are "approved" by their respective owners and are not declared invalid by a system configured portion (e.g. percentage, plurality, or majority). * Any transactions which are not approved will be taken out of the prospective block and "bumped" to the next block now - (d * T) for Phase 1 processing. * If a non-approved transaction which is older than a system configured time for the transaction type and owner, the transaction will not be "bumped" to the next block, but instead be placed in a system "pool" or "queue" for later handling or alternate processing. * All signed "approval" verification units will be grouped, concatenated, and cryptographically signed. * A "Phase 2 signature structure" is created and appended to the block. """ """ - check block sig - req: tx_type, tx_id, tx_ts, owner (origin - original phase_1 node_id), trans_sig - tx minus the payload """ phase = 2 phase_1_record = phase_1_info["record"] p1_verification_info = phase_1_info["verification_info"] phase_1_record['verification_info'] = p1_verification_info prior_block_hash = self.get_prior_hash(phase_1_record[ORIGIN_ID], phase) # validate phase_1's verification record if validate_verification_record(phase_1_record, p1_verification_info): # storing valid verification record verification_db.insert_verification(phase_1_record) # updating record phase phase_1_record[PHASE] = phase valid_transactions, invalid_transactions = self.check_tx_requirements(p1_verification_info) verification_info = { 'valid_txs': valid_transactions, 'invalid_txs': invalid_transactions, 'business': self.network.business, 'deploy_location': self.network.deploy_location } lower_hash = phase_1_record[SIGNATURE][HASH] # sign verification and rewrite record block_info = sign_verification_record(self.network.this_node.node_id, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], phase_1_record[BLOCK_ID], phase_1_record[PHASE], phase_1_record[ORIGIN_ID], int(time.time()), phase_1_record['public_transmission'], verification_info ) # inserting verification info after signing verification_id = str(uuid.uuid4()) verification_db.insert_verification(block_info['verification_record'], verification_id) # inserting receipt of signed verification for data transfer vr_transfers_db.insert_transfer(phase_1_record['origin_id'], phase_1_record['signature']['signatory'], verification_id) # send block info off for public transmission if configured to do so if phase_1_record['public_transmission']['p2_pub_trans']: self.network.public_broadcast(block_info, phase) # send block info for phase 3 validation self.network.send_block(self.network.phase_2_broadcast, block_info, phase) print "phase_2 executed"
def _execute_phase_1(self, config, current_block_id): """ TODO update all EXEC comments/docs * Each node gathers all transactions that may be included in the prospective block and groups them by transaction owner. * All transactions owned (or sourced from) a respective node's business unit or system (owned) are grouped for approval. * All transactions not owned by the node's business unit or system (others) are grouped for validation. * All owned transactions are verified per business rules, configurable, (e.g, existence or non-existence of particular fields, with field value validation logic). * All owned and verified transactions are "approved" by executing the Transaction Verification Signing Process defined below. * Any transactions deemed "unapproved" will be taken out of the prospective block from a node's perspective by non-inclusion in the signing process, and sent to a system "pool" or "queue" for analysis and alternate processing * All other (non-owned) transactions are validated to system wide rules agreed upon for all nodes through business and system processes. * All other (non-owned) transactions are declared "valid" by the node by executing the Transaction Verification Signing Process defined below. * Any transactions deemed "invalid" will be taken out of the prospective block from a node's perspective by non-inclusion in the signing process, and sent to a system "pool" or "queue" for analysis and alternate processing. """ print("Phase 1 Verify Start.") # Group transactions for last 5 seconds into current block id block_bound_lower_ts = get_block_time(current_block_id - BLOCK_FIXATE_OFFSET) print ("""Time bounds: %i - %i""" % (block_bound_lower_ts, block_bound_lower_ts + BLOCK_INTERVAL)) transaction_db.fixate_block(block_bound_lower_ts, block_bound_lower_ts + BLOCK_INTERVAL, current_block_id) transactions = transaction_db.get_all(block_id=current_block_id) # Validate the schema and structure of the transactions valid_transactions, invalid_transactions = self.split_items(valid_transaction_sig, transactions) rejected_transactions = [] approved_transactions = [] for txn in valid_transactions: if self.handle_transaction(txn): approved_transactions.append(txn) else: rejected_transactions.append(txn) if len(approved_transactions) > 0: # update status of approved transactions for tx in approved_transactions: tx["header"]["status"] = "approved" transaction_db.update_transaction(tx) # stripping payload from all transactions before signing self.strip_payload(approved_transactions) phase = 1 # signatory equals origin_id in phase 1 signatory = origin_id = self.network.this_node.node_id prior_block_hash = self.get_prior_hash(origin_id, phase) verification_info = approved_transactions lower_hash = str(final_hash([0])) # sign approved transactions block_info = sign_verification_record(signatory, prior_block_hash, lower_hash, self.service_config['public_key'], self.service_config['private_key'], current_block_id, phase, origin_id, int(time.time()), self.public_transmission, verification_info) # store signed phase specific data verification_id = str(uuid.uuid4()) verification_db.insert_verification(block_info['verification_record'], verification_id) # send block info off for public transmission if configured to do so if block_info['verification_record']['public_transmission']['p1_pub_trans']: self.network.public_broadcast(block_info, phase) # send block info for phase 2 validation self.network.send_block(self.network.phase_1_broadcast, block_info, phase) print("Phase 1 signed " + str(len(approved_transactions)) + " transactions") # update status transactions that were rejected if len(rejected_transactions) > 0: for tx in rejected_transactions: tx["header"]["status"] = "rejected" transaction_db.update_transaction(tx)