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

kernel pos index #3228

Closed
wants to merge 11 commits into from
30 changes: 21 additions & 9 deletions chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,9 +980,12 @@ impl Chain {
batch.save_body_tail(&tip)?;
}

// Rebuild our output_pos index in the db based on fresh UTXO set.
// Initialize output_pos index in the db based on current UTXO set.
txhashset.init_output_pos_index(&header_pmmr, &batch)?;

// Initialize the kernel_pos index for recent kernel history.
txhashset.init_kernel_pos_index(&header_pmmr, &batch)?;

// Commit all the changes to the db.
batch.commit()?;

Expand Down Expand Up @@ -1082,9 +1085,12 @@ impl Chain {
// current "head" and "tail" height to our cut-through horizon and
// allowing an additional 60 blocks in height before allowing a further compaction.
if let (Ok(tail), Ok(head)) = (self.tail(), self.head()) {
let horizon = global::cut_through_horizon() as u64;
let threshold = horizon.saturating_add(60);
let next_compact = tail.height.saturating_add(threshold);
let threshold = if global::is_production_mode() {
global::cut_through_horizon().saturating_add(60)
} else {
global::cut_through_horizon()
};
let next_compact = tail.height.saturating_add(threshold.into());
if next_compact > head.height {
debug!(
"compact: skipping startup compaction (next at {})",
Expand All @@ -1099,6 +1105,11 @@ impl Chain {
let mut txhashset = self.txhashset.write();
let batch = self.store.batch()?;

// Remove historical blocks from the db unless we are running in archive mode.
if !self.archive_mode {
self.remove_historical_blocks(&header_pmmr, &batch)?;
}

// Compact the txhashset itself (rewriting the pruned backend files).
{
let head_header = batch.head_header()?;
Expand All @@ -1111,11 +1122,6 @@ impl Chain {
txhashset.compact(&horizon_header, &batch)?;
}

// If we are not in archival mode remove historical blocks from the db.
if !self.archive_mode {
self.remove_historical_blocks(&header_pmmr, &batch)?;
}

// Make sure our output_pos index is consistent with the UTXO set.
txhashset.init_output_pos_index(&header_pmmr, &batch)?;

Expand Down Expand Up @@ -1145,6 +1151,12 @@ impl Chain {
Ok(self.txhashset.read().get_output_pos(commit)?)
}

/// Get the position of the kernel if it exists in the kernel_pos index.
/// The index is limited to 14 days of recent kernels.
pub fn get_kernel_pos(&self, excess: Commitment) -> Result<u64, Error> {
self.txhashset.read().get_kernel_pos(excess)
}

/// outputs by insertion index
pub fn unspent_outputs_by_pmmr_index(
&self,
Expand Down
104 changes: 85 additions & 19 deletions chain/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const BLOCK_PREFIX: u8 = b'b';
const HEAD_PREFIX: u8 = b'H';
const TAIL_PREFIX: u8 = b'T';
const OUTPUT_POS_PREFIX: u8 = b'p';
const KERNEL_POS_PREFIX: u8 = b'k';
const BLOCK_INPUT_BITMAP_PREFIX: u8 = b'B';
const BLOCK_SUMS_PREFIX: u8 = b'M';
const BLOCK_SPENT_PREFIX: u8 = b'S';
const BLOCK_KERNEL_UNDO_PREFIX: u8 = b'U';

/// All chain-related database operations
pub struct ChainStore {
Expand Down Expand Up @@ -116,7 +118,7 @@ impl ChainStore {
/// Get PMMR pos for the given output commitment.
pub fn get_output_pos(&self, commit: &Commitment) -> Result<u64, Error> {
match self.get_output_pos_height(commit)? {
Some((pos, _)) => Ok(pos),
Some(pos) => Ok(pos.pos),
None => Err(Error::NotFoundErr(format!(
"Output position for: {:?}",
commit
Expand All @@ -125,9 +127,19 @@ impl ChainStore {
}

/// Get PMMR pos and block height for the given output commitment.
pub fn get_output_pos_height(&self, commit: &Commitment) -> Result<Option<(u64, u64)>, Error> {
self.db
.get_ser(&to_key(OUTPUT_POS_PREFIX, &mut commit.as_ref().to_vec()))
pub fn get_output_pos_height(&self, commit: &Commitment) -> Result<Option<CommitPos>, Error> {
self.db.get_ser(&to_key(OUTPUT_POS_PREFIX, &mut commit.as_ref().to_vec()))
}

/// Get kernel_pos and block height from index.
/// Returns a vec of possible (pos, height) entries in the MMR.
/// Returns an empty vec if no entries found.
pub fn get_kernel_pos_height(&self, excess: &Commitment) -> Result<CommitPos, Error> {
option_to_not_found(
self.db
.get_ser(&to_key(KERNEL_POS_PREFIX, &mut excess.as_ref().to_vec())),
|| format!("Kernel position for: {:?}", excess),
)
}

/// Builds a new batch to be used with this store.
Expand Down Expand Up @@ -199,6 +211,20 @@ impl<'a> Batch<'a> {
Ok(())
}

/// We maintain an "undo" index for each full block to allow the kernel_pos
/// to be easily reverted during rewind.
/// We allow duplicate kernels so we need to know what to revert the kernel_pos
/// index to if we "undo" a kernel when rewinding a block.
pub fn save_kernel_undo_list(
&self,
h: &Hash,
pos: &Vec<(Commitment, CommitPos)>,
) -> Result<(), Error> {
self.db
.put_ser(&to_key(BLOCK_KERNEL_UNDO_PREFIX, &mut h.to_vec())[..], pos)?;
Ok(())
}

/// Migrate a block stored in the db by serializing it using the provided protocol version.
/// Block may have been read using a previous protocol version but we do not actually care.
pub fn migrate_block(&self, b: &Block, version: ProtocolVersion) -> Result<(), Error> {
Expand Down Expand Up @@ -226,6 +252,7 @@ impl<'a> Batch<'a> {
{
let _ = self.delete_block_sums(bh);
let _ = self.delete_spent_index(bh);
let _ = self.delete_kernel_undo_list(bh);
}

Ok(())
Expand All @@ -243,24 +270,25 @@ impl<'a> Batch<'a> {
}

/// Save output_pos and block height to index.
pub fn save_output_pos_height(
&self,
commit: &Commitment,
pos: u64,
height: u64,
) -> Result<(), Error> {
pub fn save_output_pos_height(&self, commit: &Commitment, pos: CommitPos) -> Result<(), Error> {
self.db.put_ser(
&to_key(OUTPUT_POS_PREFIX, &mut commit.as_ref().to_vec())[..],
&(pos, height),
&pos,
)
}

/// Delete the output_pos index entry for a spent output.
/// Delete a output_pos index entry.
pub fn delete_output_pos_height(&self, commit: &Commitment) -> Result<(), Error> {
self.db
.delete(&to_key(OUTPUT_POS_PREFIX, &mut commit.as_ref().to_vec()))
}

/// Delete a kernel_pos index entry
pub fn delete_kernel_pos_height(&self, excess: &Commitment) -> Result<(), Error> {
self.db
.delete(&to_key(KERNEL_POS_PREFIX, &mut excess.as_ref().to_vec()))
}

/// When using the output_pos iterator we have access to the index keys but not the
/// original commitment that the key is constructed from. So we need a way of comparing
/// a key with another commitment without reconstructing the commitment from the key bytes.
Expand All @@ -270,26 +298,48 @@ impl<'a> Batch<'a> {
}

/// Iterator over the output_pos index.
pub fn output_pos_iter(&self) -> Result<SerIterator<(u64, u64)>, Error> {
pub fn output_pos_iter(&self) -> Result<SerIterator<CommitPos>, Error> {
let key = to_key(OUTPUT_POS_PREFIX, &mut "".to_string().into_bytes());
self.db.iter(&key)
}

/// Get output_pos from index.
/// Save kernel_pos and block height to index.
pub fn save_kernel_pos_height(&self, excess: &Commitment, pos: CommitPos) -> Result<(), Error> {
self.db.put_ser(
&to_key(KERNEL_POS_PREFIX, &mut excess.as_ref().to_vec())[..],
&pos,
)
}

/// Iterator over the kernel_pos index.
pub fn kernel_pos_iter(&self) -> Result<SerIterator<CommitPos>, Error> {
let key = to_key(KERNEL_POS_PREFIX, &mut "".to_string().into_bytes());
self.db.iter(&key)
}

/// Get kernel_pos and block height from index.
pub fn get_kernel_pos_height(&self, excess: &Commitment) -> Result<CommitPos, Error> {
option_to_not_found(
self.db
.get_ser(&to_key(KERNEL_POS_PREFIX, &mut excess.as_ref().to_vec())),
|| format!("Kernel pos for excess: {:?}", excess),
)
}

/// Get PMMR pos for the given output commitment.
pub fn get_output_pos(&self, commit: &Commitment) -> Result<u64, Error> {
match self.get_output_pos_height(commit)? {
Some((pos, _)) => Ok(pos),
Some(pos) => Ok(pos.pos),
None => Err(Error::NotFoundErr(format!(
"Output position for: {:?}",
commit
))),
}
}

/// Get output_pos and block height from index.
pub fn get_output_pos_height(&self, commit: &Commitment) -> Result<Option<(u64, u64)>, Error> {
self.db
.get_ser(&to_key(OUTPUT_POS_PREFIX, &mut commit.as_ref().to_vec()))
/// Get PMMR pos and block height for the given output commitment.
pub fn get_output_pos_height(&self, commit: &Commitment) -> Result<Option<CommitPos>, Error> {
self.db.get_ser(&to_key(OUTPUT_POS_PREFIX, &mut commit.as_ref().to_vec()))
}

/// Get the previous header.
Expand Down Expand Up @@ -317,6 +367,11 @@ impl<'a> Batch<'a> {
.delete(&to_key(BLOCK_SPENT_PREFIX, &mut bh.to_vec()))
}

fn delete_kernel_undo_list(&self, bh: &Hash) -> Result<(), Error> {
self.db
.delete(&to_key(BLOCK_KERNEL_UNDO_PREFIX, &mut bh.to_vec()))
}

/// Save block_sums for the block.
pub fn save_block_sums(&self, h: &Hash, sums: BlockSums) -> Result<(), Error> {
self.db
Expand Down Expand Up @@ -371,6 +426,17 @@ impl<'a> Batch<'a> {
)
}

/// Get the kernel "undo list" from the db for the specified block.
/// If we need to rewind a block then we use this to revert the index to previous kernel pos
/// in the case of duplicates.
pub fn get_kernel_undo_list(&self, bh: &Hash) -> Result<Vec<(Commitment, CommitPos)>, Error> {
option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_KERNEL_UNDO_PREFIX, &mut bh.to_vec())),
|| format!("kernel undo list: {}", bh),
)
}

/// Commits this batch. If it's a child batch, it will be merged with the
/// parent, otherwise the batch is written to db.
pub fn commit(self) -> Result<(), Error> {
Expand Down
Loading