Skip to content

fix(storage): allow getting old blocks from storage #2595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions node/src/actors/inventory_manager/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,27 @@ impl InventoryManager {
Hash::SHA256(x) => x.to_vec(),
};

let fut = storage_mngr::get::<_, Block>(&key)
// This closure fills in the missing bytes for backwards-compatibility of blocks encoded
// with V1_X using bincode. In particular, it checks whether the serialized bytes contain
// staking/unstaking merkle roots
let fix = |bytes: Vec<u8>| {
if bytes[260..264] == [0x51, 0x00, 0x00, 0x00] {
[&bytes[..260], &vec![0u8; 72], &bytes[260..], &vec![0u8; 16]].concat()
} else {
bytes
}
};

// Uses `mapped_get` to alter the bytes before deserialization. Please note that this is
// not a proper migration, but rather a workaround that has a little performance penalty
let fut = storage_mngr::mapped_get::<_, Block, _>(&key, fix)
.into_actor(self)
.then(move |res, _, _| match res {
Ok(opt) => match opt {
None => fut::err(InventoryManagerError::ItemNotFound),
Some(block) => fut::ok(block),
},
Err(e) => {
log::error!("Couldn't get item from storage: {}", e);

fut::err(InventoryManagerError::MailBoxError(e))
}
Err(e) => fut::err(InventoryManagerError::MailBoxError(e)),
});

Box::pin(fut)
Expand Down Expand Up @@ -390,6 +399,10 @@ impl Handler<GetItemSuperblock> for InventoryManager {
mod tests {
use std::sync::Arc;

use crate::{
actors::chain_manager::mining::build_block, config_mngr, storage_mngr,
utils::test_actix_system,
};
use witnet_config::config::{Config, StorageBackend};
use witnet_data_structures::{
chain::{
Expand All @@ -403,11 +416,6 @@ mod tests {
vrf::BlockEligibilityClaim,
};

use crate::{
actors::chain_manager::mining::build_block, config_mngr, storage_mngr,
utils::test_actix_system,
};

use super::*;

const CHECKPOINT_ZERO_TIMESTAMP: i64 = 1_602_666_000;
Expand Down
17 changes: 16 additions & 1 deletion node/src/storage_mngr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ pub fn get<K, T>(key: &K) -> impl Future<Output = Result<Option<T>, failure::Err
where
K: serde::Serialize,
T: serde::de::DeserializeOwned + 'static,
{
mapped_get(key, |bytes| bytes)
}

/// Get value associated to a key, with the superpower of mutating the bytes on the fly before
/// attempting deserialization. This allows for pseudo-migration of data structures that were
/// stored using a former implementation or serialization method.
pub fn mapped_get<K, T, F>(
key: &K,
mapper: F,
) -> impl Future<Output = Result<Option<T>, failure::Error>>
where
K: serde::Serialize,
T: serde::de::DeserializeOwned + 'static,
F: FnMut(Vec<u8>) -> Vec<u8>,
{
// Check that we don't accidentally use this function with some certain special types
if TypeId::of::<T>() == TypeId::of::<ChainState>() {
Expand All @@ -65,7 +80,7 @@ where
};

let fut = async move {
let opt = addr.send(Get(key_bytes)).await??;
let opt = addr.send(Get(key_bytes)).await??.map(mapper);

match opt {
Some(bytes) => match deserialize(bytes.as_slice()) {
Expand Down
Loading