uint32_t handle_storage_rx_inconsistent(uint16_t handle, uint32_t timestamp)
{
    if (handle == RBC_MESH_INVALID_HANDLE)
    {
        return NRF_ERROR_INVALID_ADDR;
    }

    uint16_t handle_index = handle_entry_get(handle, true);
    if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
    {
        return NRF_ERROR_NOT_FOUND;
    }

    uint16_t data_index = m_handle_cache[handle_index].data_entry;

    if (data_index == DATA_CACHE_ENTRY_INVALID)
    {
        return NRF_ERROR_NOT_FOUND;
    }

    trickle_rx_inconsistent(&m_data_cache[data_index].trickle, timestamp);

    return NRF_SUCCESS;
}
uint32_t mesh_srv_packet_process(packet_t* packet)
{
    if (!is_initialized)
    {
        return NRF_ERROR_INVALID_STATE;
    }
    uint32_t error_code;
    
    uint8_t handle = packet->data[MESH_PACKET_HANDLE_OFFSET];
    uint16_t version = (packet->data[MESH_PACKET_VERSION_OFFSET] | 
                    (((uint16_t) packet->data[MESH_PACKET_VERSION_OFFSET + 1]) << 8));
    uint8_t* data = &packet->data[MESH_PACKET_DATA_OFFSET];
    uint16_t data_len = packet->length - MESH_PACKET_DATA_OFFSET;
    
    if (data_len > MAX_VALUE_LENGTH)
    {
        return NRF_ERROR_INVALID_LENGTH;
    }
    
    if (handle > g_mesh_service.value_count || handle == 0)
    {
        return NRF_ERROR_INVALID_ADDR;
    }
    
    
    mesh_char_metadata_t* ch_md = &g_mesh_service.char_metadata[handle - 1];
    
    bool uninitialized = !(ch_md->flags & (1 << MESH_MD_FLAGS_INITIALIZED_POS));
    
    if (uninitialized)
    {
        trickle_init(&ch_md->trickle);
    }
    
    if (ch_md->version_number != version)
    {
        trickle_rx_inconsistent(&ch_md->trickle);
    }
    
    /* new version */  
    uint16_t separation = (version >= ch_md->version_number)?
        (version - ch_md->version_number) : 
        (-(ch_md->version_number - MESH_VALUE_LOLLIPOP_LIMIT) + (version - MESH_VALUE_LOLLIPOP_LIMIT) - MESH_VALUE_LOLLIPOP_LIMIT);

    if ((ch_md->version_number < MESH_VALUE_LOLLIPOP_LIMIT && version >= ch_md->version_number) || 
        (ch_md->version_number >= MESH_VALUE_LOLLIPOP_LIMIT && separation < (UINT16_MAX - MESH_VALUE_LOLLIPOP_LIMIT)/2) || 
        uninitialized)
    {
        /* update value */
        mesh_srv_char_val_set(handle, data, data_len, false);
        ch_md->flags |= (1 << MESH_MD_FLAGS_INITIALIZED_POS);
        ch_md->flags &= ~(1 << MESH_MD_FLAGS_IS_ORIGIN_POS);
        ch_md->version_number = version;
        
        /* Manually set originator address */
        memcpy(&ch_md->last_sender_addr, &packet->sender, sizeof(ble_gap_addr_t));
        
        rbc_mesh_event_t update_evt;
        update_evt.event_type = ((uninitialized)? 
            RBC_MESH_EVENT_TYPE_NEW_VAL :
            RBC_MESH_EVENT_TYPE_UPDATE_VAL);
        update_evt.data_len = data_len;
        update_evt.value_handle = handle;
        
        update_evt.data = data;
        memcpy(&update_evt.originator_address, &packet->sender, sizeof(ble_gap_addr_t));
        
        rbc_mesh_event_handler(&update_evt);
#ifdef RBC_MESH_SERIAL
            mesh_aci_rbc_event_handler(&update_evt);
#endif
    }
    else if (version == ch_md->version_number)
    {
        /* check for conflicting data */
        uint16_t old_len = MAX_VALUE_LENGTH;
        
        error_code = mesh_srv_char_val_get(handle, NULL, &old_len, NULL);
        if (error_code != NRF_SUCCESS)
        {
            return error_code;
        }
        
        volatile bool conflicting = false;
        
        if (packet->rx_crc != ch_md->crc && 
            !(ch_md->flags & (1 << MESH_MD_FLAGS_IS_ORIGIN_POS)))
        {
            conflicting = true;
        }
        else if (old_len != data_len)
        {
            conflicting = true;
        }
        
        
        if (conflicting)
        {
            TICK_PIN(7);
            rbc_mesh_event_t conflicting_evt;
            
            conflicting_evt.event_type = RBC_MESH_EVENT_TYPE_CONFLICTING_VAL;
            
            conflicting_evt.data_len = data_len;
            conflicting_evt.value_handle = handle;
            
            conflicting_evt.data = data;
            memcpy(&conflicting_evt.originator_address, &packet->sender, sizeof(ble_gap_addr_t));
            
            trickle_rx_inconsistent(&ch_md->trickle);
            
            rbc_mesh_event_handler(&conflicting_evt);
#ifdef RBC_MESH_SERIAL
            mesh_aci_rbc_event_handler(&conflicting_evt);
#endif
        }
        else
        { 
            trickle_rx_consistent(&ch_md->trickle);
        }
        
    }
    
    
    ch_md->crc = packet->rx_crc;
    
    return NRF_SUCCESS;
}
uint32_t mesh_srv_char_val_set(uint8_t index, uint8_t* data, uint16_t len, bool update_sender)
{
    if (!is_initialized)
    {
        return NRF_ERROR_INVALID_STATE;
    }
    
    if (index > g_mesh_service.value_count || index == 0)
    {
        return NRF_ERROR_INVALID_ADDR;
    }
    
    if (len > MAX_VALUE_LENGTH)
    {
        return NRF_ERROR_INVALID_LENGTH;
    }
    uint32_t error_code = 0;
    
    mesh_char_metadata_t* ch_md = &g_mesh_service.char_metadata[index - 1];
    
    /* this is now a new version of this data, signal to the rest of the mesh */
    ++ch_md->version_number;
    
    bool first_time = 
        (ch_md->flags & 
        (1 << MESH_MD_FLAGS_USED_POS)) == 0;
    
    if (first_time)
    {
        ch_md->flags |= 
            (1 << MESH_MD_FLAGS_INITIALIZED_POS) |
            (1 << MESH_MD_FLAGS_USED_POS);
        trickle_init(&ch_md->trickle);
    }
    else
    {
        trickle_rx_inconsistent(&ch_md->trickle);
    }
    
    if (update_sender || first_time)
    {
        ble_gap_addr_t my_addr;
        sd_ble_gap_address_get(&my_addr);
        memcpy(&ch_md->last_sender_addr, &my_addr, sizeof(ble_gap_addr_t));
        ch_md->flags |= (1 << MESH_MD_FLAGS_IS_ORIGIN_POS);
    }
        
    /* notify the connected central node, if any */
    if (g_active_conn_handle != CONN_HANDLE_INVALID)
    {
        ble_gatts_hvx_params_t notify_params;
        notify_params.handle = ch_md->char_value_handle;
        notify_params.offset = 0;
        notify_params.p_data = data;
        notify_params.p_len = &len;
        notify_params.type = BLE_GATT_HVX_NOTIFICATION;
        error_code = sd_ble_gatts_hvx(g_active_conn_handle, &notify_params);
        if (error_code != NRF_SUCCESS)
        {
            if (error_code == BLE_ERROR_INVALID_CONN_HANDLE)
            {
                g_active_conn_handle = CONN_HANDLE_INVALID;
            }
            else if (error_code == BLE_GATTS_EVT_SYS_ATTR_MISSING)
            {
                sd_ble_gatts_sys_attr_set(g_active_conn_handle, NULL, 0);
            }
            else
            {
                return NRF_ERROR_INTERNAL;
            }
        }
    }
    else
    {
        error_code = sd_ble_gatts_value_set(
            ch_md->char_value_handle, 
            0, &len, data);
        
        if (error_code != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }
    }
    
    return NRF_SUCCESS;
}