-
Notifications
You must be signed in to change notification settings - Fork 598
feat: TXE docs and usability #7305
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
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7f291ca
wip
Thunkar 068a9dc
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar a84dab3
fix build
Thunkar 98788da
Merge branch 'master' of github.com:AztecProtocol/aztec-packages into…
Thunkar 99a73a5
moved things around, clarified and completed some stuff
Thunkar e16dea6
Merge branch 'master' into gj/txe_docs
Thunkar 6b26ccc
more clarifications
Thunkar 5ea89d0
Merge branch 'gj/txe_docs' of github.com:AztecProtocol/aztec-packages…
Thunkar 5d81d08
removed duplicated annotation
Thunkar 867c014
increased txe timeout in aztec-nr tests
Thunkar af6d095
add details
critesjosh c5a494c
address comments, change headings, edits
critesjosh 719c661
Merge branch 'master' into gj/txe_docs
critesjosh 5b701f3
apply rahuls suggestions
critesjosh cdbac6e
fixes
rahul-kothari File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
6 changes: 6 additions & 0 deletions
6
docs/docs/guides/smart_contracts/testing_contracts/_category_.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "position": 1, | ||
| "collapsible": true, | ||
| "collapsed": true, | ||
| "label": "Testing Contracts" | ||
| } |
190 changes: 190 additions & 0 deletions
190
docs/docs/guides/smart_contracts/testing_contracts/index.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| --- | ||
| title: Testing Contracts | ||
| --- | ||
|
|
||
| Aztec contracts can be tested in a variety of ways depending on the needs of a particular application and the complexity of the interactions they must support. | ||
|
|
||
| To test individual contract functions, you can use the Testing eXecution Environment (TXE) described below. For more complex interactions that require checking that the protocol rules are enforced, you should [write end-to-end tests using TypeScript](../../js_apps/test.md). | ||
|
|
||
| ## Pure Noir tests | ||
|
|
||
| Noir supports the `#[test]` annotation which can be used to write simple logic tests on isolated utility functions. These tests only make assertions on algorithms and cannot interact with protocol-specific constructs such as `storage` or `context`, but are extremely fast and can be useful in certain scenarios. | ||
|
|
||
| #include_code pure_noir_testing /noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr rust | ||
|
|
||
| To learn more about Noir testing, please refer to [the Noir docs](https://Noir-lang.org/docs/tooling/testing/). | ||
|
|
||
| ## TXE (pronounced "trixie") | ||
|
|
||
| In order to interact with the protocol, Aztec contracts leverage the power of oracles: functions that reach out to the outside world and are able to query and manipulate data outside of itself. The values returned by oracles are then constrained inside Noir and modifications to the blockchain state are later verified to adhere to the protocol rules by our kernel circuits. | ||
|
|
||
| However, all of this is often not necessary to ensure the contract logic itself is sound. All that we need is an entity to provide values consistent with real execution. This is where our TXE (Testing eXecution Environment, pronounced "trixie") comes in! | ||
|
|
||
| TXE is a JSON RPC server much like PXE, but provides an extra set of oracle functions called `cheatcodes` that allow developers to manipulate the state of the chain and simulate contract execution. Since TXE skips most of the checks, block building and other intricacies of the Aztec protocol, it is much faster to run than simulating everything in the sandbox. | ||
|
|
||
| ## TXE vs End-to-end tests | ||
|
|
||
| End-to-end tests are written in typescripts and use compiled Aztec contracts and generated Typescript interfaces, a private execution environment (PXE) and a simulated execution environment to process transactions, create blocks and apply state updates. This allows for advanced checks on state updates like generation the of logs, cross-chain messages and checking transaction status and also enforce the rules of the protocol (e.g. checks in our rollup circuits). If you need the rules of the protocol to be enforced or require complex interactions (such as with L1 contracts), please refer to [Testing Aztec.nr contracts with Typescript](../../js_apps/test.md). | ||
|
|
||
| The TXE is a super fast framework in Noir to quickly test your smart contract code. | ||
|
|
||
| So to summarize: | ||
| * End-to-end tests are written in Typescript. TXE in Noir. | ||
| * End-to-end tests are most similar to using mocha + ethers.js to test Solidity Contracts. TXE is like foundry (fast tests in solidity) | ||
|
|
||
| ### Running TXE | ||
|
|
||
| In order to use the TXE, it must be running on a known address. By default, TXE runs at `http://localhost:8080`. So you can run contracts tests with: | ||
|
|
||
| `aztec-nargo test --oracle-resolver http://localhost:8080` | ||
|
|
||
| :::warning | ||
| Since TXE tests are written in Noir and executed with `aztec-nargo`, they all run in parallel. This also means every test creates their own isolated environment, so state modifications are local to each one of them. | ||
|
|
||
| Executing many tests in parallel might slow processing of the RPC calls down to the point of making them timeout. To control this timeout the `NARGO_FOREIGN_CALL_TIMEOUT` env variable is used. | ||
| ::: | ||
|
|
||
| ### Writing TXE tests | ||
|
|
||
| `aztec-nr` provides an utility class called `TestEnvironment`, that should take care of the most common operations needed to setup contract testing. Setting up a new test environment with `TestEnvironment::new()` **will reset the current test's TXE state**. | ||
|
|
||
| :::tip | ||
| You can find all of the methods available in the `TestEnvironment` [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr). | ||
| ::: | ||
|
|
||
| #include_code txe_test_increment /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust | ||
|
|
||
| :::warning | ||
| Tests run significantly faster as `unconstrained` functions. This means we generate bytecode (Brillig) and not circuits (ACIR), which _should_ yield exactly the same results. Any other behavior is considered a bug. | ||
| ::: | ||
|
|
||
| ### Imports | ||
|
|
||
| Writing tests in contracts requires importing additional modules from Aztec.nr. Here are the modules that are needed for testing the increment function in the counter contract. | ||
|
|
||
| #include_code test_imports /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust | ||
|
|
||
| ### Deploying contracts | ||
|
|
||
| ```rust | ||
| let deployer = env.deploy("path_to_contract_ts_interface"); | ||
critesjosh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Now one of these can be called, depending on the contract and their possible initialization options. | ||
| // Remember a contract can only be initialized once. | ||
|
|
||
| let my_private_initializer_call_interface = MyContract::interface().private_constructor(...); | ||
| let my_contract_instance = deployer.with_private_initializer(my_private_initializer_call_interface); | ||
|
|
||
| // or | ||
|
|
||
| let my_public_initializer_call_interface = MyContract::interface().public_constructor(...); | ||
| let my_contract_instance = deployer.with_public_initializer(my_public_initializer_call_interface); | ||
|
|
||
| // or | ||
|
|
||
| let my_contract_instance = deployer.without_initializer(); | ||
| ``` | ||
|
|
||
| :::warning | ||
| At the moment, TXE uses the generated contract TypeScript interfaces to perform deployments, and they must be provided as either an absolute path, a relative path to TXE's location or a module in an npm direct dependency such as `@aztec/noir-contracts.js`. It is not always necessary to deploy a contract in order to test it, but sometimes it's inevitable (when testing functions that depend on the contract being initialized, or contracts that call others for example) **It is important to keep them up to date**, as TXE cannot recompile them on changes. This will be improved in the future. | ||
| ::: | ||
|
|
||
| ### Calling functions | ||
|
|
||
| Our test environment is capable of utilizing the autogenerated contract interfaces to abstract calls, but without going through the usual external call flow (meaning much faster execution). | ||
|
|
||
| #### Private | ||
|
|
||
| For example, to call the private `transfer` function on the token contract: | ||
|
|
||
| #include_code txe_test_transfer_private /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr rust | ||
|
|
||
| #### Public | ||
|
|
||
| To call the public `transfer_public` function: | ||
|
|
||
| #include_code call_public /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr rust | ||
|
|
||
| #### Unconstrained | ||
|
|
||
| Unconstrained functions can be directly called from the contract interface. Notice that we need to set the contract address to the specific token contract that we are calling before making the call. This is to ensure that `view_notes` works properly. | ||
|
|
||
| #include_code txe_test_call_unconstrained /noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr rust | ||
|
|
||
| ### Creating accounts | ||
|
|
||
| The test environment provides two different ways of creating accounts, depending on the testing needs. For most cases, it is only necessary to obtain a valid `AztecAddress` that represents the user's account contract. For this, is is enough to do: | ||
|
|
||
| ```rust | ||
| let mocked_account_address = env.create_account(); | ||
| ``` | ||
|
|
||
| These accounts also create the necessary keys to ensure notes can be created/nullified, etc. | ||
|
|
||
| For more advanced flows, such as authwits, it is necessary to create a real `AccountContract`, with valid signing keys that gets actually deployed to TXE. For that you can use: | ||
|
|
||
| ```rust | ||
| let real_account_address = env.create_account_contract(secret); | ||
| ``` | ||
|
|
||
| Besides deploying a complete `SchnorrAccountContract`, key derivation is performed so that authwits can be signed. It is slightly slower than the mocked version. | ||
|
|
||
| Once accounts have been created, you can impersonate them in your test by calling: | ||
|
|
||
| ```rust | ||
| env.impersonate(account_address); | ||
| ``` | ||
|
|
||
| ### Checking state | ||
|
|
||
| It is possible to use the regular oracles in tests in order to retrieve public and private state and make assertions about them. | ||
|
|
||
| :::warning | ||
| Remember to switch to the current contract's address in order to be able to read it's siloed state! | ||
| ::: | ||
|
|
||
| Reading public state: | ||
| #include_code txe_test_read_public /noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr rust | ||
|
|
||
| Reading notes: | ||
| #include_code txe_test_read_notes /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust | ||
|
|
||
| ### Authwits | ||
|
|
||
| #### Private | ||
|
|
||
| You can add [authwits](../writing_contracts/authwit.md) to the TXE. Here is an example of testing a private token transfer using authwits: | ||
|
|
||
| #include_code private_authwit /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr rust | ||
|
|
||
| #### Public | ||
|
|
||
| #include_code public_authwit /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr rust | ||
|
|
||
| ### Storing notes in cache | ||
|
|
||
| Sometimes we have to tell TXE about notes that are not generated by ourselves, but someone else. This allows us to check if we are able to decrypt them: | ||
|
|
||
| #include_code txe_test_store_note /noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr rust | ||
|
|
||
| ### Time traveling | ||
|
|
||
| TXE can force the generation of "new blocks" very quickly using: | ||
|
|
||
| ```rust | ||
| env.advance_block_by(n_blocks); | ||
| ``` | ||
|
|
||
| This will effectively consolidate state transitions into TXE's internal trees, allowing things such as reading "historical state" from private, generating inclusion proofs, etc. | ||
|
|
||
| ### Failing cases | ||
|
|
||
| You can test functions that you expect to fail generically, with the `#[test(should_fail)]` annotation, or that it should fail with a specific message with `#[test(should_fail_with = "Failure message")]`. | ||
|
|
||
| For example: | ||
|
|
||
| #include_code fail_with_message /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr rust | ||
|
|
||
| You can also use the `assert_public_call_fails` or `assert_private_call_fails` methods on the `TestEnvironment` to check that a call fails. | ||
|
|
||
| #include_code assert_public_fail /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr rust | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.