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

Verify headers and blocks only when needed #3023

Merged
merged 3 commits into from
Oct 27, 2019

Conversation

hashmap
Copy link
Contributor

@hashmap hashmap commented Sep 6, 2019

Currently we have some lightweight validation implemented as part of
entity deserialization, which is safer and allows us to not parse the
entire object if some part is invalid. At the same time this logic
always applies when we read an entity, eg when reading from DB.

This PR introduces UntrustedHeader/Block which is used when we read from
the network. It does partial validation during read, then it is supposed
to be converted into regular header/block which doesn't validate itself.

Also this PR adds "lightweight" validation to block header read like we have
for block body, so we don't parse block body if the header is invalid.

Fixes #1642

Copy link
Member

@antiochp antiochp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the approach here - just need to grab some time to review further.

Copy link
Member

@antiochp antiochp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We read "compact blocks" in protocol.rs -

let b: core::CompactBlock = msg.body()?;

Should this be untrusted as well to ensure we run the necessary validation on the header for the compact block?

Actually the only time we do anything with compact blocks is when we receive them via p2p so maybe these are always untrusted?

// We want to do this here because blocks can be pretty large
// and we want to halt processing as early as possible.
// If we receive an invalid block version then the peer is not on our hard-fork.
if !consensus::valid_header_version(height, version) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the valid_header_version check worth moving into UntrustedBlockHeader?
It takes a read lock on the global CHAIN_TYPE internally and we presumably don't need to do this check every time we read a header from the db.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable, will move it there

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Curretnly we have some lightweigt validation implemented as part of
entity deserialization, which is safer and allows us to not parse the
entire object if some part is invalid. At the same time this logic
always applies when we read an entity, eg when reading from DB.

This PR introduces UntrustedHeader/Block which is used when we read from
the network. It does partial validation during read, then it is supposed
to be converted into regular header/block which doesn't validate itself.

Also this PR adds "lightweight" validation to block header read like we have
for block body, so we don't parse block body if the header is invalid.

Fixes mimblewimble#1642
@hashmap hashmap force-pushed the header_verify_read branch from 7cff552 to 719ef01 Compare October 22, 2019 11:44
@hashmap
Copy link
Contributor Author

hashmap commented Oct 22, 2019

Actually the only time we do anything with compact blocks is when we receive them via p2p so maybe these are always untrusted?

I ended up having an additional Untrusted type, for symmetry, also some code relies on the existing structure of CompactBlock, so it's cleaner to read UntrustedCompactBlock and then convert it

@@ -365,7 +365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"

[[package]]
name = "grin_core"
version = "2.1.0-beta.1"
version = "3.0.0-alpha.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes should not be necessary on master.


/// Deserialization of an untrusted block header
impl Readable for UntrustedBlockHeader {
fn read(reader: &mut dyn Reader) -> Result<UntrustedBlockHeader, ser::Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we never actually pass an UntrustedBlockHeader around what this might look like if we modified this signature to actually return the trusted version once we had successfully read it -

impl Readable for UntrustedBlockHeader {
	fn read(reader: &mut dyn Reader) -> Result<BlockHeader, ser::Error> {
        ...
        }
    ...
}

I assume we cannot do this easily because of how Readable is defined but it would potentially allow us to not need the additonal explicit .into() to do conversions later.

Maybe being explicit like this is better though.

Copy link
Member

@antiochp antiochp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good 👍

Question for further discussion, but not a blocker for merging this as is.

@hashmap hashmap merged commit 1f5de6b into mimblewimble:master Oct 27, 2019
@hashmap hashmap deleted the header_verify_read branch October 27, 2019 07:41
@antiochp antiochp added this to the 3.0.0 milestone Dec 11, 2019
@antiochp antiochp mentioned this pull request Dec 11, 2019
return Err(ErrorKind::LowEdgebits.into());
}
let edge_bits = header.pow.edge_bits();
if !(ctx.pow_verifier)(header).is_ok() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hashmap Where does this check happen now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only place I see similar code is in validate_pow_only() but this is only used for orphan blocks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do not parse block body before header has been validated
2 participants