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

sync on discovering a "longer" chain #412

Closed
antiochp opened this issue Dec 1, 2017 · 2 comments
Closed

sync on discovering a "longer" chain #412

antiochp opened this issue Dec 1, 2017 · 2 comments

Comments

@antiochp
Copy link
Member

antiochp commented Dec 1, 2017

Here's the scenario (contrived example, I believe this happened over a larger number of blocks on both forks of the chain) -

We're happily processing blocks on the current chain -

b1 ____ b2 ____ b3 ____ b4

A new peer joins the network and advertises a previously unknown longer chain (both in terms of total_difficulty and height) -

b1 ____ b2 ______ b3 ___________________ b4
            |
            L___ b3' ____ b4' ____ b5' ____ b6'
  • Before we sync -
    • head points at b4.
    • header_head points at b4.
  • We quickly sync headers so that -
    • head remains at b4
    • sync_head points now at b6'
    • header_head is updated to b6' (header with most work)
    • But note - b4 has greater total_difficulty than both b4' and b5'

We now build a Locator to go request blocks [b3', b4', b5', b6'].

Processing blocks b3', b4' b5' are all fine -

  • head remains pointing original b4, still block with most work

Processing block b6 fails validation (and is flagged an Orphan)
because while b6' has more total_difficulty than b4 it is more than one block higher than the original b4. There is no b5' between b4 and b6'.

It actually fails validation twice.
Once in validate_header (and if this were to pass) again in validate_block -

	if b.header.height > ctx.head.height + 1 {
		return Err(Error::Orphan);
	}

Q1) Should header_head be updated during sync if we process a new header on the longest chain (at which point it diverges from the current body_head)? These can be on divergent forks as in the above scenario.
Q2) When processing a block we validate both the header and the block. If the header_head and the body_head can diverge then this necessitates passing in two separate contexts to process_block (for the two divergent chain heads)?
Q3) How can we robustly handle Orphan detection in this case where we jump suddenly from one fork to another and the heights are not strictly sequential?

@ignopeverell
Copy link
Contributor

You're right, this is bogus:

  if header.height > ctx.head.height + 1 {
    return Err(Error::Orphan);
  }

And this later on should match for a NotFoundErr and in that case return an Error::Orphan.

  let prev = try!(ctx.store.get_block_header(&header.previous,).map_err(|e| {
    Error::StoreErr(e, format!("previous block header {}", header.previous))
  },));

Forks do not require a head. They just need to have some previous block that's findable. Does that make sense?

@antiochp
Copy link
Member Author

antiochp commented Dec 1, 2017

Fixed (I believe) on testnet1 via #413 .
Will be fixed on master in #410.

ignopeverell pushed a commit that referenced this issue Dec 13, 2017
* Very quick peer banning endpoint, helps with #406
* Ping heights (#407)
* add height to ping/ping
* reformat output
* fix p2p test
* Fix orphan handling, not related to current head. Fixes #412
* Check before borrow, fixes #267
* Not finding an output commit in pos is an AlreadySpent
* Fix race condition, sending before conn is ready
* Explicit error for unknown pos of a forked block
* Remove config outdated tests. Fix #333
* Check ref and try before borrow, fix #400
* We do not want to sync with old peers anyway
* Hide cargo compiler warning for unused NoopAdapter and unused test code. Add TODOs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants