-
Notifications
You must be signed in to change notification settings - Fork 2k
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
improve handling of UnfinishedBlock
s
#17247
Conversation
6f6f468
to
c5244a2
Compare
This comment was marked as outdated.
This comment was marked as outdated.
c5244a2
to
47173fa
Compare
This comment was marked as outdated.
This comment was marked as outdated.
47173fa
to
629661f
Compare
Pull Request Test Coverage Report for Build 7611459731Warning: This coverage report may be inaccurate.We've detected an issue with your CI configuration that might affect the accuracy of this pull request's coverage report.
💛 - Coveralls |
8ef5606
to
8cbd204
Compare
UnfinishedBlock
s with the same reward hash but different foliageUnfinishedBlock
s
8cbd204
to
49eb914
Compare
I think it's fine to let through the missing coverage. there are two cases not covered by tests:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take these comments with a grain of salt, way out of my depth here so they could be completely wrong. But I've taken a look anyways, and worst case I learn something new 😄
129d3fb
to
ed112f5
Compare
ed112f5
to
14514b7
Compare
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
…v2 unfinished blocks
…inished blocks with the same partial hash but different foliage
14514b7
to
1a3dbfc
Compare
Conflicts have been resolved. A maintainer will review the pull request shortly. |
1a3dbfc
to
6c0457a
Compare
6c0457a
to
2b60121
Compare
I think it's reasonable to not add coverage to the case where we're syncing in this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good otherwise
…partial hash; return the highest ranking (lowest foliage tx block hash)
2b60121
to
87df80a
Compare
|
Is there any material difference in a return of |
@emlowe the Here's the main case: https://github.com/Chia-Network/chia-blockchain/pull/17247/files#r1464906269 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aok
This PR is best reviewed one commit at a time.
Background
UnfinishedBlock
When a farmer find a valid proof-of-space, it builds a block and distributes it to the network (an
UnfinishedBlock
). This block only has a valid proof-of-space, in order to become complete and part of the blockchain (aFullBlock
), it also needs to be infused with proof-of-time, by a timelord.When propagating an
UnfinishedBlock
, full nodes notifies all of its peers about the new block and peers that haven't already seen it will request it. Full nodes don't request the same block multiple times. i.e. If a block has already been seen (and already propagated), we don't request it.UnfinishedBlock
s are (currently) identified by their proof-of-space. Specifically, the reward block hash.FullNodeStore
When the full node receives an
UnfinishedBlock
, it's validated, stored in the full node store and then peers are notified of the new unfinished block.The full node store currently treats the reward block hash as a unique identifier for
UnfinishedBlock
s, and only stores a single block for a given reward block hash.Protocol
When we notify our peers about a new
UnfinishedBlock
, we send aNewUnfinishdeBlock
message, which looks like this:If the peer has not seen an
UnfinishedBlock
with this reward block hash, it requests it, by sending aRequestUnfinishedBlock
message, which looks like this:Just like with the full node store,
UnfinishedBlock
s are identified by their reward block hash.Scenario
When a farmer runs multiple (redundant) full node and farmer instances, harvesting the same plots, it's possible to produce multiple different
UnfinishedBlocks
with the same proof-of-space. Full nodes may have slightly different mempool, and therefore produce slightly different blocks. For example, consider a new transaction being propagated throughout the network that has reached one node but not the other when the block is created.Since the network consider these blocks the same (because the reward block hash, i.e. proof-of-space) is the same, they will each propagate to nodes and which one is seen by which node is somewhat random. In a system with many redundant timelords, different timelords are likely to infuse different versions of the unfinished blocks. The timelords will not even be aware of the existence of the other variant, because it's never propagated in that part of the network.
These blocks, once infused, will form competing chains, eventually resolved by one side of the network re-orging to the other chain.
Purpose
The purpose of this change is to improve the determinism of which block is to become the next block in the chain by making the network propagate all variants of the
UnfinishedBlock
s. This means the timelords will have access to all and can pick which one to infuse deterministically.Technically, the timelords are only infusing proofs-of-space, and it's the full node that the timelord is connected to that makes the decision of which variant of the
UnfinishedBlock
to be infused.This change, once popular in the network, is believed to reduce the risk of short-lived competing chains and reorgs.
Changes
Propagating all variants of
UnfinishedBlock
s requires changes to:UnfinishedBlock
s under the same reward block hashUnfinishedBlock
s not just by their reward block hash, but also their foliage transaction block hashUnfinishedBlock
to infuseThe commits in this PR make the above changes in that order.
Double requesting blocks
Currently, when we learn about an unfinished block we want to request, we record making the request in the full node store, by the reward block hash (
requesting_unfinished_blocks
). If some other peer tell us about an unfinished block with the same hash, we won't request it as long as we have an outstanding request already.This logic needs some changes when adding requests for unfinished blocks, not just by their reward block hash, but also foliage hash.
One commit add a second collection to the full node store, (
requesting_unfinished_blocks2
), recording both block reward hash and foliage hash of the outstanding requests. This means we can have multiple outstanding requests for blocks with the same reward block hash, as long as they have different foliage hashes.There is one issue with this. If an old peer tells us about a block with reward block hash A (and we make a request). Then a new peer tells us about a block with reward block hash A and foliage hash B. We can't know that these are the same, and we request block (A, B) from the new peer.
However, if we first learn about the block from a new peer and request (A, B), if we then learn about a block with hash A (from an old peer), we assume it's the same block.
This will cause some duplicate unfinished blocks to be downloaded. At least in the beginning of the network upgrade period.
Full node store
The underlying data structure of the full node store for unfinished blocks is this:
The value type of this dictionary was changed to include another dictionary, keyed by the foliage transaction block hash.
Along with new methods to query an
UnfinishedBlock
by both its reward block hash and its foliage transaction block hash. (get_unfinished_block2()
andget_unfinished_block_result2()
).Network protocol
We need to remain backwards compatibility with the network protocol, so we can't change the existing messages. We need to add two new messages:
NewUnfinishedBlock2
andRequestUnfinishedBlock2
. These messages are like the existing ones except they both add an optional field for the foliage transaction block hash of theUnfinishedBlock
.When we propagate a new
UnfinishedBlock
to peers, we now have to send the new protocol message to new peers, and the old message to old peers. To know whether a peer supports the new messages, we bump the protocol version from 0.35.0 to 0.36.0.Any peers that advertise protocol version 0.36.0 will receive a
NewUnfinishedBlock2
message, and older peers will receive the currentNewUnfinishedBlock
message. This required adding a new variant ofsend_to_all()
on theService
class. Specifically one where you can pass a predicate function to filter peers. This lets you send the new message only to new peers and vice versa.Anti-DOS
In order to prevent a malicious farmer (that finds a valid proof-of-space) from flooding the network with different variants of
UnfinishedBlock
s, nodes will only forward 3 variants of a block (given the same proof-of-space). This is configurable inconfig.yaml
asmax_duplicate_unfinished_blocks
. This limit is believed to be appropriate to support non-malicious cases of farmers running redundant nodes.The Full Node Store exposes the number of blocks it stores under each reward block hash. When this number is too high, we stop propagating notifications of more
UnfinishedBlock
s.Deterministic block selection
The rule for picking the
UnfinishedBlock
to infuse is simply the one with the lowest foliage transaction block hash. If the block is not a transaction block, it doesn't have such hash, and there is only one block to chose from.This rule is implemented and tested in the last commit of this PR. It only affects the full node store, since full nodes are the ones converting
UnfinishedBlock
s toFullBlocks
in response to the timelord. The timelord, and the timelord protocol, is agnostic to foliage. Only proofs of space are infused, and it's up to the full node to pick the block to infuse.Therefore, the full node store returns the "best" unfinished block whenever it's asked for one just based on the reward block hash.
Current Behavior:
Only a single variant of
UnfinishedBlock
s are propagated by nodes, possibly splitting the networks into sections that see different variants.New Behavior:
up to 3 variants of
UnfinishedBlock
s are propagated by nodes, making it very likely that all nodes (and all timelords) see all variants.