Skip to content

Commit

Permalink
blockstore: relax backwards chained merkle root check for upgrades (#…
Browse files Browse the repository at this point in the history
…1163)

* blockstore: relax backwards chained merkle root check for upgrades

* s/v1.18.12/v1.18.13

Co-authored-by: Trent Nelson <[email protected]>

---------

Co-authored-by: Trent Nelson <[email protected]>
  • Loading branch information
AshwinSekar and t-nelson authored May 6, 2024
1 parent 9374944 commit a645d07
Showing 1 changed file with 113 additions and 2 deletions.
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());
}
}

0 comments on commit a645d07

Please sign in to comment.