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

fix: deleted_txo_mmr_position_to_height_index already exists error #4924

Merged
merged 4 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
14 changes: 8 additions & 6 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,9 +1031,17 @@ impl LMDBDatabase {
for row in inputs {
// If input spends an output in this block, don't add it to the utxo set
let output_hash = row.input.output_hash();

lmdb_delete(
txn,
&self.deleted_txo_mmr_position_to_height_index,
&row.mmr_position,
"deleted_txo_mmr_position_to_height_index",
)?;
if output_rows.iter().any(|r| r.hash == output_hash) {
continue;
}

let mut input = row.input.clone();

let utxo_mined_info = self.fetch_output_in_txn(txn, output_hash.as_slice())?.ok_or_else(|| {
Expand Down Expand Up @@ -1073,12 +1081,6 @@ impl LMDBDatabase {
&input.output_hash(),
"utxo_commitment_index",
)?;
lmdb_delete(
txn,
&self.deleted_txo_mmr_position_to_height_index,
&row.mmr_position,
"deleted_txo_mmr_position_to_height_index",
)?;
}
Ok(())
}
Expand Down
154 changes: 154 additions & 0 deletions base_layer/core/tests/chain_storage_tests/chain_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ use crate::helpers::{
create_chain_header,
create_genesis_block,
find_header_with_achieved_difficulty,
generate_block_with_achieved_difficulty,
generate_new_block,
generate_new_block_with_achieved_difficulty,
generate_new_block_with_coinbase,
Expand Down Expand Up @@ -320,6 +321,159 @@ fn test_rewind_past_horizon_height() {
}

#[test]
#[allow(clippy::too_many_lines)]
fn test_handle_tip_reorg_with_zero_conf() {
// GB --> A1 --> A2 --> A3(Low PoW) [Main Chain]
// \--> B2 --> B3 -- B4 --> B5(Highest PoW) [Forked Chain]

// Create Main Chain
let network = Network::LocalNet;
let (mut store, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network);
// Block A1
let txs = vec![txn_schema!(
from: vec![outputs[0][0].clone()],
to: vec![10 * T, 10 * T, 10 * T, 10 * T]
)];
generate_new_block_with_achieved_difficulty(
&mut store,
&mut blocks,
&mut outputs,
txs,
Difficulty::from(1),
&consensus_manager,
)
.unwrap();
// Block A2
let txs_1 = txn_schema!(from: vec![outputs[1][3].clone()], to: vec![6 * T]);
let (tx_1, utxos_1) = spend_utxos(txs_1);
// create zero conf
let txs_2 = txn_schema!(from: vec![utxos_1[0].clone()], to: vec![4 * T]);
let (tx_2, utxos_2) = spend_utxos(txs_2);
let txns = vec![tx_1, tx_2];

outputs.push(utxos_2);
generate_block_with_achieved_difficulty(&mut store, &mut blocks, txns, Difficulty::from(3), &consensus_manager)
.unwrap();

// Block A3
let txs = vec![txn_schema!(from: vec![outputs[2][0].clone()], to: vec![2 * T])];
assert!(generate_new_block_with_achieved_difficulty(
&mut store,
&mut blocks,
&mut outputs,
txs,
Difficulty::from(1),
&consensus_manager
)
.is_ok());
assert_eq!(store.get_chain_metadata().unwrap().height_of_longest_chain(), 3);

// Create Forked Chain

let mut orphan_store = create_store_with_consensus(consensus_manager.clone());
orphan_store.add_block(blocks[1].to_arc_block()).unwrap();
let mut orphan_blocks = vec![blocks[0].clone(), blocks[1].clone()];
let mut orphan_outputs = vec![outputs[0].clone(), outputs[1].clone()];
// Block B2
let txs = vec![txn_schema!(
from: vec![
orphan_outputs[1][0].clone(),
orphan_outputs[1][1].clone(),
orphan_outputs[1][2].clone(),
orphan_outputs[1][3].clone(),
orphan_outputs[1][4].clone(),
],
to: vec![5 * T, 5 * T, 5 * T, 5 * T, 5 * T]
)];
generate_new_block_with_achieved_difficulty(
&mut orphan_store,
&mut orphan_blocks,
&mut orphan_outputs,
txs,
Difficulty::from(7),
&consensus_manager,
)
.unwrap();

// Adding B2 to the main chain will produce a reorg to GB->A1->B2.
if let Ok(BlockAddResult::ChainReorg { .. }) = store.add_block(orphan_blocks[2].to_arc_block()) {
} else {
panic!();
}

assert_eq!(store.fetch_tip_header().unwrap().header(), orphan_blocks[2].header());

// Check that B2 was removed from the block orphans and A2 has been orphaned.
assert!(store.fetch_orphan(*orphan_blocks[2].hash()).is_err());
assert!(store.fetch_orphan(*blocks[2].hash()).is_ok());
assert_eq!(store.get_chain_metadata().unwrap().height_of_longest_chain(), 2);

// Block B3
let txs = vec![
txn_schema!(from: vec![orphan_outputs[2][0].clone()], to: vec![3 * T]),
txn_schema!(from: vec![orphan_outputs[2][1].clone()], to: vec![3 * T]),
txn_schema!(from: vec![orphan_outputs[2][2].clone()], to: vec![3 * T]),
txn_schema!(from: vec![orphan_outputs[2][3].clone()], to: vec![3 * T]),
txn_schema!(from: vec![orphan_outputs[2][4].clone()], to: vec![3 * T]),
txn_schema!(from: vec![orphan_outputs[2][5].clone()], to: vec![3 * T]),
];
assert!(generate_new_block_with_achieved_difficulty(
&mut orphan_store,
&mut orphan_blocks,
&mut orphan_outputs,
txs,
Difficulty::from(1),
&consensus_manager
)
.is_ok());

if let Ok(BlockAddResult::Ok { .. }) = store.add_block(orphan_blocks[3].to_arc_block()) {
} else {
panic!();
}
// Block B4
let txs = vec![
txn_schema!(from: vec![orphan_outputs[3][0].clone()], to: vec![1500000 * uT]),
txn_schema!(from: vec![orphan_outputs[3][1].clone()], to: vec![1500000 * uT]),
];
assert!(generate_new_block_with_achieved_difficulty(
&mut orphan_store,
&mut orphan_blocks,
&mut orphan_outputs,
txs,
Difficulty::from(1),
&consensus_manager
)
.is_ok());

if let Ok(BlockAddResult::Ok { .. }) = store.add_block(orphan_blocks[4].to_arc_block()) {
} else {
panic!();
}

// Block B5
let txs = vec![
txn_schema!(from: vec![orphan_outputs[4][0].clone()], to: vec![50000 * uT]),
txn_schema!(from: vec![orphan_outputs[4][1].clone()], to: vec![50000 * uT]),
];
assert!(generate_new_block_with_achieved_difficulty(
&mut orphan_store,
&mut orphan_blocks,
&mut orphan_outputs,
txs,
Difficulty::from(1),
&consensus_manager
)
.is_ok());

if let Ok(BlockAddResult::Ok { .. }) = store.add_block(orphan_blocks[5].to_arc_block()) {
} else {
panic!();
}
assert_eq!(store.get_chain_metadata().unwrap().height_of_longest_chain(), 5);
}
#[test]
#[allow(clippy::too_many_lines)]
fn test_handle_tip_reorg() {
// GB --> A1 --> A2(Low PoW) [Main Chain]
// \--> B2(Highest PoW) [Forked Chain]
Expand Down