mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Commit to prev_root in block headers (#1764)
* commit to prev_root in block headers * prev_root ready to go, mergeable onto testnet4
This commit is contained in:
parent
fffe5154d2
commit
fbf955dd11
6 changed files with 100 additions and 57 deletions
|
@ -486,23 +486,37 @@ impl Chain {
|
|||
/// the current txhashset state.
|
||||
pub fn set_txhashset_roots(&self, b: &mut Block, is_fork: bool) -> Result<(), Error> {
|
||||
let mut txhashset = self.txhashset.write().unwrap();
|
||||
let (roots, sizes) = txhashset::extending_readonly(&mut txhashset, |extension| {
|
||||
if is_fork {
|
||||
pipe::rewind_and_apply_fork(b, extension)?;
|
||||
}
|
||||
extension.apply_block(b)?;
|
||||
Ok((extension.roots(), extension.sizes()))
|
||||
})?;
|
||||
let (prev_root, roots, sizes) =
|
||||
txhashset::extending_readonly(&mut txhashset, |extension| {
|
||||
if is_fork {
|
||||
pipe::rewind_and_apply_fork(b, extension)?;
|
||||
}
|
||||
|
||||
// Carefully destructure these correctly...
|
||||
// TODO - Maybe sizes should be a struct to add some type safety here...
|
||||
let (_, output_mmr_size, _, kernel_mmr_size) = sizes;
|
||||
// Retrieve the header root before we apply the new block
|
||||
let prev_root = extension.header_root();
|
||||
|
||||
// Apply the latest block to the chain state via the extension.
|
||||
extension.apply_block(b)?;
|
||||
|
||||
Ok((prev_root, extension.roots(), extension.sizes()))
|
||||
})?;
|
||||
|
||||
// Set the prev_root on the header.
|
||||
b.header.prev_root = prev_root;
|
||||
|
||||
// Set the output, rangeproof and kernel MMR roots.
|
||||
b.header.output_root = roots.output_root;
|
||||
b.header.range_proof_root = roots.rproof_root;
|
||||
b.header.kernel_root = roots.kernel_root;
|
||||
b.header.output_mmr_size = output_mmr_size;
|
||||
b.header.kernel_mmr_size = kernel_mmr_size;
|
||||
|
||||
// Set the output and kernel MMR sizes.
|
||||
{
|
||||
// Carefully destructure these correctly...
|
||||
let (_, output_mmr_size, _, kernel_mmr_size) = sizes;
|
||||
b.header.output_mmr_size = output_mmr_size;
|
||||
b.header.kernel_mmr_size = kernel_mmr_size;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -218,6 +218,7 @@ pub fn sync_block_headers(
|
|||
extension.rewind(&prev_header)?;
|
||||
|
||||
for header in headers {
|
||||
extension.validate_root(header)?;
|
||||
extension.apply_header(header)?;
|
||||
}
|
||||
|
||||
|
@ -500,6 +501,7 @@ fn verify_block_sums(b: &Block, ext: &mut txhashset::Extension) -> Result<(), Er
|
|||
/// Fully validate the block by applying it to the txhashset extension.
|
||||
/// Check both the txhashset roots and sizes are correct after applying the block.
|
||||
fn apply_block_to_txhashset(block: &Block, ext: &mut txhashset::Extension) -> Result<(), Error> {
|
||||
ext.validate_header_root(&block.header)?;
|
||||
ext.apply_block(block)?;
|
||||
ext.validate_roots()?;
|
||||
ext.validate_sizes()?;
|
||||
|
|
|
@ -701,28 +701,51 @@ impl<'a> HeaderExtension<'a> {
|
|||
header_hashes.push(current.hash());
|
||||
current = self.batch.get_block_header(¤t.previous)?;
|
||||
}
|
||||
// Include the genesis header as we will re-apply it after truncating the extension.
|
||||
header_hashes.push(genesis.hash());
|
||||
header_hashes.reverse();
|
||||
|
||||
// Trucate the extension (back to pos 0).
|
||||
self.truncate()?;
|
||||
|
||||
debug!(
|
||||
LOGGER,
|
||||
"Re-applying {} headers to extension, from {:?} to {:?}.",
|
||||
header_hashes.len(),
|
||||
header_hashes.first().unwrap(),
|
||||
header_hashes.last().unwrap(),
|
||||
);
|
||||
// Re-apply the genesis header after truncation.
|
||||
self.apply_header(&genesis)?;
|
||||
|
||||
for h in header_hashes {
|
||||
let header = self.batch.get_block_header(&h)?;
|
||||
// self.validate_header_root()?;
|
||||
self.apply_header(&header)?;
|
||||
if header_hashes.len() > 0 {
|
||||
debug!(
|
||||
LOGGER,
|
||||
"Re-applying {} headers to extension, from {:?} to {:?}.",
|
||||
header_hashes.len(),
|
||||
header_hashes.first().unwrap(),
|
||||
header_hashes.last().unwrap(),
|
||||
);
|
||||
|
||||
for h in header_hashes {
|
||||
let header = self.batch.get_block_header(&h)?;
|
||||
self.validate_root(&header)?;
|
||||
self.apply_header(&header)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The root of the header MMR for convenience.
|
||||
pub fn root(&self) -> Hash {
|
||||
self.pmmr.root()
|
||||
}
|
||||
|
||||
/// Validate the prev_root of the header against the root of the current header MMR.
|
||||
pub fn validate_root(&self, header: &BlockHeader) -> Result<(), Error> {
|
||||
// If we are validating the genesis block then we have no prev_root.
|
||||
// So we are done here.
|
||||
if header.height == 1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.root() != header.prev_root {
|
||||
Err(ErrorKind::InvalidRoot.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows the application of new blocks on top of the sum trees in a
|
||||
|
@ -1078,14 +1101,19 @@ impl<'a> Extension<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the root of the current header MMR.
|
||||
pub fn header_root(&self) -> Hash {
|
||||
self.header_pmmr.root()
|
||||
}
|
||||
|
||||
/// Validate the following MMR roots against the latest header applied -
|
||||
/// * output
|
||||
/// * rangeproof
|
||||
/// * kernel
|
||||
///
|
||||
/// Note we do not validate the header MMR roots here as we need to validate
|
||||
/// a header against the state of the MMR *prior* to applying it as
|
||||
/// each header commits to the root of the MMR of all previous headers,
|
||||
/// Note we do not validate the header MMR root here as we need to validate
|
||||
/// a header against the state of the MMR *prior* to applying it.
|
||||
/// Each header commits to the root of the MMR of all previous headers,
|
||||
/// not including the header itself.
|
||||
///
|
||||
pub fn validate_roots(&self) -> Result<(), Error> {
|
||||
|
@ -1107,23 +1135,19 @@ impl<'a> Extension<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Validate the provided header by comparing its "prev_root" to the
|
||||
/// Validate the provided header by comparing its prev_root to the
|
||||
/// root of the current header MMR.
|
||||
///
|
||||
/// TODO - Implement this once we commit to prev_root in block headers.
|
||||
///
|
||||
pub fn validate_header_root(&self, _header: &BlockHeader) -> Result<(), Error> {
|
||||
if self.header.height == 0 {
|
||||
pub fn validate_header_root(&self, header: &BlockHeader) -> Result<(), Error> {
|
||||
if header.height == 1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let _roots = self.roots();
|
||||
|
||||
// TODO - validate once we commit to header MMR root in the header
|
||||
// (not just previous hash)
|
||||
// if roots.header_root != header.prev_root
|
||||
|
||||
Ok(())
|
||||
let roots = self.roots();
|
||||
if roots.header_root != header.prev_root {
|
||||
Err(ErrorKind::InvalidRoot.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate the header, output and kernel MMR sizes against the block header.
|
||||
|
|
|
@ -118,6 +118,8 @@ pub struct BlockHeader {
|
|||
pub height: u64,
|
||||
/// Hash of the block previous to this in the chain.
|
||||
pub previous: Hash,
|
||||
/// Root hash of the header MMR at the previous header.
|
||||
pub prev_root: Hash,
|
||||
/// Timestamp at which the block was built.
|
||||
pub timestamp: DateTime<Utc>,
|
||||
/// Merklish root of all the commitments in the TxHashSet
|
||||
|
@ -143,8 +145,9 @@ fn fixed_size_of_serialized_header(_version: u16) -> usize {
|
|||
let mut size: usize = 0;
|
||||
size += mem::size_of::<u16>(); // version
|
||||
size += mem::size_of::<u64>(); // height
|
||||
size += mem::size_of::<i64>(); // timestamp
|
||||
size += mem::size_of::<Hash>(); // previous
|
||||
size += mem::size_of::<u64>(); // timestamp
|
||||
size += mem::size_of::<Hash>(); // prev_root
|
||||
size += mem::size_of::<Hash>(); // output_root
|
||||
size += mem::size_of::<Hash>(); // range_proof_root
|
||||
size += mem::size_of::<Hash>(); // kernel_root
|
||||
|
@ -176,8 +179,9 @@ impl Default for BlockHeader {
|
|||
BlockHeader {
|
||||
version: 1,
|
||||
height: 0,
|
||||
previous: ZERO_HASH,
|
||||
timestamp: DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc),
|
||||
previous: ZERO_HASH,
|
||||
prev_root: ZERO_HASH,
|
||||
output_root: ZERO_HASH,
|
||||
range_proof_root: ZERO_HASH,
|
||||
kernel_root: ZERO_HASH,
|
||||
|
@ -211,9 +215,9 @@ impl Writeable for BlockHeader {
|
|||
/// Deserialization of a block header
|
||||
impl Readable for BlockHeader {
|
||||
fn read(reader: &mut Reader) -> Result<BlockHeader, ser::Error> {
|
||||
let (version, height) = ser_multiread!(reader, read_u16, read_u64);
|
||||
let (version, height, timestamp) = ser_multiread!(reader, read_u16, read_u64, read_i64);
|
||||
let previous = Hash::read(reader)?;
|
||||
let timestamp = reader.read_i64()?;
|
||||
let prev_root = Hash::read(reader)?;
|
||||
let output_root = Hash::read(reader)?;
|
||||
let range_proof_root = Hash::read(reader)?;
|
||||
let kernel_root = Hash::read(reader)?;
|
||||
|
@ -230,8 +234,9 @@ impl Readable for BlockHeader {
|
|||
Ok(BlockHeader {
|
||||
version,
|
||||
height,
|
||||
previous,
|
||||
timestamp: DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc),
|
||||
previous,
|
||||
prev_root,
|
||||
output_root,
|
||||
range_proof_root,
|
||||
kernel_root,
|
||||
|
@ -250,11 +255,9 @@ impl BlockHeader {
|
|||
writer,
|
||||
[write_u16, self.version],
|
||||
[write_u64, self.height],
|
||||
[write_i64, self.timestamp.timestamp()],
|
||||
[write_fixed_bytes, &self.previous],
|
||||
[write_i64, self.timestamp.timestamp()]
|
||||
);
|
||||
ser_multiwrite!(
|
||||
writer,
|
||||
[write_fixed_bytes, &self.prev_root],
|
||||
[write_fixed_bytes, &self.output_root],
|
||||
[write_fixed_bytes, &self.range_proof_root],
|
||||
[write_fixed_bytes, &self.kernel_root],
|
||||
|
|
|
@ -257,7 +257,7 @@ fn empty_block_serialized_size() {
|
|||
let b = new_block(vec![], &keychain, &prev, &key_id);
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
let target_len = 1_223;
|
||||
let target_len = 1_255;
|
||||
assert_eq!(vec.len(), target_len);
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ fn block_single_tx_serialized_size() {
|
|||
let b = new_block(vec![&tx1], &keychain, &prev, &key_id);
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
let target_len = 2_805;
|
||||
let target_len = 2_837;
|
||||
assert_eq!(vec.len(), target_len);
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ fn empty_compact_block_serialized_size() {
|
|||
let cb: CompactBlock = b.into();
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &cb).expect("serialization failed");
|
||||
let target_len = 1_231;
|
||||
let target_len = 1_263;
|
||||
assert_eq!(vec.len(), target_len);
|
||||
}
|
||||
|
||||
|
@ -297,7 +297,7 @@ fn compact_block_single_tx_serialized_size() {
|
|||
let cb: CompactBlock = b.into();
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &cb).expect("serialization failed");
|
||||
let target_len = 1_237;
|
||||
let target_len = 1_269;
|
||||
assert_eq!(vec.len(), target_len);
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ fn block_10_tx_serialized_size() {
|
|||
let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id);
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &b).expect("serialization failed");
|
||||
let target_len = 17_043;
|
||||
let target_len = 17_075;
|
||||
assert_eq!(vec.len(), target_len,);
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ fn compact_block_10_tx_serialized_size() {
|
|||
let cb: CompactBlock = b.into();
|
||||
let mut vec = Vec::new();
|
||||
ser::serialize(&mut vec, &cb).expect("serialization failed");
|
||||
let target_len = 1_291;
|
||||
let target_len = 1_323;
|
||||
assert_eq!(vec.len(), target_len,);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,9 +95,9 @@ fn build_block(
|
|||
key_id: Option<Identifier>,
|
||||
wallet_listener_url: Option<String>,
|
||||
) -> Result<(core::Block, BlockFees), Error> {
|
||||
// prepare the block header timestamp
|
||||
let head = chain.head_header()?;
|
||||
|
||||
// prepare the block header timestamp
|
||||
let mut now_sec = Utc::now().timestamp();
|
||||
let head_sec = head.timestamp.timestamp();
|
||||
if now_sec <= head_sec {
|
||||
|
|
Loading…
Reference in a new issue