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

Deterministic Pact SPV test #934

Merged
merged 10 commits into from
Feb 25, 2020
Merged

Deterministic Pact SPV test #934

merged 10 commits into from
Feb 25, 2020

Conversation

sirlensalot
Copy link
Contributor

No description provided.

@sirlensalot sirlensalot changed the title Pact SPV test overhaul to not use CutDb Deterministic Pact SPV test Feb 23, 2020
Copy link
Contributor

@larskuhtz larskuhtz left a comment

Choose a reason for hiding this comment

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

Could you also enable the Pact.SPV tests in .github/workflows/applications.yaml?

src/Chainweb/Cut/Test.hs Show resolved Hide resolved
:: HasCallStack
=> HasChainId cid
=> (BlockHeader -> Bool)
-- ^ POW validator
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you tried if the test works with calling createNewCut?

I think, the same can be achieved by using a chainweb version that has trivial target and doesn't do DA. I think that's the case for most test versions.

Copy link
Contributor Author

@sirlensalot sirlensalot Feb 24, 2020

Choose a reason for hiding this comment

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

It's possible, but that's not the intention. The point here is to use mocking techniques to avoid violating separation-of-concerns in unit tests.

Using ChainwebVersion is less desirable, as it is overloaded with three separate concerns:

  1. A protocol version. This in itself (protocol version compliance) is not of interest to many subsystems.
  2. A graph. This is relevant to multi-chain-aware subsystems.
  3. A hashing algorithm/DA/difficulty regime. This is relevant to mining- and DA-concerned subsystems.

Notice how all three of these properties are irrelevant to some single-chain subsystem that is not involved in mining or DA or protocols.

Here, by mocking out the nonce validation, a test is able to precisely specify whether 3 is of concern. (const True) (maybe with a suitably literate type synonym) is the most accurate way to say that mining difficulty is irrelevant, in which case it is strictly better as it makes a test less brittle if later on it needs to change something regarding 2 or 1.

Finally, leaving it to ChainwebVersion means that the only true safe way to use these functions is via a mine function, in case the protocol requires mining. This necessarily involves a clock, randomness etc, and is explicitly what I wanted to avoid.

Copy link
Contributor

Choose a reason for hiding this comment

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

Short reply:

the test already depends on being used with a chainweb version that uses a trivial POW target, because insert does POW validation. Passing const True seems thus redundant.

Long reply:

The consistency rules for a chainweb are part of the chainweb version, which are implemented by the validation rules. Validation includes:

  1. block header validation (intrinsic and indicutive)
  2. cut validation (braiding structure of chainweb)
  3. payload validation (construction of payload Merkle tree, tx uniqueness, tx timing validation, gas, etc.)
  4. pact validation (computation of outputs via pact validation)

In the original design we used parameterized test chainweb versions to tweak consistency properties for testing. In most cases properties are statically bound to a particular version. Currently only the graph can be changed freely on some test versions. But @fosskers and I have been discussing to add free more parameters, like for instance the block rate.

In the current code we don't follow this original design everywhere, but we use chainweb versions to disable certain validation properties. For instance most test versions already use a trivial POW target, which is equivalent to passing const True to the POW validator.

Copy link
Contributor

Choose a reason for hiding this comment

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

The idea behind using the chainweb version to relax validation constraints is to gather all configurations in a single place (Chainweb.Version) and avoid accidentally disabling or changing consistency properties. We try to make it impossible (or at least hard) to run code with chainweb version Mainnet01 (or any other production version) with some consistency rules altered or disabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I got confused by the check in the code here, I didn't realize insert did it as well.

In that case the indirection is indeed unnecessary. On the larger topic above, I might want to allow tests to set the target to 0 (irrespective of what comes out of newHeader and it's merkle hash), but that's for a later debate. I'll switch to using createNewCut and drop this function.

:: WebBlockHeaderDb
-> Nonce
-> GenBlockTime
-- ^ block time generation function
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

{ _bdbWebBlockHeaderDb :: WebBlockHeaderDb
, _bdbPayloadDb :: PayloadDb RocksDbCas
, _bdbCut :: MVar Cut
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Just as a note: It might make sense to base the actual CutDB on a data type like this and add all reactive functionality as an extra layer.

That's out of scope for this PR. Just an idea to remember for future cut db refactoring.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's very interesting. This was the "natural" result of finding the common dependencies of the SPV test, combined with the MVar Cut to simply use "current cut" as a shortcut for having a "latest block" concept.

But now that you mention it, it does resemble CutDB (swap a TVar in anyway)!

In general I was very pleased with how easy this refactoring was, which speaks extremely highly of the underlying design.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also I would be happy to do your suggested refactoring. In general I am very glad to be getting to know these parts of the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just be aware that it might lead you to the less pleasant territories of the underlying design :-) Namely the code for validating remote cuts and fetching dependencies. But separating that from a local-only cut db might be useful.

runCut bdb pact = do
forM_ (chainIds v) $ \cid -> do
ph <- getParentTestBlockDb bdb cid
-- print (_blockHeight ph, cid)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
-- print (_blockHeight ph, cid)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a useful debug output so I wanted to keep it. Should I instead make it a Debug log?

Copy link
Contributor

Choose a reason for hiding this comment

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

Just ignore the suggestion. I was just not sure if the comment was there intentionally or just forgotten.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

on second thought I'll just remove it

c <- readMVar cmv
cutToPayloadOutputs c pdb

runCut :: TestBlockDb -> WebPactExecutionService -> IO CutOutputs
Copy link
Contributor

Choose a reason for hiding this comment

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

This function seems very useful. Could you add a comment that describes what the functions does?

I think this code is useful beyond the scope of the tests in this module. Maybe move it to a a new test utils module (e.g. Chainweb.Test.Utils.Cuts) to make it easier to reuse?

Copy link
Contributor Author

@sirlensalot sirlensalot Feb 24, 2020

Choose a reason for hiding this comment

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

I'll comment and move it to Chainweb.Test.Pact.Utils alongside testWebPactExecutionService.

I'm not so sure about CutOutputs, do you think that's worth promoting? If not this function can simply be IO ().

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure how useful the outputs are. However, throwing in a void on the use side isn't a big deal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm ... the problem with this function is it hardcodes the requirements of addPayloadBlock above ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd need to add a "payload adder" indirection basically with the signature of addPayloadBlock. Do you think that isn't too wonky for promotion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try it out and see.


c0 <- _cut cutDb
-- cut 0: empty run (not sure why this is needed but test fails without it)
void $ runCut bdb pact
Copy link
Contributor

Choose a reason for hiding this comment

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

Does testWebPactExecutionService synchronize the pact with the current cut? It calls evalPactServiceM_ ctx (initialPayloadState dummyLogger v cid), but I am not sure if that's enough to fully initialize the checkpointer.

Copy link
Contributor

Choose a reason for hiding this comment

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

The following code is used in a chainweb node to sync the checkpointer. I don't know if this is needed, too, in case bh is the genesis block.

        syncOne :: (BlockHeader, ChainResources logger) -> IO ()
        syncOne (bh, cr) = do
            let pact = _chainResPact cr
            let logCr = logFunctionText $ _chainResLogger cr
            let hsh = _blockHash bh
            let h = _blockHeight bh
            logCr Info $ "pact db synchronizing to block "
                      <> T.pack (show (h, hsh))
            payload <- payloadWithOutputsToPayloadData
                       <$> casLookupM payloadDb (_blockPayloadHash bh)
            void $ _pactValidateBlock pact bh payload
            logCr Info "pact db synchronized"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure, I'll investigate.

Copy link
Contributor

Choose a reason for hiding this comment

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

it may also be just a corner case related to starting with the genesis blocks. In production networks there probably never will be normal user txs at single digit block heights.

@sirlensalot sirlensalot merged commit c9d8c11 into master Feb 25, 2020
@sirlensalot sirlensalot deleted the feat/spv-test-overhaul branch February 25, 2020 03:41
@fosskers fosskers added component: pact-service The Pact Service, or Pact language updates in general. component: testing labels Mar 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: pact-service The Pact Service, or Pact language updates in general. component: testing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants