-
Notifications
You must be signed in to change notification settings - Fork 615
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
Changes from 2 commits
7f291ca
068a9dc
a84dab3
98788da
99a73a5
e16dea6
6b26ccc
5ea89d0
5d81d08
867c014
af6d095
c5a494c
719c661
5b701f3
cdbac6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "position": 2, | ||
| "collapsible": true, | ||
| "collapsed": true, | ||
| "label": "Testing" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| --- | ||
| title: Testing | ||
| --- | ||
|
|
||
| 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. | ||
|
|
||
| ## 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 docs](https://Noir-lang.org/docs/tooling/testing/) | ||
|
|
||
| ## TXE | ||
|
|
||
| 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 the modifications to the blockchain state 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, and all that we need is an entity to provide values consistent with real execution. This is where our TXE (Testing eXecution Environment) 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, blockbuilding and other intrincacies of the aztec protocol, it is much faster to run than simulating everything in the sandbox. | ||
|
|
||
| ### Running TXE | ||
|
|
||
| In order to use the TXE, it must be running on a known address. Assuming the default `http://localhost:8080`, contract tests would be run with: | ||
|
|
||
| `nargo test --oracle-resolver http://localhost:8080` | ||
|
|
||
| :::warning | ||
| Since TXE tests are written in Noir and executed with `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. | ||
| Furthermore, 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 contract tests in Noir | ||
|
|
||
| `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** | ||
|
|
||
| #include_code txe_test_increment /noir-projects/noir-contracts/contracts/counter_contract/src/main.nr rust | ||
|
|
||
| #### Deploying contracts | ||
|
|
||
| ```rust | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can these not be hardcoded?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried to provide examples wherever it made sense, but in some places they're just generic pieces of code suggesting possible use cases. Don't really know what's best, too specific might be bad too. |
||
| let deployer = env.deploy("path_to_contract_ts_interface"); | ||
|
|
||
| // 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 = env..with_private_initializer(my_private_initializer_call_interface); | ||
|
|
||
| // or | ||
|
|
||
| let my_public_initializer_call_interface = MyContract::interface().public_constructor(...); | ||
| let my_contract_instance = env.with_public_initializer(my_public_initializer_call_interface); | ||
|
|
||
| // or | ||
|
|
||
| let my_contract_instance = env.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) | ||
|
|
||
| #include_code txe_test_transfer_private /noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr rust | ||
|
|
||
| Unconstrained functions can be directly called from the contract interface: | ||
|
|
||
| #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 switching to the current contract's address in order to be able to read it's siloed state! | ||
| ::: | ||
|
|
||
| Reading public state: | ||
| #include_code txe_test_call_unconstrained /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 | ||
|
|
||
| #### 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 | ||
|
|
||
| ## End-to-end | ||
|
|
||
| If you need the rules of the protocol to be enforce or require more complex interactions (such as with L1 contracts), please refer to [Testing Aztec.nr contracts with TypeScript](../../../guides/js_apps/test.md) | ||
Uh oh!
There was an error while loading. Please reload this page.