Given a transaction-ordered sequence of parsed operations,
        check their validity and give them to the state engine to 
        affect state changes.

        It calls 'db_check' to validate each operation, and 'db_commit'
        to add it to the state engine.  Gets back a list of state
        transitions (ops) to snapshot.

        Returns a defaultdict with the following fields:
        'virtualchain_ordered':  the list of operations committed by the implementation (where each operation is a dict of fields)
        'virtualchain_all_ops':  this is ops, plus a list containing the "final" operations returned by the implementation in response to the 'virtualchain_final' hint
        'virtualchain_final':  this is the list of final operations returned by the implementation in response to the 'virtualchain_final' hint.

        Aborts on error

def process_ops(self, block_id, ops):
        """
        Given a transaction-ordered sequence of parsed operations,
        check their validity and give them to the state engine to 
        affect state changes.

        It calls 'db_check' to validate each operation, and 'db_commit'
        to add it to the state engine.  Gets back a list of state
        transitions (ops) to snapshot.

        Returns a defaultdict with the following fields:
        'virtualchain_ordered':  the list of operations committed by the implementation (where each operation is a dict of fields)
        'virtualchain_all_ops':  this is ops, plus a list containing the "final" operations returned by the implementation in response to the 'virtualchain_final' hint
        'virtualchain_final':  this is the list of final operations returned by the implementation in response to the 'virtualchain_final' hint.

        Aborts on error
        """

        new_ops = defaultdict(list)

        for op in self.opcodes:
            new_ops[op] = []

        # transaction-ordered listing of accepted operations
        new_ops['virtualchain_ordered'] = []
        new_ops['virtualchain_all_ops'] = ops

        to_commit_sanitized = []
        to_commit_reserved = []

        # let the implementation do an initial scan over the blocks
        # NOTE: these will be different objects in memory from the objects passed into db_check
        initial_scan = []
        for i in xrange(0, len(ops)):

            op_data = ops[i]
            op_sanitized, _ = self.remove_reserved_keys( op_data )
            initial_scan.append( copy.deepcopy( op_sanitized ) )

        # allow the implementation to do a pre-scan of the set of ops 
        # (e.g. in Blockstack, this gets used to find name registration collisions)
        if hasattr(self.impl, "db_scan_block"):
            self.impl.db_scan_block( block_id, initial_scan, db_state=self.state )
        else:
            log.debug("Compat: no db_scan_block")

        # check each operation 
        for i in range(0, len(ops)):
            op_data = ops[i]
            op_sanitized, reserved = self.remove_reserved_keys( op_data )
            opcode = reserved['virtualchain_opcode']

            # check this op
            rc = self.impl.db_check(block_id, new_ops, opcode, op_sanitized, reserved['virtualchain_txid'], reserved['virtualchain_txindex'], to_commit_sanitized, db_state=self.state)
            if rc:

                # commit this op
                new_op_list = self.impl.db_commit(block_id, opcode, op_sanitized, reserved['virtualchain_txid'], reserved['virtualchain_txindex'], db_state=self.state)
                if type(new_op_list) != list:
                    new_op_list = [new_op_list]

                for new_op in new_op_list:
                    if new_op is not None:
                        if type(new_op) == dict:

                            # externally-visible state transition 
                            to_commit_sanitized_op = copy.deepcopy( new_op )
                            to_commit_sanitized.append( to_commit_sanitized_op )

                            new_op.update( reserved )
                            new_ops[opcode].append( new_op )
                            new_ops['virtualchain_ordered'].append( new_op )

                        else:
                            # internal state transition 
                            continue

            else:
                self.log_reject( block_id, reserved['virtualchain_txindex'], opcode, copy.deepcopy(op_sanitized))

        
        # final commit hint.
        # the implementation has a chance here to feed any extra data into the consensus hash with this call
        # (e.g. to affect internal state transitions that occur as seconary, holistic consequences to the sequence
        # of prior operations for this block).
        final_ops = self.impl.db_commit( block_id, 'virtualchain_final', {'virtualchain_ordered': new_ops['virtualchain_ordered']}, None, None, db_state=self.state )
        if final_ops is not None:
            # make sure each one has all the virtualchain reserved fields
            for i in range(0, len(final_ops)):
                for fieldname in RESERVED_FIELDS:
                    assert fieldname in final_ops[i], 'Extra consensus operation at offset {} is missing {}'.format(i, fieldname)

            new_ops['virtualchain_final'] = final_ops
            new_ops['virtualchain_ordered'] += final_ops
            new_ops['virtualchain_all_ops'] += final_ops

        return new_ops