Skip to content
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

blockstore: relax backwards chained merkle root check for upgrades #1163

Merged
merged 2 commits into from
May 6, 2024
Merged
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
115 changes: 113 additions & 2 deletions ledger/src/blockstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1883,7 +1883,7 @@ impl Blockstore {
return true;
};

let prev_merkle_root_meta = merkle_root_metas
let Some(prev_merkle_root_meta) = merkle_root_metas
.get(&prev_erasure_set)
.map(WorkingEntry::as_ref)
.map(Cow::Borrowed)
Expand All @@ -1892,7 +1892,14 @@ impl Blockstore {
.unwrap()
.map(Cow::Owned)
})
.expect("merkle root meta must exist for erasure meta");
else {
warn!(
"The merkle root meta for the previous erasure set {prev_erasure_set:?} does not exist.
This should only happen if you have recently upgraded from a version < v1.18.13.
Skipping the backwards chained merkle root for {erasure_set:?}"
);
return true;
};
let prev_shred_id = ShredId::new(
slot,
prev_merkle_root_meta.first_received_shred_index(),
Expand Down Expand Up @@ -11588,4 +11595,108 @@ pub mod tests {
)
);
}

#[test]
fn test_chained_merkle_root_upgrade_inconsistency_backwards() {
// Insert a coding shred (without a merkle meta) then inconsistent shreds from the next FEC set
let ledger_path = get_tmp_ledger_path_auto_delete!();
let blockstore = Blockstore::open(ledger_path.path()).unwrap();

let parent_slot = 0;
let slot = 1;
let fec_set_index = 0;
let (data_shreds, coding_shreds, leader_schedule) =
setup_erasure_shreds_with_index(slot, parent_slot, 10, fec_set_index);
let coding_shred_previous = coding_shreds[0].clone();
let next_fec_set_index = fec_set_index + data_shreds.len() as u32;

assert!(blockstore
.insert_shred_return_duplicate(coding_shred_previous.clone(), &leader_schedule,)
.is_empty());

// Remove the merkle root meta in order to simulate this blockstore originating from
// an older version.
let mut write_batch = blockstore.db.batch().unwrap();
blockstore
.db
.delete_range_cf::<cf::MerkleRootMeta>(&mut write_batch, slot, slot)
.unwrap();
blockstore.db.write(write_batch).unwrap();
assert!(blockstore
.merkle_root_meta(coding_shred_previous.erasure_set())
.unwrap()
.is_none());

// Add an incorrectly chained merkle from the next set. Although incorrectly chained
// we skip the duplicate check as the merkle root meta is missing.
let merkle_root = Hash::new_unique();
assert!(merkle_root != coding_shred_previous.merkle_root().unwrap());
let (data_shreds, coding_shreds, leader_schedule) =
setup_erasure_shreds_with_index_and_chained_merkle(
slot,
parent_slot,
10,
next_fec_set_index,
Some(merkle_root),
);
let data_shred = data_shreds[0].clone();
let coding_shred = coding_shreds[0].clone();
assert!(blockstore
.insert_shred_return_duplicate(coding_shred, &leader_schedule)
.is_empty());
assert!(blockstore
.insert_shred_return_duplicate(data_shred, &leader_schedule,)
.is_empty());
}

#[test]
fn test_chained_merkle_root_upgrade_inconsistency_forwards() {
// Insert a data shred (without a merkle root), then an inconsistent coding shred from the previous FEC set.
let ledger_path = get_tmp_ledger_path_auto_delete!();
let blockstore = Blockstore::open(ledger_path.path()).unwrap();

let parent_slot = 0;
let slot = 1;
let fec_set_index = 0;
let (data_shreds, coding_shreds, leader_schedule) =
setup_erasure_shreds_with_index(slot, parent_slot, 10, fec_set_index);
let coding_shred = coding_shreds[0].clone();
let next_fec_set_index = fec_set_index + data_shreds.len() as u32;

// Incorrectly chained merkle
let merkle_root = Hash::new_unique();
assert!(merkle_root != coding_shred.merkle_root().unwrap());
let (next_data_shreds, next_coding_shreds, leader_schedule_next) =
setup_erasure_shreds_with_index_and_chained_merkle(
slot,
parent_slot,
10,
next_fec_set_index,
Some(merkle_root),
);
let next_data_shred = next_data_shreds[0].clone();

assert!(blockstore
.insert_shred_return_duplicate(next_data_shred, &leader_schedule_next,)
.is_empty());

// Remove the merkle root meta in order to simulate this blockstore originating from
// an older version.
let mut write_batch = blockstore.db.batch().unwrap();
blockstore
.db
.delete_range_cf::<cf::MerkleRootMeta>(&mut write_batch, slot, slot)
.unwrap();
blockstore.db.write(write_batch).unwrap();
assert!(blockstore
.merkle_root_meta(next_coding_shreds[0].erasure_set())
.unwrap()
.is_none());

// Insert previous FEC set, although incorrectly chained we skip the duplicate check
// as the merkle root meta is missing.
assert!(blockstore
.insert_shred_return_duplicate(coding_shred, &leader_schedule)
.is_empty());
}
}
Loading