-
Notifications
You must be signed in to change notification settings - Fork 23
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
Fix GetIsValid
bugs (once and for all?)
#279
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -424,7 +424,11 @@ addBlock cfg blk m = Model { | |||||||||||
immutableChainHashes `isPrefixOf` | ||||||||||||
map blockHash (Chain.toOldestFirst fork) | ||||||||||||
|
||||||||||||
consideredCandidates = filter (extendsImmutableChain . fst) candidates | ||||||||||||
-- Considered candidates are candidates that extend the immutable part of | ||||||||||||
-- the chain. Furthermore, the current selected chain is not considered as a | ||||||||||||
-- candidate. | ||||||||||||
Comment on lines
+427
to
+429
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
consideredCandidates = filter ((/= CPS.chainState (cps m)) . fst) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the other changes, is this now actually necessary to make any tests pass? Again, motivation is that this would be a difference to #284. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, I don't think it's necessary. If the current chain is the best candidate, then chain selection is idempotent (not sure if it's the right word). Including the current chain if it isn't the best candidate shouldn't have any effect on the outcome. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we use |
||||||||||||
$ filter (extendsImmutableChain . fst) candidates | ||||||||||||
|
||||||||||||
newChain :: Chain blk | ||||||||||||
newLedger :: ExtLedgerState blk | ||||||||||||
|
@@ -436,109 +440,10 @@ addBlock cfg blk m = Model { | |||||||||||
(currentChain m) | ||||||||||||
$ consideredCandidates | ||||||||||||
|
||||||||||||
-- See note [Getting the valid blocks] just below | ||||||||||||
valid' = foldl | ||||||||||||
(Chain.foldChain (\s b -> Set.insert (blockHash b) s)) | ||||||||||||
(valid m) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this to extend the previous
Suggested change
Motivation is that this would be a difference to #284. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm hesitant towards this, because the model could "forget" valid blocks if the blocks are on a chain that isn't connected to the Genesis block anymore. That was the motivation for IntersectMBO/ouroboros-network#3651 (comment). Assume we use Garbage collection on the model is instantaneous, while garbage collection on the SUT is not. The real VolatileDB knows about the validity of all blocks in it, even if those blocks are disconnected. If we ask the SUT if a disconnected block is valid, and it returns There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm now also unsure whether we should have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very good point, I went back to 90b3bdf, where I can reproduce IntersectMBO/ouroboros-network#3389. Now it turns out that c8a6186 (what I originally thought would be enough as the entire fix, ie that was the fifth commit in #284 before I force-pushed it) does not fix the bug. Instead, maintaining the set of valid blocks cumulatively does fix the bug, I pushed that know to #284. Note that it does not restrict the model
So I think we could indeed remove this. |
||||||||||||
(takeWhile (not . Chain.isPrefixOf newChain) | ||||||||||||
(map fst consideredCandidates) ++ [newChain]) | ||||||||||||
|
||||||||||||
-- = Getting the valid blocks | ||||||||||||
-- | ||||||||||||
-- The chain selection algorithms implemented by the model and by the SUT differ | ||||||||||||
-- but have the same outcome.We illustrate this with an example. Imagine having | ||||||||||||
-- the following candidate chains where @v@ represents a valid block and @x@ | ||||||||||||
-- represents an invalid block: | ||||||||||||
-- | ||||||||||||
-- > C0: vvvvvxxxxx | ||||||||||||
-- > C1: vvvvvvvx | ||||||||||||
-- > C2: vvv | ||||||||||||
-- | ||||||||||||
-- For candidate Cx, we will call CxV the valid prefix and CxI the invalid suffix. | ||||||||||||
-- | ||||||||||||
-- The chain selection algorithm will run whenever we add a block, although it | ||||||||||||
-- will only select a new chain when adding a block results in a chain that is | ||||||||||||
-- longer than the currently selected chain. Note that the chain selection | ||||||||||||
-- algorithm doesn't know beforehand the validity of the blocks in the | ||||||||||||
-- candidates. The process it follows will be: | ||||||||||||
-- | ||||||||||||
-- 1. Sort the chains by 'SelectView'. Note that for Praos this will trivially | ||||||||||||
-- imply first consider the candidates by length. | ||||||||||||
-- | ||||||||||||
-- > sortedCandidates == [C0, C1, C2] | ||||||||||||
-- | ||||||||||||
-- 2. Until a candidate is found to be valid and longer than the currently selected | ||||||||||||
-- chain, take the head of the (sorted) list of candidates and validate the | ||||||||||||
-- blocks in it one by one. | ||||||||||||
-- | ||||||||||||
-- If a block in the candidate is found to be invalid, the candidate is | ||||||||||||
-- truncated, added back to the list, and the algorithm starts again at step 1. | ||||||||||||
-- The valid blocks in the candidate are recorded in the set of known-valid | ||||||||||||
-- blocks, so that the next time they are applied, it is known that applying | ||||||||||||
-- said block can't fail and therefore some checks can be skipped. The invalid | ||||||||||||
-- blocks in the candidate are recorded in the set of known-invalid blocks so | ||||||||||||
-- that they are not applied again. | ||||||||||||
-- | ||||||||||||
-- The steps on the example are as follows: | ||||||||||||
-- | ||||||||||||
-- 1. Start with the sorted candidate chains: [C0, C1, C2] | ||||||||||||
-- 2. Validate first chain C0 resulting in C0V and C0I. | ||||||||||||
-- 3. Append C0V to the list of remaining candidates: [C1, C2] ++ [C0V] | ||||||||||||
-- 4. Add the valid blocks to the state: | ||||||||||||
-- > knownValid = append C0V knownValid | ||||||||||||
-- 5. Add the invalid blocks to the state: | ||||||||||||
-- > knownInvalid = append C0I knownInvalid | ||||||||||||
-- 6. Re-sort list | ||||||||||||
-- > sortBy `selectView` [C1, C2, C0V] == [C1, C0V, C2] | ||||||||||||
-- 7. Validate first chain C1 resulting in C1V and C1I. | ||||||||||||
-- 8. Append C1V to the list of remaining candidates: [C0V, C2] ++ [C1V] | ||||||||||||
-- 9. Add the valid blocks to the state: | ||||||||||||
-- > knownValid = append C1V knownValid | ||||||||||||
-- 10. Add the invalid blocks to the state: | ||||||||||||
-- > knownInvalid = append C1I knownInvalid | ||||||||||||
-- 11. Re-sort list | ||||||||||||
-- > sortBy `selectView` [C0V, C2, C1V] == [C1V, C0V, C2] | ||||||||||||
-- 12. Validate first chain C1V, which is fully valid and returned. | ||||||||||||
-- | ||||||||||||
-- 3. If such a candidate is found, the algorithm will return it as a result. | ||||||||||||
-- Otherwise, the algorithm will return a 'Nothing'. | ||||||||||||
-- | ||||||||||||
-- > chainSelection [C0, C1, C2] = Just C1V | ||||||||||||
-- | ||||||||||||
-- On the other hand, the chain selection on the model takes some shortcuts to | ||||||||||||
-- achieve the same result: | ||||||||||||
-- | ||||||||||||
-- 1. 'validChains' will return the list of candidates sorted by 'SelectView' and | ||||||||||||
-- each candidate is truncated to its valid prefix. | ||||||||||||
-- | ||||||||||||
-- > validChains [C0, C1, C2] = (invalid == C0I + C1I, candidates == [C0V, C1V, C2]) | ||||||||||||
-- | ||||||||||||
-- 2. 'selectChain' will sort the chains by 'SelectView' but note that now it will | ||||||||||||
-- use the 'SelectView' of the already truncated candidate. | ||||||||||||
-- | ||||||||||||
-- > selectChain [C0V, C1V, C2] = listToMaybe (sortBy `selectView` [C0V, C1V, C2]) | ||||||||||||
-- > = listToMaybe ([C1V, C0V, C2]) | ||||||||||||
-- > = Just C1V | ||||||||||||
-- | ||||||||||||
-- The selected candidate will be the same one that the chain selection | ||||||||||||
-- algorithm would choose. However, as the chain selection algorithm will | ||||||||||||
-- consider the candidates as they were sorted by 'SelectView' on the | ||||||||||||
-- non-truncated candidates, blocks in 'C0V' are also considered valid by the | ||||||||||||
-- real algorithm. | ||||||||||||
-- | ||||||||||||
-- To get as a result a set of valid blocks that mirrors the one from the | ||||||||||||
-- real algorithm, the model can process the list of candidates returned by | ||||||||||||
-- 'validChains' until it find the one 'selectChain' chose as these will be | ||||||||||||
-- the ones that the real algorithm would test and re-add to the list once | ||||||||||||
-- truncated. | ||||||||||||
-- | ||||||||||||
-- > knownInvalid = append (C0I + C1I) knownInvalid | ||||||||||||
-- > knownValid = foldl append knownValid (takeWhile (/= C1V) candidates ++ [C1V]) | ||||||||||||
-- | ||||||||||||
-- Note that the set of known valid blocks is equivalent to the set computed | ||||||||||||
-- by real algorithm, but the set of known invalid blocks is a superset of | ||||||||||||
-- the ones known by the real algorithm. See the note | ||||||||||||
-- Ouroboros.Storage.ChainDB.StateMachine.[Invalid blocks]. | ||||||||||||
(map fst candidates) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I am asking this both as this is a difference to #284, and as |
||||||||||||
|
||||||||||||
addBlocks :: (Eq blk, LedgerSupportsProtocol blk) | ||||||||||||
=> TopLevelConfig blk | ||||||||||||
|
@@ -873,12 +778,8 @@ validChains cfg m bs = | |||||||||||
-- first, which results in the valid chain A->B', which is then chosen | ||||||||||||
-- over the equally preferable A->B as it will be the first in the list | ||||||||||||
-- after a stable sort. | ||||||||||||
sortChains $ pruneKnownInvalid <$> chains bs | ||||||||||||
sortChains $ chains bs | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the comment above still accurate? Wouldn't the reader be puzzled about why we don't filter the invalid chains first as in the SUT? |
||||||||||||
where | ||||||||||||
pruneKnownInvalid chain = | ||||||||||||
-- We don't want known invalid blocks to influence the sorting of candidate chains. | ||||||||||||
Chain.takeWhile (\b -> blockHash b `Map.notMember` (invalid m)) chain | ||||||||||||
|
||||||||||||
sortChains :: [Chain blk] -> [Chain blk] | ||||||||||||
sortChains = | ||||||||||||
sortBy $ flip ( | ||||||||||||
|
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.
Maybe this could say that
sortChains
takes the current chain separately?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.
If we add your suggestion in #279 (comment), then the comment can be reverted to what it previously was.