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

Difference testing for 'core' protocol using model generated (heuristic) traces #315

Merged
merged 68 commits into from
Sep 6, 2022

Conversation

danwt
Copy link
Contributor

@danwt danwt commented Aug 31, 2022

Overview

Version 3 of a differential testing approach to testing the 'core' aspects of the Interchain Security system.

This PR contains three major components

  1. A 'simibc' testutil directory containing reusable components for testing ibc connected chains in a fine-grained way (here)
  2. A Typescript project containing modelling code and code for generating traces for difference testing (here)
  3. A golang test driver which is called from a unit test, which runs traces read from a json file, and uses them to test the system (here)

All of the included files are necessary for a full understanding, however, some files contain the most information.

  1. Please find a README file which is contained within the Typescript project. This readme file explains the intentions of the work, and gives usage instructions for adapting the model, and generating traces using it.

  2. model.ts contains the majority of the semantic logic used for modelling the system.

  3. main.ts contains the entry point for interacting with the model and generating traces.

  4. properties.ts contains the code used to check some properties of the protocol, most importantly the bond based consumer voting power property.

  5. core_test.go contains the core driver code used to read json traces and execute them against real instances of the system, simulated using ibc-go/testing. The pivotal unit test is here. There is a second unit test here which serves only to check that the driver is written correctly.

I suggest to read the README first, and then decide which to read next based on your interest.

Gathering feedback

All feedback and especially criticism is welcome!

I'm aware of some major issues already but I think now is a sensible time to merge and to leave improvements to future work.

Future work and issue

Code quality

  • diff-tests/core/setup.go is an absolute monster
  • semi-duplicated efforts with simibc and the dependency ibc-go/testing
  • model init state is hardcoded in the typescript and the go code, instead of read into the go code
  • a few semi-duplicated helper methods between diff-tests/core/core_test.go and diff-tests/core/setup.go
  • lack of comments

Test capabilities

  • Testing what happens when assumptions are broken
  • Other, see this text

@danwt danwt requested a review from MSalopek August 31, 2022 12:52
@@ -0,0 +1,12 @@
import { gen } from '../src/main.js';
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the stuff in this folder necessary?

Copy link
Contributor Author

@danwt danwt Sep 1, 2022

Choose a reason for hiding this comment

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

The easiest way I know of to check the statement coverage of the trace generation on the model is to use yarn jest --collect-coverage which requires a jest test.
I don't think we should give this up.

invariantSnapshot: InvariantSnapshot;
}

interface Consequence {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to know more about what a Consequence is. Is it just the complete model state at any given time? Is it the parts of the model state that you care about checking, minus some "hidden" state?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, this is poorly documented. A consequence is a slice of model state relevant to the action that was last done. It's used for state comparison with the SUT and is helpful for understanding traces when reading through the actions.
Previously I had much more state here, but it's too much and make the jsons huge and any problems really hard to debug.

@@ -0,0 +1,24 @@
export default {
Copy link
Contributor

@jtremback jtremback Sep 1, 2022

Choose a reason for hiding this comment

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

Almost 100% certain this isn't needed. Can you remove all boilerplate files that you don't have a specific purpose for from this folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point to remove unnecessary files, but this one is very much needed. Jest will not run without this.

s.Run(fmt.Sprintf("Trace num: %d", i), func() {
// Setup a new pair of chains for each trace
s.SetupTest()
s.traces.CurrentTraceIx = i
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels really awkward. Can't you just iterate the list and pass the current trace to executeTrace?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might be possible and I was just lazy. I'll check again. The thing is I want to keep the trace in 'global' state for diagnostics

}()
// Record information about the trace, for debugging
// diagnostics.
s.executeTrace()
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does the state after the action get checked (the "Consequence")? I see matchState being called in the endAndBeginBlock. Is that it? If it is, it seems odd to have your state being checked by one of your actions.

Copy link
Contributor Author

@danwt danwt Sep 1, 2022

Choose a reason for hiding this comment

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

We only compare committed block state because that's the only thing that matters at the end of the day. Anything that happens inside the block is implementation detail. I.e. as long as the same transactions result in the same committed block state we don't care what the go implementation actually does with those transactions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could check after every action but IMO that would bring unnecessary restrictions and make it harder to make the model abstract and to make the implementation efficient

Copy link
Contributor

@mpoke mpoke left a comment

Choose a reason for hiding this comment

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

Second part of the review (still need to look over properties.ts). In general, the code should be commented better.

diff-tests/core/model/src/model.ts Show resolved Hide resolved
diff-tests/core/model/src/model.ts Show resolved Hide resolved
diff-tests/core/model/src/model.ts Outdated Show resolved Hide resolved
diff-tests/core/model/src/model.ts Show resolved Hide resolved
diff-tests/core/model/src/model.ts Outdated Show resolved Hide resolved
diff-tests/core/model/src/model.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@mpoke mpoke left a comment

Choose a reason for hiding this comment

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

Final part of the review for model.ts and properties.ts. See my comments below.

In general, amazing work @danwt 🚀

diff-tests/core/model/src/properties.ts Show resolved Hide resolved
diff-tests/core/model/src/properties.ts Show resolved Hide resolved
diff-tests/core/model/src/model.ts Show resolved Hide resolved
diff-tests/core/model/src/model.ts Show resolved Hide resolved
Comment on lines 108 to 113
h: Record<Chain, number>;
t: Record<Chain, number>;
tokens: number[];
undelegationQ: Undelegation[];
delegatorTokens: number;
power: (number | undefined)[];
Copy link
Contributor

Choose a reason for hiding this comment

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

Add comments describing the field. Ditto throughout the model.

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, latest model.ts has all fields commented, did you see?

I'm not sure about also commenting these parts. Tbh there probably a much better way to write the typescript than I have done, I need to study the language a bit more.

diff-tests/core/model/src/properties.ts Show resolved Hide resolved
diff-tests/core/model/src/properties.ts Show resolved Hide resolved
diff-tests/core/model/src/properties.ts Show resolved Hide resolved
diff-tests/core/model/src/common.ts Outdated Show resolved Hide resolved
function powerProvider(block: CommittedBlock) {
return _.range(NUM_VALIDATORS).map(
(i) =>
block.invariantSnapshot.tokens[i] +
Copy link
Contributor

Choose a reason for hiding this comment

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

These are all the tokens of validator i regardless of whether it is bonded, unbonding, or unbonded. For the property, only bonded and unbonding validators are relevant (plus the unbonding undelegations that are captured in the next lines).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Useful catch!

/**
* @param block to compute validator voting powers for
* @param hp is the earliest height for unbondings to account for
* @returns burnable voting power for each validator on the Provider chain
*/
function powerProvider(block: CommittedBlock, hp: number): number[] {
return _.range(NUM_VALIDATORS).map((i) => {
let x = 0;
if (block.invariantSnapshot.status[i] !== Status.UNBONDED) {
x += block.invariantSnapshot.tokens[i];
}
x += sum(
block.invariantSnapshot.undelegationQ
.filter((e) => e.val === i)
.filter((e) => hp <= e.creationHeight)
.map((e) => e.initialBalance),
);
return x;
});
}

@shaspitz
Copy link
Contributor

shaspitz commented Sep 2, 2022

Could we also add the new files to tests/differential instead of diff-test to be compatible with #325

Copy link
Contributor

@mpoke mpoke left a comment

Choose a reason for hiding this comment

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

Nice work @danwt.

@danwt danwt merged commit 7e639da into main Sep 6, 2022
@danwt danwt deleted the danwt/difftest-v3-reb0 branch September 6, 2022 10:46
danwt pushed a commit that referenced this pull request Sep 6, 2022
commit 7e639da
Author: Daniel T <[email protected]>
Date:   Tue Sep 6 11:46:08 2022 +0100

    Difference testing for 'core' protocol using model generated (heuristic) traces (#315)

    * blanket add difftest

    * (assum + trace pass) upstream sync setup

    * (full pass) shuffle directories

    * (all pass) minor tidyup simibc interface

    * (all pass) removes extraneous updateClients, deliverAcks

    * (pass) del extra UpdateClient in framework deliverAcks

    * (pass) tweak docstrings

    * (pass) remove chains map from framework

    * (pass) rename, doc simibc

    * (regression) tweaks

    * (pass) tweaks

    * (all pass) bump max gass

    * Track traces

    * Bumps ci configs

    * Try fix gosec

    * Try fix sonar

    * Try fix docker go build for 1.18

    * README

    * Move diff artifacts to one dir

    * Update README

    * Update git LFS

    * Update sonar properties

    * Update README

    * Rename NetworkLink -> OrderedLink

    * Better docs main.ts

    * Docstring matchState

    * Removes some hardcoded nums in core_test.go

    * Adds couple of comments to core_test.go

    * Docstring for Consequence (.ts)

    * Update core package.json

    * Ensure typechecks enabled

    * Prettier on model.ts

    * DEL .editorconfig for ts model

    * del eslint

    * Bump sonar

    * Minor tidyup Traces struct go

    * Del unused field in properties.ts

    * new semantics: model.ts::expired -> willBeProcessedByStakingModule

    * Better comment DeliverPackets

    * Better comments for model class fields

    * Improve model staking class docstrings

    * Improve model staking class docstrings

    * Fix more_one_third_val_power_change event

    * Update testutil/simibc/ordered_link.go comment

    Co-authored-by: Shawn Marshall-Spitzbart <[email protected]>

    * Adds event.receive_slash_request_unbonded

    * Adds event.receive_slash_request_unbonded

    * Better docstring BlockHistory

    * Adds 4 new events to constants

    * Model endblock method split

    * Couple more comments model.ts

    * Comment model.ts

    * New traces

    * DEL traces

    * CP (debug)

    * (debug) offset adjustment

    * tentative, fix regression (debug)

    * kill prints

    * (tentative) property fix

    * Fixes BBCVP power provider

    * New traces

    * Del 2x prints

    * Comment property perf

    * rn power -> consumerPower in model

    * rn power -> consumerPower in driver

    * Removes outdated comment

    Co-authored-by: Daniel <[email protected]>
    Co-authored-by: Jehan <[email protected]>
    Co-authored-by: Shawn Marshall-Spitzbart <[email protected]>
danwt added a commit that referenced this pull request Sep 6, 2022
* move to dir

* other stuff

* revert one change

* Update Dockerfile

* Squashed commit , with resolutions, of the following:

commit 7e639da
Author: Daniel T <[email protected]>
Date:   Tue Sep 6 11:46:08 2022 +0100

    Difference testing for 'core' protocol using model generated (heuristic) traces (#315)

    * blanket add difftest

    * (assum + trace pass) upstream sync setup

    * (full pass) shuffle directories

    * (all pass) minor tidyup simibc interface

    * (all pass) removes extraneous updateClients, deliverAcks

    * (pass) del extra UpdateClient in framework deliverAcks

    * (pass) tweak docstrings

    * (pass) remove chains map from framework

    * (pass) rename, doc simibc

    * (regression) tweaks

    * (pass) tweaks

    * (all pass) bump max gass

    * Track traces

    * Bumps ci configs

    * Try fix gosec

    * Try fix sonar

    * Try fix docker go build for 1.18

    * README

    * Move diff artifacts to one dir

    * Update README

    * Update git LFS

    * Update sonar properties

    * Update README

    * Rename NetworkLink -> OrderedLink

    * Better docs main.ts

    * Docstring matchState

    * Removes some hardcoded nums in core_test.go

    * Adds couple of comments to core_test.go

    * Docstring for Consequence (.ts)

    * Update core package.json

    * Ensure typechecks enabled

    * Prettier on model.ts

    * DEL .editorconfig for ts model

    * del eslint

    * Bump sonar

    * Minor tidyup Traces struct go

    * Del unused field in properties.ts

    * new semantics: model.ts::expired -> willBeProcessedByStakingModule

    * Better comment DeliverPackets

    * Better comments for model class fields

    * Improve model staking class docstrings

    * Improve model staking class docstrings

    * Fix more_one_third_val_power_change event

    * Update testutil/simibc/ordered_link.go comment

    Co-authored-by: Shawn Marshall-Spitzbart <[email protected]>

    * Adds event.receive_slash_request_unbonded

    * Adds event.receive_slash_request_unbonded

    * Better docstring BlockHistory

    * Adds 4 new events to constants

    * Model endblock method split

    * Couple more comments model.ts

    * Comment model.ts

    * New traces

    * DEL traces

    * CP (debug)

    * (debug) offset adjustment

    * tentative, fix regression (debug)

    * kill prints

    * (tentative) property fix

    * Fixes BBCVP power provider

    * New traces

    * Del 2x prints

    * Comment property perf

    * rn power -> consumerPower in model

    * rn power -> consumerPower in driver

    * Removes outdated comment

    Co-authored-by: Daniel <[email protected]>
    Co-authored-by: Jehan <[email protected]>
    Co-authored-by: Shawn Marshall-Spitzbart <[email protected]>

* DEL extra files

Co-authored-by: Daniel <[email protected]>
Co-authored-by: Daniel T <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants