<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def parse_transaction(self, block_id, tx):
 """ Given a block ID and an data-bearing transaction, try to parse it into a virtual chain operation. Use the implementation's 'db_parse' method to do so. Data transactions that do not have the magic bytes or a valid opcode will be skipped automatically. The db_parse method does not need to know how to handle them. @tx is a dict with `txid`: the transaction ID `txindex`: the offset in the block where this tx occurs `nulldata`: the hex-encoded scratch data from the transaction `ins`: the list of transaction inputs `outs`: the list of transaction outputs `senders`: the list of transaction senders `fee`: the transaction fee `txhex`: the hex-encoded raw transaction Return a dict representing the data on success. Return None on error """

        
        data_hex = tx['nulldata']
        inputs = tx['ins']
        outputs = tx['outs']
        senders = tx['senders']
        fee = tx['fee']
        txhex = tx['hex']
        merkle_path = tx['tx_merkle_path']
        
        if not is_hex(data_hex):
            # should always work; the tx downloader converts the binary string to hex
            # not a valid hex string 
            raise ValueError("Invalid nulldata: not hex-encoded")
        
        if len(data_hex) % 2 != 0:
            # should always work; the tx downloader converts the binary string to hex
            # not valid hex string 
            raise ValueError("Invalid nulldata: not hex-encoded")
        
        data_bin = None
        try:
            # should always work; the tx downloader converts the binary string to hex
            data_bin = data_hex.decode('hex')
        except Exception, e:
            log.error("Failed to parse transaction: %s (data_hex = %s)" % (tx, data_hex))
            raise ValueError("Invalid nulldata: not hex-encoded")
        
        if not data_bin.startswith(self.magic_bytes):
            # not for us
            return None
        
        if len(data_bin) < len(self.magic_bytes) + 1:
            # invalid operation--no opcode
            return None

        # 3rd byte is always the operation code
        op_code = data_bin[len(self.magic_bytes)]
        if op_code not in self.opcodes:
            return None 
        
        # looks like an op.  Try to parse it.
        op_payload = data_bin[len(self.magic_bytes)+1:]
        
        op = self.impl.db_parse(block_id, tx['txid'], tx['txindex'], op_code, op_payload, senders, inputs, outputs, fee, db_state=self.state, raw_tx=txhex)
        if op is None:
            # not valid 
            return None 
        
        # store it
        op['virtualchain_opcode'] = op_code
        op['virtualchain_txid'] = tx['txid']
        op['virtualchain_txindex'] = tx['txindex']
        op['virtualchain_txhex'] = txhex
        op['virtualchain_tx_merkle_path'] = merkle_path
        op['virtualchain_senders'] = senders
        op['virtualchain_fee'] = fee
        op['virtualchain_data_hex'] = op_payload.encode('hex')
        
        return op