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

retire support for v2 blocks #3587

Merged
merged 1 commit into from
Mar 6, 2021
Merged
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
102 changes: 1 addition & 101 deletions chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ use crate::types::{
};
use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::RwLock;
use crate::ChainStore;
use grin_core::ser;
use grin_store::Error::NotFoundErr;
use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::{collections::HashMap, io::Cursor};

/// Orphan pool size is limited by MAX_ORPHAN_SIZE
pub const MAX_ORPHAN_SIZE: usize = 200;
Expand Down Expand Up @@ -173,10 +171,6 @@ impl Chain {
) -> Result<Chain, Error> {
let store = Arc::new(store::ChainStore::new(&db_root)?);

// DB migrations to be run prior to the chain being used.
// Migrate full blocks to protocol version v3.
Chain::migrate_db_v2_v3(&store)?;

// open the txhashset, creating a new one if necessary
let mut txhashset = txhashset::TxHashSet::open(db_root.clone(), store.clone(), None)?;

Expand Down Expand Up @@ -273,43 +267,6 @@ impl Chain {
res
}

/// We plan to support receiving blocks with CommitOnly inputs.
/// We also need to support relaying blocks with FeaturesAndCommit inputs to peers.
/// So we need a way to convert blocks from CommitOnly to FeaturesAndCommit.
/// Validating the inputs against the utxo_view allows us to look the outputs up.
pub fn convert_block_v2(&self, block: Block) -> Result<Block, Error> {
debug!(
"convert_block_v2: {} at {} ({} -> v2)",
block.header.hash(),
block.header.height,
block.inputs().version_str(),
);

if block.inputs().is_empty() {
return Ok(Block {
header: block.header,
body: block.body.replace_inputs(Inputs::FeaturesAndCommit(vec![])),
});
}

let mut header_pmmr = self.header_pmmr.write();
let mut txhashset = self.txhashset.write();
let inputs: Vec<_> =
txhashset::extending_readonly(&mut header_pmmr, &mut txhashset, |ext, batch| {
let previous_header = batch.get_previous_header(&block.header)?;
pipe::rewind_and_apply_fork(&previous_header, ext, batch)?;
ext.extension
.utxo_view(ext.header_extension)
.validate_inputs(&block.inputs(), batch)
.map(|outputs| outputs.into_iter().map(|(out, _)| out).collect())
})?;
let inputs = inputs.as_slice().into();
Ok(Block {
header: block.header,
body: block.body.replace_inputs(inputs),
})
}

fn determine_status(
&self,
head: Option<Tip>,
Expand Down Expand Up @@ -401,11 +358,6 @@ impl Chain {
// Only do this once we know the header PoW is valid.
self.check_orphan(&b, opts)?;

// We can only reliably convert to "v2" if not an orphan (may spend output from previous block).
// We convert from "v3" to "v2" by looking up outputs to be spent.
// This conversion also ensures a block received in "v2" has valid input features (prevents malleability).
let b = self.convert_block_v2(b)?;

let (maybe_new_head, prev_head) = {
let mut header_pmmr = self.header_pmmr.write();
let mut txhashset = self.txhashset.write();
Expand Down Expand Up @@ -1367,58 +1319,6 @@ impl Chain {
self.header_pmmr.read().get_header_hash_by_height(height)
}

/// Migrate our local db from v2 to v3.
/// "commit only" inputs.
fn migrate_db_v2_v3(store: &ChainStore) -> Result<(), Error> {
if store.batch()?.is_blocks_v3_migrated()? {
// Previously migrated so skipping.
debug!("migrate_db_v2_v3: previously migrated, skipping");
return Ok(());
}
let mut total = 0;
let mut keys_to_migrate = vec![];
for (k, v) in store.batch()?.blocks_raw_iter()? {
total += 1;

// We want to migrate all blocks that cannot be read via v3 protocol version.
let block_v3: Result<Block, _> =
ser::deserialize(&mut Cursor::new(&v), ProtocolVersion(3));
if block_v3.is_err() {
let block_v2: Result<Block, _> =
ser::deserialize(&mut Cursor::new(&v), ProtocolVersion(2));
if block_v2.is_ok() {
keys_to_migrate.push(k);
}
}
}
debug!(
"migrate_db_v2_v3: {} (of {}) blocks to migrate",
keys_to_migrate.len(),
total,
);
let mut count = 0;
keys_to_migrate
.chunks(100)
.try_for_each(|keys| {
let batch = store.batch()?;
for key in keys {
batch.migrate_block(&key, ProtocolVersion(2), ProtocolVersion(3))?;
count += 1;
}
batch.commit()?;
debug!("migrate_db_v2_v3: successfully migrated {} blocks", count);
Ok(())
})
.and_then(|_| {
// Set flag to indicate we have migrated all blocks in the db.
// We will skip migration in the future.
let batch = store.batch()?;
batch.set_blocks_v3_migrated(true)?;
batch.commit()?;
Ok(())
})
}

/// Gets the block header in which a given output appears in the txhashset.
pub fn get_header_for_output(&self, commit: Commitment) -> Result<BlockHeader, Error> {
let header_pmmr = self.header_pmmr.read();
Expand Down
43 changes: 0 additions & 43 deletions chain/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ pub const NRD_KERNEL_ENTRY_PREFIX: u8 = b'k';
const BLOCK_SUMS_PREFIX: u8 = b'M';
const BLOCK_SPENT_PREFIX: u8 = b'S';

/// Prefix for various boolean flags stored in the db.
const BOOL_FLAG_PREFIX: u8 = b'B';
/// Boolean flag for v3 migration.
const BLOCKS_V3_MIGRATED: &str = "blocks_v3_migrated";

/// All chain-related database operations
pub struct ChainStore {
db: store::Store,
Expand Down Expand Up @@ -219,44 +214,6 @@ impl<'a> Batch<'a> {
Ok(())
}

/// DB flag representing full migration of blocks to v3 version.
/// Default to false if flag not present.
pub fn is_blocks_v3_migrated(&self) -> Result<bool, Error> {
let migrated: Option<BoolFlag> = self
.db
.get_ser(&to_key(BOOL_FLAG_PREFIX, BLOCKS_V3_MIGRATED))?;
match migrated {
None => Ok(false),
Some(x) => Ok(x.into()),
}
}

/// Set DB flag representing full migration of blocks to v3 version.
pub fn set_blocks_v3_migrated(&self, migrated: bool) -> Result<(), Error> {
self.db.put_ser(
&to_key(BOOL_FLAG_PREFIX, BLOCKS_V3_MIGRATED)[..],
&BoolFlag(migrated),
)?;
Ok(())
}

/// Migrate a block stored in the db reading from one protocol version and writing
/// with new protocol version.
pub fn migrate_block(
&self,
key: &[u8],
from_version: ProtocolVersion,
to_version: ProtocolVersion,
) -> Result<(), Error> {
let block: Option<Block> = self.db.get_with(key, move |_, mut v| {
ser::deserialize(&mut v, from_version).map_err(From::from)
})?;
if let Some(block) = block {
self.db.put_ser_with_version(key, &block, to_version)?;
}
Ok(())
}

/// Low level function to delete directly by raw key.
pub fn delete(&self, key: &[u8]) -> Result<(), Error> {
self.db.delete(key)
Expand Down
6 changes: 4 additions & 2 deletions servers/src/common/adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,14 @@ where
}

/// Gets a full block by its hash.
/// Will convert to v2 compatibility based on peer protocol version.
/// We only support v3 blocks since HF4.
/// If a peer is requesting a block and only appears to support v2
/// then ignore the request.
fn get_block(&self, h: Hash, peer_info: &PeerInfo) -> Option<core::Block> {
self.chain()
.get_block(&h)
.map(|b| match peer_info.version.value() {
0..=2 => self.chain().convert_block_v2(b).ok(),
0..=2 => None,
3..=ProtocolVersion::MAX => Some(b),
})
.unwrap_or(None)
Expand Down