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

Generator of Tendermint types for unit, integration, and model-based testing #393

Closed
andrey-kuprianov opened this issue Jun 29, 2020 · 1 comment
Assignees
Labels
light-client Issues/features which involve the light client tests verification

Comments

@andrey-kuprianov
Copy link
Contributor

andrey-kuprianov commented Jun 29, 2020

As it is documented already in many places, we need to develop a simple generator for complex tendermint-rs types.

Such a generator can be used for multiple purposes:

  • generate complex types for unit tests (#381, comment on #330, #383)
  • generate input for integration tests (#331, 290)
  • in model-based testing (MBT), transform an abstract counterexample from a model checker (e.g. Apalache) into a concrete integration test
  • may be for isolation purposes, like to isolate LIghtClient types from complete Tendermint types (#387)

The generator should have the following characteristics for the above applications:

  • the generated object graphs should have correct relationships between objects (e.g., a header and a commit for it)
  • have a programmer-friendly Rust API allowing to generate types with minimal input. The API should allow both to use defaults for many parameters, as well as to set them explicitely.
  • for MBT, it should provide a CLI, allowing to access it, obeying the above principles from other languages / frameworks.

Here I describe the proposed solution, which is currently being implemented in tendermint-testgen

Rust API

Each tendermint type that need to be generated receives a companion type, which lists all possible options for generating this type.
Here is an example Validator struct, a companion for tendermint::validator::Info:

pub struct Validator {
  id: Option<String>,
  voting_power: Option<u64>,
  proposer_priority: Option<i64>,
}

Such validator companion can be generated then using the builder pattern as follows:

let val = Validator::new("a").voting_power(10).proposer_priority(100);

Only the parameters of the new() constructor are necessary, everything else is optional and can be omitted. In the case of a validator, it can be generated from a simple string identifier
Similarly, for a header the only required parameter is a set of validators (e.g. time then defaults to now()), and for a commit only requires a header.

Thus, the following code could then generate a header and commit companions from the two validators a and b:

let validators = vec![Validator::new("a"), Validator::new("b")];
let header = Header::new(&validators).height(100);
let commit = Commit::new(&header).round(10);

And the real Tendermint objects can be then produced from the companions as follows:

let block_header = header.produce().unwrap();
let block_commit = commit.produce().unwrap();

Command-line interface

For MBT we need to be able to generate Tendermint objects from outside of Rust. We assume that the following prerequisites are met:

  • The user has a favorite model-checker (e.g. Apalache), which is able to produce counterexamples in JSON (Apalache can of course do it).
  • The user has a way to execute an integration test, written in JSON, and including all necessary details, against his implementation (which e.g. LightClient can do).
  • There is a tool that is capable to translate between related but different JSON formats (e.g., our JSON Artifact Translator).

The workflow then, from a birds eye view is such:

  1. A user creates a TLA+ spec, that is an abstraction of the implementation he wants to test
  2. The user describes an invariant, which, when violated, would produce an interesting behavior as a counterexample
  3. The user runs, manually, or as part of automated test generation, his favorite model checker, and gets a counterexample.
  4. As the counterexample is abstract, i.e. some important implementation-level details are not present. Thus, it needs to be translated from the abstract to the concrete form. Such a translation can be described with a simple declarative spec, like this one

The only part, which is missing in the above transformation step, is the translation of the abstract counterexample types into the concrete implementation types.
This is also under implementation in tendermint-testgen, a CLI utility which is based on the same companion types as described above.

The CLI parameters can be supplied in two ways:

  • via STDIN: in that case they are expected to be a valid JSON object,
    with each parameter being a field of this object. Accepting input from STDIN allows to integrate the tool into transformation chains with other tools
  • via command line arguments to the specific command. This allows either to use the utility as a standalone tool, or to customize the output produced from STDIN otherwise.

If a parameter is supplied both via STDIN and CLI, the latter is given preference.

In case a particular object can be produced from a single parameter
(like validator), there is a shortcut that allows to provide this parameter
directly via STDIN, without wrapping it into JSON object.
E.g., in the validator case, the following are equivalent:

tendermint-testgen validator --id a --voting-power 3
echo -n '{"id": "a", "voting_power": 3}' | tendermint-testgen --stdin validator
echo -n a | tendermint-testgen --stdin validator --voting-power 3
echo -n '{"id": "a"}' | tendermint-testgen --stdin validator --voting-power 3
echo -n '{"id": "a", "voting_power": 100}' | tendermint-testgen --stdin validator --voting-power 3

The result is a JSON object printed out on STDOUT; the result of the any above commands is:

{
  "address": "730D3D6B2E9F4F0F23879458F2D02E0004F0F241",
  "pub_key": {
    "type": "tendermint/PubKeyEd25519",
    "value": "YnT69eNDaRaNU7teDTcyBedSD0B/Ziqx+sejm0wQba0="
  },
  "voting_power": "3",
  "proposer_priority": null
}    
@andrey-kuprianov andrey-kuprianov added light-client Issues/features which involve the light client verification tests labels Jun 29, 2020
@andrey-kuprianov andrey-kuprianov self-assigned this Jun 29, 2020
andrey-kuprianov added a commit that referenced this issue Jul 17, 2020
andrey-kuprianov added a commit that referenced this issue Jul 20, 2020
andrey-kuprianov added a commit that referenced this issue Jul 20, 2020
andrey-kuprianov added a commit that referenced this issue Jul 21, 2020
andrey-kuprianov added a commit that referenced this issue Jul 21, 2020
andrey-kuprianov added a commit that referenced this issue Jul 21, 2020
andrey-kuprianov added a commit that referenced this issue Jul 21, 2020
andrey-kuprianov added a commit that referenced this issue Jul 22, 2020
brapse pushed a commit that referenced this issue Jul 29, 2020
…testing (#468)

* mbt/tendermint-produce validator

* add constructor for ProposerPriority

* mbt-utils/produce header (not complete yet)

* better error handling

* add default_consensus_params(); not clear how to correctly hash it

* mbt-utils-produce: started on commit

* read input using generic function

* mbt-utils: rework produce_validator to accept input id/input JSON/CLI options

* use serde deserializers for option fields

* mbt-utils/produce-header: allow to set next_vals/time via input/cli

* mbt-utils/produce-header: remove debug output

* mbt-utils: refactor produce validator

* refactor produce_validator to allow calling it from within Rust code,
to easily produce Info struct
* add --ignore-stdin option to allow skipping parsing from STDIN

* mbt-utils: refactor produce header

* mbt-utils/produce-header: allow to specify header height

* mbt-utils/produce-commit: refactor + add height

* started refactoring using traits; added --usage

* better help

* mbt-utils/move produce header into Producer trait

* mbt-utils/move produce commit into Producer trait

* minor improvements

* mbt-utils: produce commit from header

* mbt-utils: add generator functions for Validator,Header,Commit

* mbt-utils: simplify Producer interface

* mbt-utils: make stdin parsing failable

* mbt-utils: pull up signer from Validator::produce()

* mbt-utils: started on producing real signatures for commits

* mbt-utils: small simplification

* mbt-utils: added preliminary support for signatures

* Add missing pub modifiers in a few places

* Remove unused imports

* Fix clippy warnings

* Refactor into a library and a binary

* Move tendermint-produce command into bin/ directory

* Remove newline at end of files

* mbt-utils: start refactoring, FromStr for Validator, Commit, Header

* mbt-utils: move encode_with_stdin out of Producer trait

* mbt-utils: remove parse_stdin() from Producer/Validator/Commit/Header

* mbt:utils: get rid of unwraps for better error handling

* #393: refactor mbt-tendermint-produce into tendermint-typegen

* #393: change in usage mbt-tendermint-produce into tendermint-typegen

* #393: switch to gen_setter macros for setters

* #393: start on vote

* #393: tendermint-typegen -> tendermint-testgen

* #393: more of vote + shorten code

* #393: shorten imports

* #393: add generation of votes; refactor commit

* #393: commit: use getters

* #393: finish code restructuring

* #393: validator unit test

* #393: header unit test

* #393: rename mbt-utils -> tendermint-testgen

* #393: more tests for validator and header

* #393: unit test for vote

* #393: unit test for commit; factor out sign/verify helpers

* #393: fix clippy warnings

* #393: account for suggestions from @shonfeder

* #393: cargo fmt

* Apply suggestions from @romac review

Co-authored-by: Romain Ruetschi <romain@informal.systems>

* #393: apply suggestions from @romac review + necessary changes

* #393: apply more suggestions from @romac review

* #393: add version for tendermint dep, as per @liamsi suggestion

* #393: fix clippy warning as suggested by @romac

* After a bit of afterthought and as a result of @Shivani912 comment,
decided to make `round` an explicit parameter when constructing the
commit.

The reason for this is that we want to allow generating votes only from
the header, but without explicitely given round, the generated votes
will point to the default 1. Setting the `round` afterwards in the Commit
struct will have no effect on those votes.

That's why `round` is now an explicit parameter, and there are two constructors:
`new()`, and `new_with_votes()`.

* #393: move tendermint-testgen -> testgen as suggested by @romac

Co-authored-by: Romain Ruetschi <romain@informal.systems>
@andrey-kuprianov
Copy link
Contributor Author

merged by 877c586

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
light-client Issues/features which involve the light client tests verification
Projects
None yet
Development

No branches or pull requests

1 participant