Given an operation (opcode, op_data), a point in time (block_id, vtxindex, txid), and a current
    record (history_id, cur_record), apply the operation to the record and save the delta to the record's
    history.  Also, insert or update the new record into the db.

    The cur_record must exist already.

    Return the newly updated record on success, with all compatibility quirks preserved.
    Raise an exception on failure.

    DO NOT CALL THIS METHOD DIRECTLY.

def namedb_state_transition( cur, opcode, op_data, block_id, vtxindex, txid, history_id, cur_record, record_table, constraints_ignored=[] ):
    """
    Given an operation (opcode, op_data), a point in time (block_id, vtxindex, txid), and a current
    record (history_id, cur_record), apply the operation to the record and save the delta to the record's
    history.  Also, insert or update the new record into the db.

    The cur_record must exist already.

    Return the newly updated record on success, with all compatibility quirks preserved.
    Raise an exception on failure.

    DO NOT CALL THIS METHOD DIRECTLY.
    """
    
    # sanity check: must be a state-transitioning operation
    try:
        assert opcode in OPCODE_NAME_STATE_TRANSITIONS + OPCODE_NAMESPACE_STATE_TRANSITIONS, "BUG: opcode '%s' is not a state-transition"
        assert 'opcode' not in op_data, 'BUG: opcode not allowed in op_data'
    except Exception, e:
        log.exception(e)
        log.error("BUG: opcode '%s' is not a state-transition operation" % opcode)
        os.abort()

    # make sure we have a name/namespace_id and block number
    op_data_name = copy.deepcopy(op_data)

    if opcode in OPCODE_NAME_STATE_TRANSITIONS:
        # name state transition 
        op_data_name['name'] = history_id

    elif opcode in OPCODE_NAMESPACE_STATE_TRANSITIONS:
        # namespace state transition 
        op_data_name['namespace_id'] = history_id

    # sanity check make sure we got valid state transition data
    try:
        assert cur_record.has_key('block_number'), 'current record does not have a block number'
        op_data_name['block_number'] = cur_record['block_number']

        rc = namedb_state_transition_sanity_check( opcode, op_data_name, history_id, cur_record, record_table )
        if not rc:
            raise Exception("State transition sanity checks failed")

        rc = namedb_state_mutation_sanity_check( opcode, op_data_name )
        if not rc:
            raise Exception("State mutation sanity checks failed")

    except Exception, e:
        log.exception(e)
        log.error("FATAL: state transition sanity checks failed")
        os.abort()

    # 1. generate the new record that will be used for consensus.
    # It will be the new data overlayed on the current record, with all quirks applied.
    new_record = {}
    new_record.update(cur_record)
    new_record.update(op_data_name)
    new_record['opcode'] = opcode

    canonicalized_record = op_canonicalize_quirks(opcode, new_record, cur_record)
    canonicalized_record['opcode'] = opcode
    
    rc = namedb_history_save(cur, opcode, history_id, None, new_record.get('value_hash', None), block_id, vtxindex, txid, canonicalized_record)
    if not rc:
        log.error("FATAL: failed to save history for '%s' at (%s, %s)" % (history_id, block_id, vtxindex))
        os.abort()

    rc = False
    merged_new_record = None
    
    # 2. Store the actual op_data, to be returned on name lookups
    # Don't store extra fields that don't belong in the db (i.e. that we don't have colunms for), but preserve them across the write.
    stored_op_data = {}
    stored_op_data.update(op_data_name)

    # separate out the extras
    _, op_data_extra = namedb_find_missing_and_extra(cur, stored_op_data, record_table)
    if len(op_data_extra) > 0:
        log.debug("Remove extra fields: {}".format(','.join(op_data_extra)))
        for extra in op_data_extra:
            del stored_op_data[extra]
    
    if opcode in OPCODE_NAME_STATE_TRANSITIONS:
        # name state transition 
        rc = namedb_name_update( cur, opcode, stored_op_data, constraints_ignored=constraints_ignored )
        if not rc:
            log.error("FATAL: opcode is not a state-transition operation on names")
            os.abort()

        merged_new_record = namedb_get_name(cur, history_id, block_id, include_history=False, include_expired=True)

    elif opcode in OPCODE_NAMESPACE_STATE_TRANSITIONS:
        # namespace state transition 
        rc = namedb_namespace_update( cur, opcode, stored_op_data, constraints_ignored=constraints_ignored )
        if not rc:
            log.error("FATAL: opcode is not a state-transition operation on namespaces")
            os.abort()

        merged_new_record = namedb_get_namespace(cur, history_id, block_id, include_history=False, include_expired=True)

    # 3. success! make sure the merged_new_record is consistent with canonicalized_record
    for f in merged_new_record:
        if f not in canonicalized_record:
            raise Exception("canonicalized record is missing {}".format(f))

    return canonicalized_record