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

Block: support uncle validation #935

Merged
merged 5 commits into from Nov 16, 2020
Merged

Block: support uncle validation #935

merged 5 commits into from Nov 16, 2020

Conversation

jochem-brouwer
Copy link
Member

@jochem-brouwer jochem-brouwer commented Oct 31, 2020

Closes #933

This PR fixes the following TODO in block:

    // TODO: Validate that the uncle header hasn't been included in the blockchain yet.
    // This is not possible in ethereumjs-blockchain since this PR was merged:
    // https://github.com/ethereumjs/ethereumjs-blockchain/pull/47

@codecov
Copy link

codecov bot commented Oct 31, 2020

Codecov Report

Merging #935 (6286888) into master (9b86316) will increase coverage by 0.95%.
The diff coverage is 79.48%.

Impacted file tree graph

Flag Coverage Δ
block 77.69% <79.48%> (+2.65%) ⬆️
blockchain 77.39% <ø> (ø)
common 91.87% <ø> (ø)
ethash 82.08% <ø> (ø)
tx 86.04% <ø> (-0.22%) ⬇️
vm 87.21% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@jochem-brouwer jochem-brouwer force-pushed the blockchain-concurrency-and-safety-improvements branch 5 times, most recently from 4db67e9 to 12f0dcb Compare November 8, 2020 13:11
Base automatically changed from blockchain-concurrency-and-safety-improvements to master November 9, 2020 13:21
@holgerd77
Copy link
Member

Ok, actually 2nd last "biggie" I am getting aware. 🙃 Is this intended to go into v5 as well? Likely, right?

@jochem-brouwer
Copy link
Member Author

Hi @holgerd77, I have created a seperate PR which handles the requirements for this specific PR: this is #942. The uncle handling logic is rather complex; the actual logic itself depends on changing the current interface of blocks' validate method to make the blockchain argument mandatory instead of optional; this is thus in #942. I have marked those as "P2 very important" to indicate those should be in V5.

@holgerd77
Copy link
Member

Ok, great, than please rebase #942 as soon as you'll have some time.

@jochem-brouwer
Copy link
Member Author

Ah yes good point, will do now.

@jochem-brouwer jochem-brouwer changed the base branch from master to block-blockchain-validation-mandatory November 9, 2020 22:11
@jochem-brouwer
Copy link
Member Author

Wrote a massive amount of uncle tests here, which I think are also great because of the educational value. This should cover all rules regarding uncles 😄

I hope the VM tests pass now.

@jochem-brouwer jochem-brouwer changed the title block: preliminary work on validating uncles Block: support uncle validation Nov 9, 2020
@jochem-brouwer
Copy link
Member Author

VM tests fail but this is due to the linter? I haven't touched VM here so the linter should pass? Does maybe ESLint here have a different version or something? @evertonfraga

Anyways this is ready for review 👍 😄

@jochem-brouwer jochem-brouwer marked this pull request as ready for review November 9, 2020 22:32
Base automatically changed from block-blockchain-validation-mandatory to master November 10, 2020 08:18
@jochem-brouwer
Copy link
Member Author

It would be great if we can get this in the RC release, as this completes the validation logic of block 😄

Will rebase.

block: fix validate uncles
block: fix option carry-over bug in uncle headers

block: remove comment, add changes to changelog
@jochem-brouwer
Copy link
Member Author

OK, ready for review again!

Copy link
Member

@holgerd77 holgerd77 left a comment

Choose a reason for hiding this comment

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

This seems to be a complex topic which needs to be handled with some care, not sure if this can still make it into the RC release, we'll see.

Have some starter questions.


// validate the header
for (let i = 0; i < uncleHeaders.length; i++) {
const header = uncleHeaders[i]
Copy link
Member

Choose a reason for hiding this comment

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

Also not a blocker, but I find for (const uncleHeader of uncleHeaders) more elegant and simple.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did all the others without a loop and instead with a map, don't know why I used a for loop here. Note that for(const x of y) is something that ESLint does not like (we have ignored that rule). I don't know why ESLint doesn't like that syntax.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah right, I can't directly use map because it calls an async function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Have rewritten this using map and Promise.all, personally, I like it 😄

parentHash = parentBlock.header.parentHash
}

canonicalBlockMap.map((block) => {
Copy link
Member

Choose a reason for hiding this comment

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

It would be good if these different validation sections in _validateUncleHeaders get some short comment headers (maybe analogue to the already commented validateUncles starting section) to structure this a bit and grasp what is going on.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes good point, will do this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Have squashed this logic using some helpers, should be easier to read now 😄

packages/block/src/block.ts Show resolved Hide resolved
}
})

const getBlocks = this.header.number.clone().sub(lowestUncleNumber).addn(1).toNumber()
Copy link
Member

Choose a reason for hiding this comment

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

If the lowestUncleNumber here is e.g. 1 for whatever reason the following loop would go and re-read the whole blockchain. This can't be right I would assume? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

I am not seeing why this would read the whole blockchain. Please note that in validating the headers, it is checked that the uncle is at most 8 blocks older, so this should put an upper bound of the amount of blocks being read.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, I wasn't aware of this check in uncle header validation.

@jochem-brouwer
Copy link
Member Author

Okay, upon re-reading the code after your comments @holgerd77 I think I can simplify this code a bit more by introducing some helper variables. I am using map and filter in this validation function but I think I can just make this a bit simpler by creating some lookup tables which indicate if certain hashes match certain properties or not. Will update soon including moving some of the comments around / writing some more comments.

@jochem-brouwer
Copy link
Member Author

Okay, just updated this one, moved some comments around and made the validation logic better readable (I hope) 😄 . Ready for re-review.

Copy link
Member

@holgerd77 holgerd77 left a comment

Choose a reason for hiding this comment

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

Thanks @jochem-brouwer, this code is now super-clean and really a great read! 👍

One structural suggestion on combining the methods but we can also directly proceed if you don't have time for the change right now and would rather like to go on.

for (const uh of this.uncleHeaders) {
await this._validateUncleHeader(uh, blockchain)
}
await this._validateUncleHeaders(this.uncleHeaders, blockchain)
Copy link
Member

Choose a reason for hiding this comment

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

On a second look I don't think it makes any sense to keep this double public/private method structure with validateUncles() and _validateUncleHeaders(), these double this.uncleHeaders.length checks (one on > 2, the other on === 0) rather makes this in the current split form more inconsistent, do we want to throw this into one function? 🤔 This double documentation is also a bit "redundancy for nothing" for my taste.

}
})

const getBlocks = this.header.number.clone().sub(lowestUncleNumber).addn(1).toNumber()
Copy link
Member

Choose a reason for hiding this comment

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

Thanks, I wasn't aware of this check in uncle header validation.

// Helper variable: set hash to `true` if uncle hash is included in any canonical block
const includedUncles: { [key: string]: boolean } = {}

// Due to the header validation check above, we know that `getBlocks` it between 1 and 8 inclusive.
Copy link
Member

Choose a reason for hiding this comment

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

Super nit-pick: "it" -> "is"

@holgerd77 holgerd77 mentioned this pull request Nov 16, 2020
18 tasks
Copy link
Member

@holgerd77 holgerd77 left a comment

Choose a reason for hiding this comment

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

On a second tought: this method combination question is such an internal thing, have added this to the tiny-fixes-issue and will merge here now, so we keep a blank slate for the releases.

@holgerd77 holgerd77 merged commit 81fc88b into master Nov 16, 2020
@holgerd77 holgerd77 deleted the validate-uncles branch November 16, 2020 11:50
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.

Block: validate if uncle not included in canonical chain
2 participants