-
Notifications
You must be signed in to change notification settings - Fork 217
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
Move important UTxO state transition functions to the top-level and test them #2848
Conversation
9182740
to
dafd813
Compare
- Move the function that modifies the state of a UTxO based on a transaction to the top level so that it can be tested. - Move other useful functions to the top-level for the same reason. - Test important functions in wallet model.
dafd813
to
88c9886
Compare
-- mempty `difference` u = mempty | ||
-- u `difference` u = mempty | ||
difference :: UTxO -> UTxO -> UTxO | ||
difference u1 u2 = |
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.
Suggestion: move this difference
function to the UTxO
module.
Justification: it's a function that operates purely on UTxO
values, and could viewed as analogous to these difference functions:
TokenBundle.difference
TokenMap.difference
Set.difference
Map.difference
- ...
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.
Absolutely 👍
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.
@sevanspowell and I had a discussion about the difference
function.
We believe it might make more sense if this function were to have the following signature:
- difference :: UTxO -> UTxO -> UTxO
+ difference :: UTxO -> Set TxIn -> UTxO
Reasons:
- The current implementation relies on finding the difference between two
TxOut
values in the event that a key appears in both UTxO sets. - But it's not clear what useful answer we can obtain from differencing a pair of
TxOut
values. For example, if the pair ofTxOut
values have different addresses, then which address should we pick, and why? - In all situations that we currently deal with, there should only be one possible
TxOut
for a givenTxIn
. (We shouldn't ever have a state where there are two possibleTxOut
values for a givenTxIn
.) - If we assume that there is only one unique mapping for any given
TxIn
, it makes sense to constrain the type so that we're only subtracting a set ofTxIn
values (so the function does not need to deal with the odd case whereTxOut
values differ) from the key set of ourUTxO
parameter.
However, after making this simplification, we can see there is already a pre-existing function with these semantics:
excluding :: UTxO -> Set TxIn -> UTxO
excluding (UTxO utxo) =
UTxO . Map.withoutKeys utxo
Hence, in all places that currently use UTxO.difference
, it might make more sense to call UTxO.excluding
with just the set of TxIn
values from the second map.
- Remove usage of "knownTxO" when calculating "ourNextUTxO", it's not necessary. - Move definition of knownTxO closer to where it's actually necessary.
- @jonathanknowles pointed out that the difference function on UTxOs is problematic to define over the domain of all UTxOs. Ideally, the mapping between TxIn and TxOut should be unique, and so we should never need to worry about a case where two different TxOuts are associated with the same TxIn, but the existing type can't guarantee this property, and so we need to define the behaviour when a TxIn has two different TxOuts. The behaviour is tricky to define correctly, so we opt to just use the existing "excluding" function instead, which can the same intended function as "difference".
- Move the "filterOurUTxOs" function to the UTxO module and rename the function to "filterByAddressM". - Also create a "filterByAddress" function that is the same as "filterByAddressM" but runs using the identity functor. - Add a test to verify the relationship between the two.
8106875
to
78ffacb
Compare
The updated comments and test names match the generality of the function (which can take any indicator function on addresses).
This makes it reusable from other locations. We also move `prop_addressParity_coverage` to `AddressSpec`.
…apply-tx-testable Suggestions for PR 2848
- Using "applyTxToUTxO" then "filterByAddress" filters the entire UTxO on every application of a Tx. By presuming that we've already filtered the previous UTxO, then only applying parts of the new Tx that we care about, we can get better performance characteristics. - Add new "spendTx" function to support this. It does part of what "applyTxToUTxO" does. - Redefine "applyTxToUTxO" in terms of "spendTx" - Replace call to "applyTxToUTxO" with the equivalent, and more efficient: "spendTx tx u <> filterByAddress (utxoFromTx tx)" - Add property tests to prove equivalences. - Re-jig and clean up existing property tests a bit.
53b9ec4
to
7717363
Compare
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.
Hi Sam
I've already made a few comments. But to add to those:
-
I reckon it would be useful to add some coverage checks to the following properties:
-
prop_applyTxToUTxO_balance
-
prop_applyTxToUTxO_entries
-
prop_applyTxToUTxO_spendTx_utxoFromTx
-
prop_filterByAddress_balance_applyTxToUTxO
-
prop_spendTx_balance_inequality
-
prop_spendTx_balance
-
prop_spendTx_filterByAddress
-
prop_spendTx_isSubset
-
prop_spendTx
-
prop_utxoFromTx_balance
-
prop_utxoFromTx_is_unspent
-
-
There are several properties where we use
forAll
withgenX
andshrinkX
for some typeX
, where we don't modify the generator in any way.I think we could simplify the code a bit by introducing
Arbitrary
instances (withinModelSpec
) forUTxO
,Tx
, and so on. For example: https://github.com/input-output-hk/cardano-wallet/pull/2848/files#r700738862. Though I reckon it's a good idea to do this only after adding coverage checks, in case we need to alter the size parameter for any of the generators.
This allows the definition of a `CoArbitrary` instance for `Address`, which makes it possible for QuickCheck to generate arbitrary functions of type `Address -> t`, provided that type `t` has an `Arbitrary` instance.
Instead of using constant functions, we use arbitrary functions of type `Address -> Bool`.
…sts. Instead of using constant functions, we use arbitrary functions of type `Address -> Bool`.
It's perfectly valid for a transaction to have no outputs if all incoming value is consumed in some other way (either through the fee, or by burning tokens).
By conservatively reducing coverage expectations, this change reduces the overall run time from around 20 seconds to around 1 second.
What we really want here is to capture the notion of "strictly less than", but `leq` gives us "less than OR equal". We can't use `<`, as token maps are only partially ordered. So we have to use a combination of `leq` and `/=`. In response to review comment: https://github.com/input-output-hk/cardano-wallet/pull/2867/files#r701595441
…x-testable Strengthen tests for `applyTx` and `filterByAddress`.
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.
LGTM!
I've run the test suite with --qc-max-success 10000
, and all the tests pass.
bors r+ |
2848: Move important UTxO state transition functions to the top-level and test them r=jonathanknowles a=sevanspowell This work is intended to allow future modifications to the UTxO state transition function (i.e. collateral) to be tested in isolation, at the unit level. My changes are primarily to the nested function "applyTx". I have tried to keep my changes minimal: - Use more verbose variable names in that function to make the purpose of each variable clear. - More clearly delineate between transaction outputs that are unspent and transaction outputs "we know about" but may possibly be spent. - Make some effort to separate the ideas of "knowledge of a UTxO" from "ownership of a UTxO" to make testing easier. - Extract out the core state transition function from the existing "do lots" "applyTx" function and move it to the top-level. - Move other useful functions to the top-level for the same reason. - Test important functions in wallet model spec. - Re-implement hasKnownInput/hasKnownOutput to make their implementations a little more clear and symmetric. ### Comments These changes make testing collateral much easier, we can just test the "applyTxToUTxO" function at the unit level, and adjust existing model tests as necessary. ### Issue Number ADP-1092 Co-authored-by: Samuel Evans-Powell <[email protected]> Co-authored-by: Jonathan Knowles <[email protected]>
Build failed: This one looks like a networking issue: BYRON_TX_LIST_03 - Shelley CLI does not list Byron wallet transactions (220ms)
BYRON_TRANS_DELETE - Cannot delete tx on Byron wallet using shelley CLI (200ms)
BYRON_TRANS_CREATE / BYRON_TRANS_ESTIMATE - Cannot create/estimate tx on Byron wallet using shelley CLI
create (657ms)
UnexpectedFailure (MissingBlockError (EmptySlot (RealPoint (SlotNo 4724) d189e4debd0a5da70dbf6542a05920234532be37462a6ec2887573dd904714fa)))
cardano-node: The ImmutableDB got corrupted, full validation will be enabled for the next startup fees (445ms)
SHELLEY_CLI_WALLETS
retry: expected: Status {statusCode = 201, statusMessage = "Created"}
retry: but got: Status {statusCode = 500, statusMessage = "Internal Server Error"}
retry:
retry: from the following response: Left (HttpException (ConnectionFailure Network.Socket.connect: <socket: 20>: does not exist (Connection refused)))
retry: Retrying failed test.
retry: Test failed again in the same way. |
bors retry |
2848: Move important UTxO state transition functions to the top-level and test them r=jonathanknowles a=sevanspowell This work is intended to allow future modifications to the UTxO state transition function (i.e. collateral) to be tested in isolation, at the unit level. My changes are primarily to the nested function "applyTx". I have tried to keep my changes minimal: - Use more verbose variable names in that function to make the purpose of each variable clear. - More clearly delineate between transaction outputs that are unspent and transaction outputs "we know about" but may possibly be spent. - Make some effort to separate the ideas of "knowledge of a UTxO" from "ownership of a UTxO" to make testing easier. - Extract out the core state transition function from the existing "do lots" "applyTx" function and move it to the top-level. - Move other useful functions to the top-level for the same reason. - Test important functions in wallet model spec. - Re-implement hasKnownInput/hasKnownOutput to make their implementations a little more clear and symmetric. ### Comments These changes make testing collateral much easier, we can just test the "applyTxToUTxO" function at the unit level, and adjust existing model tests as necessary. ### Issue Number ADP-1092 Co-authored-by: Samuel Evans-Powell <[email protected]> Co-authored-by: Jonathan Knowles <[email protected]>
Build failed: Test failure: , tip = ApiBlockReference
{ absoluteSlotNumber = ApiT
( SlotNo 4574 )
, slotId = ApiSlotId
{ epochNumber = ApiT
( EpochNo
{ unEpochNo = 45 }
)
, slotNumber = ApiT
( SlotInEpoch
{ unSlotInEpoch = 74 }
)
}
, time = 2021 - 09 - 03 08 : 33 : 40.8 UTC
, block = ApiBlockInfo
( Quantity 2201 )
}
}
Waited longer than 90s to resolve action: "Wallet balance is as expected".
expected: Quantity 999978
but got: Quantity 0
To rerun use: --match "/CLI Specifications/SHELLEY_CLI_HW_WALLETS/HW_WALLETS_01x - Restoration from account public key preserves funds/"
Randomized with seed 2141484065 I tried this locally, and it passes. So will retry. Looks like: |
bors retry |
Build succeeded: |
This work is intended to allow future modifications to the UTxO state transition function (i.e. collateral) to be tested in isolation, at the unit level.
My changes are primarily to the nested function "applyTx". I have tried to keep my changes minimal:
Comments
These changes make testing collateral much easier, we can just test the "applyTxToUTxO" function at the unit level, and adjust existing model tests as necessary.
Issue Number
ADP-1092