no more conversion to support v2 blocks (#3587)

get_block simply returns None if v2 support required
This commit is contained in:
Antioch Peverell 2021-03-06 21:40:20 +00:00 committed by GitHub
parent e023739658
commit 35740204a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 5 additions and 146 deletions

View file

@ -35,15 +35,13 @@ use crate::types::{
}; };
use crate::util::secp::pedersen::{Commitment, RangeProof}; use crate::util::secp::pedersen::{Commitment, RangeProof};
use crate::util::RwLock; use crate::util::RwLock;
use crate::ChainStore;
use grin_core::ser;
use grin_store::Error::NotFoundErr; use grin_store::Error::NotFoundErr;
use std::collections::HashMap;
use std::fs::{self, File}; use std::fs::{self, File};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{collections::HashMap, io::Cursor};
/// Orphan pool size is limited by MAX_ORPHAN_SIZE /// Orphan pool size is limited by MAX_ORPHAN_SIZE
pub const MAX_ORPHAN_SIZE: usize = 200; pub const MAX_ORPHAN_SIZE: usize = 200;
@ -173,10 +171,6 @@ impl Chain {
) -> Result<Chain, Error> { ) -> Result<Chain, Error> {
let store = Arc::new(store::ChainStore::new(&db_root)?); 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 // open the txhashset, creating a new one if necessary
let mut txhashset = txhashset::TxHashSet::open(db_root.clone(), store.clone(), None)?; let mut txhashset = txhashset::TxHashSet::open(db_root.clone(), store.clone(), None)?;
@ -273,43 +267,6 @@ impl Chain {
res 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( fn determine_status(
&self, &self,
head: Option<Tip>, head: Option<Tip>,
@ -401,11 +358,6 @@ impl Chain {
// Only do this once we know the header PoW is valid. // Only do this once we know the header PoW is valid.
self.check_orphan(&b, opts)?; 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 (maybe_new_head, prev_head) = {
let mut header_pmmr = self.header_pmmr.write(); let mut header_pmmr = self.header_pmmr.write();
let mut txhashset = self.txhashset.write(); let mut txhashset = self.txhashset.write();
@ -1367,58 +1319,6 @@ impl Chain {
self.header_pmmr.read().get_header_hash_by_height(height) 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. /// 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> { pub fn get_header_for_output(&self, commit: Commitment) -> Result<BlockHeader, Error> {
let header_pmmr = self.header_pmmr.read(); let header_pmmr = self.header_pmmr.read();

View file

@ -46,11 +46,6 @@ pub const NRD_KERNEL_ENTRY_PREFIX: u8 = b'k';
const BLOCK_SUMS_PREFIX: u8 = b'M'; const BLOCK_SUMS_PREFIX: u8 = b'M';
const BLOCK_SPENT_PREFIX: u8 = b'S'; 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 /// All chain-related database operations
pub struct ChainStore { pub struct ChainStore {
db: store::Store, db: store::Store,
@ -219,44 +214,6 @@ impl<'a> Batch<'a> {
Ok(()) 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. /// Low level function to delete directly by raw key.
pub fn delete(&self, key: &[u8]) -> Result<(), Error> { pub fn delete(&self, key: &[u8]) -> Result<(), Error> {
self.db.delete(key) self.db.delete(key)

View file

@ -378,12 +378,14 @@ where
} }
/// Gets a full block by its hash. /// 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> { fn get_block(&self, h: Hash, peer_info: &PeerInfo) -> Option<core::Block> {
self.chain() self.chain()
.get_block(&h) .get_block(&h)
.map(|b| match peer_info.version.value() { .map(|b| match peer_info.version.value() {
0..=2 => self.chain().convert_block_v2(b).ok(), 0..=2 => None,
3..=ProtocolVersion::MAX => Some(b), 3..=ProtocolVersion::MAX => Some(b),
}) })
.unwrap_or(None) .unwrap_or(None)