-
Notifications
You must be signed in to change notification settings - Fork 95
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
Conversation
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.
Could you also enable the Pact.SPV tests in .github/workflows/applications.yaml
?
src/Chainweb/Cut/Test.hs
Outdated
:: HasCallStack | ||
=> HasChainId cid | ||
=> (BlockHeader -> Bool) | ||
-- ^ POW validator |
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.
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.
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.
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:
- A protocol version. This in itself (protocol version compliance) is not of interest to many subsystems.
- A graph. This is relevant to multi-chain-aware subsystems.
- 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.
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.
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:
- block header validation (intrinsic and indicutive)
- cut validation (braiding structure of chainweb)
- payload validation (construction of payload Merkle tree, tx uniqueness, tx timing validation, gas, etc.)
- 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.
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.
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.
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.
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 |
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.
👍
{ _bdbWebBlockHeaderDb :: WebBlockHeaderDb | ||
, _bdbPayloadDb :: PayloadDb RocksDbCas | ||
, _bdbCut :: MVar Cut | ||
} |
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.
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.
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.
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.
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.
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.
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.
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.
test/Chainweb/Test/Pact/SPV.hs
Outdated
runCut bdb pact = do | ||
forM_ (chainIds v) $ \cid -> do | ||
ph <- getParentTestBlockDb bdb cid | ||
-- print (_blockHeight ph, cid) |
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.
-- print (_blockHeight ph, cid) |
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.
This is a useful debug output so I wanted to keep it. Should I instead make it a Debug
log?
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.
Just ignore the suggestion. I was just not sure if the comment was there intentionally or just forgotten.
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.
on second thought I'll just remove it
test/Chainweb/Test/Pact/SPV.hs
Outdated
c <- readMVar cmv | ||
cutToPayloadOutputs c pdb | ||
|
||
runCut :: TestBlockDb -> WebPactExecutionService -> IO CutOutputs |
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.
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?
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.
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 ()
.
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.
Not sure how useful the outputs are. However, throwing in a void
on the use side isn't a big deal.
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.
Hmm ... the problem with this function is it hardcodes the requirements of addPayloadBlock
above ...
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.
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?
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.
I'll try it out and see.
test/Chainweb/Test/Pact/SPV.hs
Outdated
|
||
c0 <- _cut cutDb | ||
-- cut 0: empty run (not sure why this is needed but test fails without it) | ||
void $ runCut bdb pact |
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.
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.
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.
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"
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.
Not sure, I'll investigate.
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.
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.
No description provided.