From 369b78d66641b30e3e40bef1f146bb244b111852 Mon Sep 17 00:00:00 2001 From: Trident bot Date: Fri, 16 Aug 2024 09:28:51 +0000 Subject: [PATCH] Deployed 41066bd to dev with MkDocs 1.6.0 and mike 2.1.3 --- dev/CHANGELOG/index.html | 2 +- .../fuzzing-current-limitations/index.html | 2 +- dev/fuzzing/fuzzing-examples/index.html | 2 +- dev/fuzzing/fuzzing-introduction/index.html | 2 +- dev/fuzzing/fuzzing-lifecycle/index.html | 2 +- dev/fuzzing/fuzzing-run-debug/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p0/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p1/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p2/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p3/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p4/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p5/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p6/index.html | 2 +- dev/fuzzing/howto/fuzzing-howto-p7/index.html | 2 +- .../fuzzing-initialization-all/index.html | 2 +- .../index.html | 2 +- .../fuzzing-initialization-fuzz/index.html | 2 +- dev/home/home-installation/index.html | 2 +- dev/index.html | 2 +- .../howto/poc-howto-p0/index.html | 2 +- .../howto/poc-howto-p1/index.html | 2 +- .../poc-initialization-all/index.html | 4 +- .../poc-initialization-poc/index.html | 2 +- .../index.html | 2 +- dev/integration-tests/poc-examples/index.html | 2 +- .../poc-introduction/index.html | 2 +- dev/integration-tests/poc-run/index.html | 2 +- dev/search/search_index.json | 2 +- dev/sitemap.xml | 54 +++++++++--------- dev/sitemap.xml.gz | Bin 440 -> 440 bytes 30 files changed, 56 insertions(+), 56 deletions(-) diff --git a/dev/CHANGELOG/index.html b/dev/CHANGELOG/index.html index 75d54ab6..a8130ab2 100644 --- a/dev/CHANGELOG/index.html +++ b/dev/CHANGELOG/index.html @@ -1 +1 @@ - Changelog - Trident
Skip to content

Changelog#

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (SemVer).

Note: Version 0 of Semantic Versioning is handled differently from version 1 and above. The minor version will be incremented upon a breaking change and the patch version will be incremented for features.

[dev] - Unreleased#

[0.7.0] - 2024-08-14#

Added#

  • impr/ add feature flag to the AccountsSnapshots macro (183)
  • feat/ add Support for CPI (182)
  • feat/ add option to initialize Trident with Macro/File (for Snapshots) option based on preference (179)
  • feat/create AccountsSnapshots derive macro for Snapshots creation (#177)
  • feat/fuzzing moved to separate crate trident-fuzz (#175)
  • feat/unify dependencies provided by the Trident (#172)
  • feat/fuzzer-stats-logging, an optional statistics output for fuzzing session (#144)

Fixed#

  • fix/in case of fuzzing failure throw error instead of only printing message (#167)
  • fix/snapshot's zeroed account as optional (#170)

Removed#

  • del/remove localnet subcommand (178)
  • del/remove unnecessary fuzzing feature as trident is mainly fuzzer (#176)
  • del/remove Trident explorer (#171)

[0.6.0] - 2024-05-20#

Added#

  • feat/anchor 0.30.0 support (#148)
  • fix/allow to process duplicate transactions (#147)
  • feat/possibility to implement custom transaction error handling (#145)
  • feat/support of automatically obtaining fully qualified paths of Data Accounts Custom types for accounts_snapshots.rs (#141)
  • feat/allow direct accounts manipulation and storage (#142)
  • feat/support of non-corresponding instruction and context names (#130)
  • feat/refactored and improved program flow during init and build, added activity indicator (#129)
  • feat/allow solana versions up to v1.17.* and pin Rust 1.77 nightly compiler (#128)
  • feat/new init command option to initialize fuzz or poc tests only (#124)
  • feat/debug-mode detailed output (#125)
  • feat/anchor 0.29.0 support (#121)
  • doc/add help comment + update documentation (#120)
  • feat/fuzzer error handling (#118)
  • feat/convert fuzz Pubkey to AccountId (#116)
  • feat/additional anchor types (#115)
  • feat/import ToAccountInfo trait in fuzzing prelude (#113)
  • test/added code generation and macros tests (#112)
  • feat/fuzzer framework core, macros, helpers, templates, and examples. (#111)
  • feat/improved trident-tests folder structure for PoC and Fuzz Tests (#109)
  • feat/support for additional fuzzer parameters in Trident.toml config file (#107)
  • feat/posibility to pass params to the fuzzer via Trident.toml config file (#106)
  • feat/client now reads by default keypair from default location (#105)
  • feat/added new --exit-code option to return corresponding exit code based on fuzz test result (#104)
  • feat/removed/updated deprecated functions, removed allow deprecated macros (#103)
  • feat/added new function to read keypair file generated by Anchor (#102)
  • feat/clean command (#101)
  • feat/improved program_client generated code (#100)
  • feat/automatically add hfuzz_target to .gitignore file (#99)
  • feat/support for dynamic templates. (#98)

Fixed#

  • fix/refactored fuzz test executor error handling (#127)
  • fix/warn user on composite accounts and continue fuzz test generation (#133)
  • fix/progress bar loop lock release (#132)
  • fix/fixed fuzz test generation with init-if-needed Anchor feature (#131)
  • fix/program client custom types (#117)
  • fix/check fuzz test name collision by checking the name against HashSet (#114)

[0.5.0] - 2023-08-28#

Added#

  • cli: Added trident subcommand fuzz to run and debug fuzz tests using honggfuzz-rs.
  • cli: Added trident --skip-fuzzer option for init subcommand to skip generation of fuzz test templates.
  • client: Added new Cargo feature fuzzing that enables optional dependencies related to fuzz testing.

[0.4.1] - 2023-08-21#

Changed#

  • Upgrade Solana (=1.16.6) and Anchor framework (=0.28.0) versions.

Fixed#

  • Implemented Anchor Client logic was not able to work with newer version of Anchor. Fixed with async_rpc and async feature.
  • Trident init IDL Parse Error on newer version of Rust, fixed with updated accounts token.

[0.3.0] - 2022-09-23#

Changed#

  • Upgrade Solana (~1.10) and Anchor framework (~0.25) versions

Added#

  • Custom Solana RPC error reporter. If the Solana RPC error is thrown, the error code, message and data (logs) are reported to the output.
  • Custom imports in the .program_client. User is able to import custom types and structures into program client. The import part of the code would not be re-generated.

[0.2.0] - 2022-07-27#

Added#

  • Trident is now configurable. This requires Trident.toml file to exist in the project's root directory - without this file the execution will fail. To solve this re-run trident init or just create an empty Trident.toml file in the project's root directory.
\ No newline at end of file + Changelog - Trident
Skip to content

Changelog#

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (SemVer).

Note: Version 0 of Semantic Versioning is handled differently from version 1 and above. The minor version will be incremented upon a breaking change and the patch version will be incremented for features.

[dev] - Unreleased#

[0.7.0] - 2024-08-14#

Added#

  • impr/ add feature flag to the AccountsSnapshots macro (183)
  • feat/ add Support for CPI (182)
  • feat/ add option to initialize Trident with Macro/File (for Snapshots) option based on preference (179)
  • feat/create AccountsSnapshots derive macro for Snapshots creation (#177)
  • feat/fuzzing moved to separate crate trident-fuzz (#175)
  • feat/unify dependencies provided by the Trident (#172)
  • feat/fuzzer-stats-logging, an optional statistics output for fuzzing session (#144)

Fixed#

  • fix/in case of fuzzing failure throw error instead of only printing message (#167)
  • fix/snapshot's zeroed account as optional (#170)

Removed#

  • del/remove localnet subcommand (178)
  • del/remove unnecessary fuzzing feature as trident is mainly fuzzer (#176)
  • del/remove Trident explorer (#171)

[0.6.0] - 2024-05-20#

Added#

  • feat/anchor 0.30.0 support (#148)
  • fix/allow to process duplicate transactions (#147)
  • feat/possibility to implement custom transaction error handling (#145)
  • feat/support of automatically obtaining fully qualified paths of Data Accounts Custom types for accounts_snapshots.rs (#141)
  • feat/allow direct accounts manipulation and storage (#142)
  • feat/support of non-corresponding instruction and context names (#130)
  • feat/refactored and improved program flow during init and build, added activity indicator (#129)
  • feat/allow solana versions up to v1.17.* and pin Rust 1.77 nightly compiler (#128)
  • feat/new init command option to initialize fuzz or poc tests only (#124)
  • feat/debug-mode detailed output (#125)
  • feat/anchor 0.29.0 support (#121)
  • doc/add help comment + update documentation (#120)
  • feat/fuzzer error handling (#118)
  • feat/convert fuzz Pubkey to AccountId (#116)
  • feat/additional anchor types (#115)
  • feat/import ToAccountInfo trait in fuzzing prelude (#113)
  • test/added code generation and macros tests (#112)
  • feat/fuzzer framework core, macros, helpers, templates, and examples. (#111)
  • feat/improved trident-tests folder structure for PoC and Fuzz Tests (#109)
  • feat/support for additional fuzzer parameters in Trident.toml config file (#107)
  • feat/posibility to pass params to the fuzzer via Trident.toml config file (#106)
  • feat/client now reads by default keypair from default location (#105)
  • feat/added new --exit-code option to return corresponding exit code based on fuzz test result (#104)
  • feat/removed/updated deprecated functions, removed allow deprecated macros (#103)
  • feat/added new function to read keypair file generated by Anchor (#102)
  • feat/clean command (#101)
  • feat/improved program_client generated code (#100)
  • feat/automatically add hfuzz_target to .gitignore file (#99)
  • feat/support for dynamic templates. (#98)

Fixed#

  • fix/refactored fuzz test executor error handling (#127)
  • fix/warn user on composite accounts and continue fuzz test generation (#133)
  • fix/progress bar loop lock release (#132)
  • fix/fixed fuzz test generation with init-if-needed Anchor feature (#131)
  • fix/program client custom types (#117)
  • fix/check fuzz test name collision by checking the name against HashSet (#114)

[0.5.0] - 2023-08-28#

Added#

  • cli: Added trident subcommand fuzz to run and debug fuzz tests using honggfuzz-rs.
  • cli: Added trident --skip-fuzzer option for init subcommand to skip generation of fuzz test templates.
  • client: Added new Cargo feature fuzzing that enables optional dependencies related to fuzz testing.

[0.4.1] - 2023-08-21#

Changed#

  • Upgrade Solana (=1.16.6) and Anchor framework (=0.28.0) versions.

Fixed#

  • Implemented Anchor Client logic was not able to work with newer version of Anchor. Fixed with async_rpc and async feature.
  • Trident init IDL Parse Error on newer version of Rust, fixed with updated accounts token.

[0.3.0] - 2022-09-23#

Changed#

  • Upgrade Solana (~1.10) and Anchor framework (~0.25) versions

Added#

  • Custom Solana RPC error reporter. If the Solana RPC error is thrown, the error code, message and data (logs) are reported to the output.
  • Custom imports in the .program_client. User is able to import custom types and structures into program client. The import part of the code would not be re-generated.

[0.2.0] - 2022-07-27#

Added#

  • Trident is now configurable. This requires Trident.toml file to exist in the project's root directory - without this file the execution will fail. To solve this re-run trident init or just create an empty Trident.toml file in the project's root directory.
\ No newline at end of file diff --git a/dev/fuzzing/fuzzing-current-limitations/index.html b/dev/fuzzing/fuzzing-current-limitations/index.html index 056177a2..4a5e2489 100644 --- a/dev/fuzzing/fuzzing-current-limitations/index.html +++ b/dev/fuzzing/fuzzing-current-limitations/index.html @@ -1 +1 @@ - Current limitations - Trident
Skip to content

Current limitations#

This section summarizes some known limitations in the current development stage. Further development will be focused on resolving these limitations.

  • Remaining accounts in check methods are not supported.
  • Composite accounts are not supported (however it is possible to generate a fuzz test and finish the composite accounts deserialization manually).
\ No newline at end of file + Current limitations - Trident
Skip to content

Current limitations#

This section summarizes some known limitations in the current development stage. Further development will be focused on resolving these limitations.

  • Remaining accounts in check methods are not supported.
  • Composite accounts are not supported (however it is possible to generate a fuzz test and finish the composite accounts deserialization manually).
\ No newline at end of file diff --git a/dev/fuzzing/fuzzing-examples/index.html b/dev/fuzzing/fuzzing-examples/index.html index f0e1d846..6261de1b 100644 --- a/dev/fuzzing/fuzzing-examples/index.html +++ b/dev/fuzzing/fuzzing-examples/index.html @@ -1 +1 @@ - Fuzzing Examples - Trident
Skip to content

Fuzzing Examples#

Hello World! Example#

Possible vulnerabilities and bugs#

Example usage of Arbitrary Trait with Custom Data type as Instruction parameter#

Example usage of limiting the Instruction data structure with the Arbitrary trait#

Example usage of CPI with available source code to the callee program#

Example usage of CPI with unavailable source code to the callee program (i.e. callee as SBF)#

\ No newline at end of file + Fuzzing Examples - Trident
Skip to content

Fuzzing Examples#

Hello World! Example#

Possible vulnerabilities and bugs#

Example usage of Arbitrary Trait with Custom Data type as Instruction parameter#

Example usage of limiting the Instruction data structure with the Arbitrary trait#

Example usage of CPI with available source code to the callee program#

Example usage of CPI with unavailable source code to the callee program (i.e. callee as SBF)#

\ No newline at end of file diff --git a/dev/fuzzing/fuzzing-introduction/index.html b/dev/fuzzing/fuzzing-introduction/index.html index 73a7b515..bbf48032 100644 --- a/dev/fuzzing/fuzzing-introduction/index.html +++ b/dev/fuzzing/fuzzing-introduction/index.html @@ -1 +1 @@ - Introduction - Trident
Skip to content

Introduction#

Fuzzing is a software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The aim is to uncover bugs and vulnerabilities that might not be detected with conventional testing strategies.

Trident#

The Trident testing framework equips developers with tools to efficiently develop fuzz tests for Anchor-based programs. It streamlines the fuzz testing process through automation and comprehensive support:

  • Automatically parses Anchor-based programs to generate necessary implementations for deserializing instruction accounts.
  • Generates templates for developers to customize according to the specific needs of their fuzz test scenarios.
  • Offers derive macros to effortlessly implement required traits, reducing manual coding efforts.
  • Includes a bank client and helper functions for simplified account management during testing.
  • Provides a Command-Line Interface (CLI) for executing and debugging fuzz tests with ease.

Trident is built for customization, enabling developers to tailor their fuzz tests by adjusting:

  • Execution Order of Instructions: Test different sequences and their effects on the program to uncover sequence-related vulnerabilities.
  • Instruction Parameters: Identify how variations in inputs influence program behavior, testing for robustness against a wide range of data.
  • Instruction Accounts: Explore the impact of different account states on the software's functionality, ensuring comprehensive account testing.
  • Comprehensive Testing: Conduct thorough and effective fuzz testing by combining any of the above aspects.

This framework supports a detailed and methodical approach to fuzz testing, facilitating the identification and remediation of potential vulnerabilities in software applications.

\ No newline at end of file + Introduction - Trident
Skip to content

Introduction#

Fuzzing is a software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The aim is to uncover bugs and vulnerabilities that might not be detected with conventional testing strategies.

Trident#

The Trident testing framework equips developers with tools to efficiently develop fuzz tests for Anchor-based programs. It streamlines the fuzz testing process through automation and comprehensive support:

  • Automatically parses Anchor-based programs to generate necessary implementations for deserializing instruction accounts.
  • Generates templates for developers to customize according to the specific needs of their fuzz test scenarios.
  • Offers derive macros to effortlessly implement required traits, reducing manual coding efforts.
  • Includes a bank client and helper functions for simplified account management during testing.
  • Provides a Command-Line Interface (CLI) for executing and debugging fuzz tests with ease.

Trident is built for customization, enabling developers to tailor their fuzz tests by adjusting:

  • Execution Order of Instructions: Test different sequences and their effects on the program to uncover sequence-related vulnerabilities.
  • Instruction Parameters: Identify how variations in inputs influence program behavior, testing for robustness against a wide range of data.
  • Instruction Accounts: Explore the impact of different account states on the software's functionality, ensuring comprehensive account testing.
  • Comprehensive Testing: Conduct thorough and effective fuzz testing by combining any of the above aspects.

This framework supports a detailed and methodical approach to fuzz testing, facilitating the identification and remediation of potential vulnerabilities in software applications.

\ No newline at end of file diff --git a/dev/fuzzing/fuzzing-lifecycle/index.html b/dev/fuzzing/fuzzing-lifecycle/index.html index b337bcb5..084d73ce 100644 --- a/dev/fuzzing/fuzzing-lifecycle/index.html +++ b/dev/fuzzing/fuzzing-lifecycle/index.html @@ -1 +1 @@ - Fuzz Test Lifecycle - Trident
Skip to content

Fuzz Test Lifecycle#

In the sequence diagram below you can see a simplified fuzz test lifecycle.

Some diagram states are labeled with emojis:

  • âš¡ Mandatory methods that must be implemented by the user.
  • 👤 Optional methods that can be implemented by the user.

Lifecycle#

  1. The fuzzer is running until:
    1. The maximal number of iterations is reached (if specified).
    2. A crash was detected and the exit_upon_crash parameter was set.
    3. User interrupted the test manually (for example by hitting CTRL+C).
  2. In each iteration, the fuzzer generates a sequence of random instructions to execute.
    1. User can optionally customize how the instructions are generated and can specify the instructions that should be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.
  3. For each instruction:
    1. User defined mandatory method get_accounts() is called to collect necessary instruction accounts.
    2. User defined mandatory method get_data() is called to collect instruction data.
    3. A snapshot of all instruction accounts before the instruction execution is saved.
    4. The instruction is executed.
    5. A snapshot of all instruction accounts after the instruction execution is saved.
    6. User defined optional method check() is called to check accounts data and evaluate invariants.
fuzzer_iterations = 0
fuzzer_iterations = 0
fuzzer_iterations < 
max_iterations
fuzzer_iterations <...
done
done
create pre-instruction 
accounts snapshots
create pre-instruction...
execute instruction
execute instruction
create post-instruction 
accounts snapshots
create post-instruction...
check invariants 👤
check invariants 👤
fuzzer_iterations++
fuzzer_iterations++
Generate instructions
pre_ixs 👤
pre_ixs 👤
ixs 👤
ixs 👤
post_ixs 👤
post_ixs 👤
end
end
for ix in instructions
for ix in instructions
get instruction accounts âš¡
get instruction accounts âš¡
get instruction data âš¡
get instruction data âš¡
next ix
next ix
Text is not SVG - cannot display
\ No newline at end of file + Fuzz Test Lifecycle - Trident
Skip to content

Fuzz Test Lifecycle#

In the sequence diagram below you can see a simplified fuzz test lifecycle.

Some diagram states are labeled with emojis:

  • âš¡ Mandatory methods that must be implemented by the user.
  • 👤 Optional methods that can be implemented by the user.

Lifecycle#

  1. The fuzzer is running until:
    1. The maximal number of iterations is reached (if specified).
    2. A crash was detected and the exit_upon_crash parameter was set.
    3. User interrupted the test manually (for example by hitting CTRL+C).
  2. In each iteration, the fuzzer generates a sequence of random instructions to execute.
    1. User can optionally customize how the instructions are generated and can specify the instructions that should be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.
  3. For each instruction:
    1. User defined mandatory method get_accounts() is called to collect necessary instruction accounts.
    2. User defined mandatory method get_data() is called to collect instruction data.
    3. A snapshot of all instruction accounts before the instruction execution is saved.
    4. The instruction is executed.
    5. A snapshot of all instruction accounts after the instruction execution is saved.
    6. User defined optional method check() is called to check accounts data and evaluate invariants.
fuzzer_iterations = 0
fuzzer_iterations = 0
fuzzer_iterations < 
max_iterations
fuzzer_iterations <...
done
done
create pre-instruction 
accounts snapshots
create pre-instruction...
execute instruction
execute instruction
create post-instruction 
accounts snapshots
create post-instruction...
check invariants 👤
check invariants 👤
fuzzer_iterations++
fuzzer_iterations++
Generate instructions
pre_ixs 👤
pre_ixs 👤
ixs 👤
ixs 👤
post_ixs 👤
post_ixs 👤
end
end
for ix in instructions
for ix in instructions
get instruction accounts âš¡
get instruction accounts âš¡
get instruction data âš¡
get instruction data âš¡
next ix
next ix
Text is not SVG - cannot display
\ No newline at end of file diff --git a/dev/fuzzing/fuzzing-run-debug/index.html b/dev/fuzzing/fuzzing-run-debug/index.html index 19fd8579..85f0822b 100644 --- a/dev/fuzzing/fuzzing-run-debug/index.html +++ b/dev/fuzzing/fuzzing-run-debug/index.html @@ -48,4 +48,4 @@ trident fuzz run-debug <TARGET_NAME> <CRASH_FILE_PATH> # for example: trident fuzz run-debug fuzz_0 trident-tests/fuzz_tests/fuzzing/fuzz_0/cr1.fuzz -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p0/index.html b/dev/fuzzing/howto/fuzzing-howto-p0/index.html index 02f78780..aa8988f6 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p0/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p0/index.html @@ -1 +1 @@ - First Steps - Trident
Skip to content

First Steps#

At the current development stage, there are some manual steps required to make your fuzz test compile:

  1. Add dependencies specific to your program to trident-tests/fuzz_tests/Cargo.toml (such as anchor-spl etc.).
  2. Add necessary use statements into trident-tests/fuzz_tests/<FUZZ_TEST_NAME>/accounts_snapshots.rs to import missing types.
\ No newline at end of file + First Steps - Trident
Skip to content

First Steps#

At the current development stage, there are some manual steps required to make your fuzz test compile:

  1. Add dependencies specific to your program to trident-tests/fuzz_tests/Cargo.toml (such as anchor-spl etc.).
  2. Add necessary use statements into trident-tests/fuzz_tests/<FUZZ_TEST_NAME>/accounts_snapshots.rs to import missing types.
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p1/index.html b/dev/fuzzing/howto/fuzzing-howto-p1/index.html index 3c52b3a4..52417408 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p1/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p1/index.html @@ -4,4 +4,4 @@ token_vault: AccountsStorage<TokenStore>, mint: AccountsStorage<MintStore>, } -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p2/index.html b/dev/fuzzing/howto/fuzzing-howto-p2/index.html index 66e02150..0b3f3c73 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p2/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p2/index.html @@ -8,4 +8,4 @@ }; Ok(data) } -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p3/index.html b/dev/fuzzing/howto/fuzzing-howto-p3/index.html index df1dc9ba..ac7a534a 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p3/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p3/index.html @@ -77,4 +77,4 @@ seeds: vec_of_seeds, } }).pubkey(); -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p4/index.html b/dev/fuzzing/howto/fuzzing-howto-p4/index.html index 18b4d6c3..254521e7 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p4/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p4/index.html @@ -22,4 +22,4 @@ Ok(()) } -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p5/index.html b/dev/fuzzing/howto/fuzzing-howto-p5/index.html index 10f8f31b..a534f5f2 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p5/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p5/index.html @@ -5,4 +5,4 @@ Ok(vec![init_ix]) } } -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p6/index.html b/dev/fuzzing/howto/fuzzing-howto-p6/index.html index 8b7e6aec..a957e664 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p6/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p6/index.html @@ -101,4 +101,4 @@ } ... } -

Example#

For a practical example, please refer to the Examples section.

\ No newline at end of file +

Example#

For a practical example, please refer to the Examples section.

\ No newline at end of file diff --git a/dev/fuzzing/howto/fuzzing-howto-p7/index.html b/dev/fuzzing/howto/fuzzing-howto-p7/index.html index da11c93f..a094ef81 100644 --- a/dev/fuzzing/howto/fuzzing-howto-p7/index.html +++ b/dev/fuzzing/howto/fuzzing-howto-p7/index.html @@ -127,4 +127,4 @@ } } ... -

Example#

For a practical example, please refer to the Examples section.

\ No newline at end of file +

Example#

For a practical example, please refer to the Examples section.

\ No newline at end of file diff --git a/dev/fuzzing/initialization/fuzzing-initialization-all/index.html b/dev/fuzzing/initialization/fuzzing-initialization-all/index.html index bacb9bba..7b4f7161 100644 --- a/dev/fuzzing/initialization/fuzzing-initialization-all/index.html +++ b/dev/fuzzing/initialization/fuzzing-initialization-all/index.html @@ -14,4 +14,4 @@ │ └── poc_tests # integration tests folder ├── Trident.toml └── ... -

\ No newline at end of file +

\ No newline at end of file diff --git a/dev/fuzzing/initialization/fuzzing-initialization-fuzz-add/index.html b/dev/fuzzing/initialization/fuzzing-initialization-fuzz-add/index.html index a15fce64..ee0e2238 100644 --- a/dev/fuzzing/initialization/fuzzing-initialization-fuzz-add/index.html +++ b/dev/fuzzing/initialization/fuzzing-initialization-fuzz-add/index.html @@ -10,4 +10,4 @@ │ │ └── Cargo.toml # already present ├── Trident.toml # already present └── ... -

\ No newline at end of file +

\ No newline at end of file diff --git a/dev/fuzzing/initialization/fuzzing-initialization-fuzz/index.html b/dev/fuzzing/initialization/fuzzing-initialization-fuzz/index.html index 9fe0d0ce..7aafe1df 100644 --- a/dev/fuzzing/initialization/fuzzing-initialization-fuzz/index.html +++ b/dev/fuzzing/initialization/fuzzing-initialization-fuzz/index.html @@ -12,4 +12,4 @@ │ │ └── Cargo.toml ├── Trident.toml └── ... -

\ No newline at end of file +

\ No newline at end of file diff --git a/dev/home/home-installation/index.html b/dev/home/home-installation/index.html index 8646620d..9def1cd3 100644 --- a/dev/home/home-installation/index.html +++ b/dev/home/home-installation/index.html @@ -5,4 +5,4 @@ cargo install --version <version> trident-cli

Supported versions#

Trident CLI Anchor Solana Rust
v0.7.0 >=0.29.*1 ^1.17.4 nightly
v0.6.0 >=0.29.*1 ^1.17 nightly
v0.5.0 ~0.28.* =1.16.6
v0.4.0 ~0.27.* >=1.15
v0.3.0 ~0.25.* >=1.10
v0.2.0 ~0.24.* >=1.9
  1. To use Trident with Anchor 0.29.0, run the following commands from your project's root directory after Trident initialization:
    cargo update anchor-client@0.30.0 --precise 0.29.0
     cargo update anchor-spl@0.30.0 --precise 0.29.0
    -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/index.html b/dev/index.html index 649c2a9a..bfe76a81 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1 +1 @@ - Trident - Trident
Skip to content

Trident#

Trident is a Rust-based framework to fuzz and integration test Solana programs to help you ship secure code.

Features#

  • Automated Test Generation: Simplifies the testing process by automatically creating templates for fuzz and integration tests for programs written using the Anchor Framework.

  • Dynamic Data Generation: Increases test coverage with random instruction data and pseudo-random accounts for unpredictable fuzz test scenarios.

  • Custom Instruction Sequences: Provides the flexibility to design specific sequences of instructions to meet particular testing needs or to focus on particular aspects of program behavior during fuzz testing. Invariant Checks: Allows for custom invariants checks to spot vulnerabilities and unwanted behaviors.

\ No newline at end of file + Trident - Trident
Skip to content

Trident#

Trident is a Rust-based framework to fuzz and integration test Solana programs to help you ship secure code.

Features#

  • Automated Test Generation: Simplifies the testing process by automatically creating templates for fuzz and integration tests for programs written using the Anchor Framework.

  • Dynamic Data Generation: Increases test coverage with random instruction data and pseudo-random accounts for unpredictable fuzz test scenarios.

  • Custom Instruction Sequences: Provides the flexibility to design specific sequences of instructions to meet particular testing needs or to focus on particular aspects of program behavior during fuzz testing. Invariant Checks: Allows for custom invariants checks to spot vulnerabilities and unwanted behaviors.

\ No newline at end of file diff --git a/dev/integration-tests/howto/poc-howto-p0/index.html b/dev/integration-tests/howto/poc-howto-p0/index.html index 38d6af46..3fc01cef 100644 --- a/dev/integration-tests/howto/poc-howto-p0/index.html +++ b/dev/integration-tests/howto/poc-howto-p0/index.html @@ -41,4 +41,4 @@ let state = fixture.get_state().await?; assert_eq!(state.something_changed, "yes"); } -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/integration-tests/howto/poc-howto-p1/index.html b/dev/integration-tests/howto/poc-howto-p1/index.html index 56cee9d7..fc587d28 100644 --- a/dev/integration-tests/howto/poc-howto-p1/index.html +++ b/dev/integration-tests/howto/poc-howto-p1/index.html @@ -26,4 +26,4 @@ token_program: Token::id(), } } -
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/integration-tests/initialization/poc-initialization-all/index.html b/dev/integration-tests/initialization/poc-initialization-all/index.html index 8691b6d0..29309d39 100644 --- a/dev/integration-tests/initialization/poc-initialization-all/index.html +++ b/dev/integration-tests/initialization/poc-initialization-all/index.html @@ -1,4 +1,4 @@ - All-Suite - Trident
Skip to content

All-Suite#

To initialize Trident and generate all-suite test templates, navigate to your project's root directory and run

trident init
+ All-Suite - Trident      

All-Suite#

To initialize Trident and generate all-suite test templates, navigate to your project's root directory and run

trident init both
 

The command will generate the following folder structure:

project-root
 ├── .program_client
 ├── trident-tests
@@ -9,4 +9,4 @@
 │   │   └── Cargo.toml
 ├── Trident.toml
 └── ...
-

\ No newline at end of file +

\ No newline at end of file diff --git a/dev/integration-tests/initialization/poc-initialization-poc/index.html b/dev/integration-tests/initialization/poc-initialization-poc/index.html index cc08161c..f1335ac1 100644 --- a/dev/integration-tests/initialization/poc-initialization-poc/index.html +++ b/dev/integration-tests/initialization/poc-initialization-poc/index.html @@ -8,4 +8,4 @@ │ │ └── Cargo.toml ├── Trident.toml └── ... -

\ No newline at end of file +

\ No newline at end of file diff --git a/dev/integration-tests/initialization/poc-initialization-program-client/index.html b/dev/integration-tests/initialization/poc-initialization-program-client/index.html index 9301fa7f..ef985d1e 100644 --- a/dev/integration-tests/initialization/poc-initialization-program-client/index.html +++ b/dev/integration-tests/initialization/poc-initialization-program-client/index.html @@ -1,2 +1,2 @@ Program Client - Trident
Skip to content

Program Client#

By default, Integration Tests initialization generates also a .program_client crate with all necessary implementation for Instructions.

If you are interested in updating the .program_client implementation due to an update inside your program, run

trident build
-

This command will also initialize .program_client if the crate does not exist yet.

\ No newline at end of file +

This command will also initialize .program_client if the crate does not exist yet.

\ No newline at end of file diff --git a/dev/integration-tests/poc-examples/index.html b/dev/integration-tests/poc-examples/index.html index f3d18d91..80798324 100644 --- a/dev/integration-tests/poc-examples/index.html +++ b/dev/integration-tests/poc-examples/index.html @@ -1 +1 @@ - Examples - Trident
Skip to content
\ No newline at end of file + Examples - Trident
Skip to content
\ No newline at end of file diff --git a/dev/integration-tests/poc-introduction/index.html b/dev/integration-tests/poc-introduction/index.html index 3abd5b08..5b14d779 100644 --- a/dev/integration-tests/poc-introduction/index.html +++ b/dev/integration-tests/poc-introduction/index.html @@ -80,4 +80,4 @@ .color13 {fill: #b4009e;} .color14 {fill: #61d6d6;} .color15 {fill: #f2f2f2;} - marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tr marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tri marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trid marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tride marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ triden marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident te marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident tes marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test Finished test [unoptimized + debuginfo] target(s) in 0.17s Running unittests src/lib.rs (target/debug/deps/program_client-35df7129a7acbd48)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/test.rs (target/debug/deps/test-e1d41610f5897c1f)running 2 tests-------------- TEST START --------------program_keypair(1).pubkey = Po1RaS8BEDbNcn5oXsFryAeQ6Wn8fvmE111DJaKCgPCWaiting for fees to stabilize 1...test test_unhappy_path ... okWaiting for fees to stabilize 2...test test_happy_path ... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.89s Running unittests src/lib.rs (target/debug/deps/turnstile-451803623b25a701)running 1 testtest test_id ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests program_clientmarcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ exit
\ No newline at end of file + marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tr marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tri marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trid marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tride marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ triden marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident te marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident tes marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test Finished test [unoptimized + debuginfo] target(s) in 0.17s Running unittests src/lib.rs (target/debug/deps/program_client-35df7129a7acbd48)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/test.rs (target/debug/deps/test-e1d41610f5897c1f)running 2 tests-------------- TEST START --------------program_keypair(1).pubkey = Po1RaS8BEDbNcn5oXsFryAeQ6Wn8fvmE111DJaKCgPCWaiting for fees to stabilize 1...test test_unhappy_path ... okWaiting for fees to stabilize 2...test test_happy_path ... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.89s Running unittests src/lib.rs (target/debug/deps/turnstile-451803623b25a701)running 1 testtest test_id ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests program_clientmarcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ exit
\ No newline at end of file diff --git a/dev/integration-tests/poc-run/index.html b/dev/integration-tests/poc-run/index.html index 902af12b..83f449e8 100644 --- a/dev/integration-tests/poc-run/index.html +++ b/dev/integration-tests/poc-run/index.html @@ -2,4 +2,4 @@

Skipping tests#

#[trident_test]
 #[ignore]
 async fn test() {}
-
\ No newline at end of file +
\ No newline at end of file diff --git a/dev/search/search_index.json b/dev/search/search_index.json index 7c2bb4b8..d9e26788 100644 --- a/dev/search/search_index.json +++ b/dev/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Trident","text":"

Trident is a Rust-based framework to fuzz and integration test Solana programs to help you ship secure code.

"},{"location":"#features","title":"Features","text":""},{"location":"CHANGELOG/","title":"Changelog","text":"

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (SemVer).

Note: Version 0 of Semantic Versioning is handled differently from version 1 and above. The minor version will be incremented upon a breaking change and the patch version will be incremented for features.

"},{"location":"CHANGELOG/#dev-unreleased","title":"[dev] - Unreleased","text":""},{"location":"CHANGELOG/#070-2024-08-14","title":"[0.7.0] - 2024-08-14","text":""},{"location":"CHANGELOG/#added","title":"Added","text":""},{"location":"CHANGELOG/#fixed","title":"Fixed","text":""},{"location":"CHANGELOG/#removed","title":"Removed","text":""},{"location":"CHANGELOG/#060-2024-05-20","title":"[0.6.0] - 2024-05-20","text":""},{"location":"CHANGELOG/#added_1","title":"Added","text":""},{"location":"CHANGELOG/#fixed_1","title":"Fixed","text":""},{"location":"CHANGELOG/#050-2023-08-28","title":"[0.5.0] - 2023-08-28","text":""},{"location":"CHANGELOG/#added_2","title":"Added","text":""},{"location":"CHANGELOG/#041-2023-08-21","title":"[0.4.1] - 2023-08-21","text":""},{"location":"CHANGELOG/#changed","title":"Changed","text":""},{"location":"CHANGELOG/#fixed_2","title":"Fixed","text":""},{"location":"CHANGELOG/#030-2022-09-23","title":"[0.3.0] - 2022-09-23","text":""},{"location":"CHANGELOG/#changed_1","title":"Changed","text":""},{"location":"CHANGELOG/#added_3","title":"Added","text":""},{"location":"CHANGELOG/#020-2022-07-27","title":"[0.2.0] - 2022-07-27","text":""},{"location":"CHANGELOG/#added_4","title":"Added","text":""},{"location":"fuzzing/fuzzing-current-limitations/","title":"Current limitations","text":"

This section summarizes some known limitations in the current development stage. Further development will be focused on resolving these limitations.

"},{"location":"fuzzing/fuzzing-examples/","title":"Fuzzing Examples","text":""},{"location":"fuzzing/fuzzing-examples/#hello-world-example","title":"Hello World! Example","text":""},{"location":"fuzzing/fuzzing-examples/#possible-vulnerabilities-and-bugs","title":"Possible vulnerabilities and bugs","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-arbitrary-trait-with-custom-data-type-as-instruction-parameter","title":"Example usage of Arbitrary Trait with Custom Data type as Instruction parameter","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-limiting-the-instruction-data-structure-with-the-arbitrary-trait","title":"Example usage of limiting the Instruction data structure with the Arbitrary trait","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-cpi-with-available-source-code-to-the-callee-program","title":"Example usage of CPI with available source code to the callee program","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-cpi-with-unavailable-source-code-to-the-callee-program-ie-callee-as-sbf","title":"Example usage of CPI with unavailable source code to the callee program (i.e. callee as SBF)","text":""},{"location":"fuzzing/fuzzing-introduction/","title":"Introduction","text":"

Fuzzing is a software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The aim is to uncover bugs and vulnerabilities that might not be detected with conventional testing strategies.

"},{"location":"fuzzing/fuzzing-introduction/#trident","title":"Trident","text":"

The Trident testing framework equips developers with tools to efficiently develop fuzz tests for Anchor-based programs. It streamlines the fuzz testing process through automation and comprehensive support:

Trident is built for customization, enabling developers to tailor their fuzz tests by adjusting:

This framework supports a detailed and methodical approach to fuzz testing, facilitating the identification and remediation of potential vulnerabilities in software applications.

"},{"location":"fuzzing/fuzzing-lifecycle/","title":"Fuzz Test Lifecycle","text":"

In the sequence diagram below you can see a simplified fuzz test lifecycle.

Some diagram states are labeled with emojis:

"},{"location":"fuzzing/fuzzing-lifecycle/#lifecycle","title":"Lifecycle","text":"
  1. The fuzzer is running until:
    1. The maximal number of iterations is reached (if specified).
    2. A crash was detected and the exit_upon_crash parameter was set.
    3. User interrupted the test manually (for example by hitting CTRL+C).
  2. In each iteration, the fuzzer generates a sequence of random instructions to execute.
    1. User can optionally customize how the instructions are generated and can specify the instructions that should be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.
  3. For each instruction:
    1. User defined mandatory method get_accounts() is called to collect necessary instruction accounts.
    2. User defined mandatory method get_data() is called to collect instruction data.
    3. A snapshot of all instruction accounts before the instruction execution is saved.
    4. The instruction is executed.
    5. A snapshot of all instruction accounts after the instruction execution is saved.
    6. User defined optional method check() is called to check accounts data and evaluate invariants.
fuzzer_iterations = 0fuzzer_iterations = 0fuzzer_iterations <\u00a0max_iterationsfuzzer_iterations <...donedonecreate pre-instruction\u00a0accounts snapshotscreate pre-instruction...execute instructionexecute instructioncreate post-instruction\u00a0accounts snapshotscreate post-instruction...check invariants \ud83d\udc64check invariants \ud83d\udc64fuzzer_iterations++fuzzer_iterations++Generate instructionspre_ixs \ud83d\udc64pre_ixs \ud83d\udc64ixs \ud83d\udc64ixs \ud83d\udc64post_ixs \ud83d\udc64post_ixs \ud83d\udc64endendfor ix in instructionsfor ix in instructionsget instruction accounts \u26a1get instruction accounts \u26a1get instruction data \u26a1get instruction data \u26a1next ixnext ixText is not SVG - cannot display"},{"location":"fuzzing/fuzzing-run-debug/","title":"Run and Debug","text":""},{"location":"fuzzing/fuzzing-run-debug/#run","title":"Run","text":"

Once you have finished the implementation of the Fuzz Test, you can run the Test as follows:

# Replace <TARGET_NAME> with the name of particular\n# fuzz test (for example: \"fuzz_0\")\ntrident fuzz run <TARGET_NAME>\n

Under the hood Trident uses honggfuzz-rs.

You can pass supported parameters via the Trident.toml configuration file:

# Content of Trident.toml\n[honggfuzz]\n# Timeout in seconds (default: 10)\ntimeout = 10\n# Number of fuzzing iterations (default: 0 [no limit])\niterations = 0\n# Number of concurrent fuzzing threads (default: 0 [number of CPUs / 2])\nthreads = 0\n# Don't close children's stdin, stdout, stderr; can be noisy (default: false)\nkeep_output = false\n# Disable ANSI console; use simple log output (default: false)\nverbose = false\n# Exit upon seeing the first crash (default: false)\nexit_upon_crash = false\n# Maximal number of mutations per one run (default: 6)\nmutations_per_run = 6\n# Target compilation directory, (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/hfuzz_target\"]).\n# To not clash with cargo build's default target directory.\ncargo_target_dir = \"\"\n# Honggfuzz working directory, (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/hfuzz_workspace\"]).\nhfuzz_workspace = \"\"\n# Directory where crashes are saved to (default: \"\" [workspace directory])\ncrashdir = \"\"\n# Input file extension (e.g. 'swf'), (default: \"\" ['fuzz'])\nextension = \"\"\n# Number of seconds this fuzzing session will last (default: 0 [no limit])\nrun_time = 0\n# Maximal size of files processed by the fuzzer in bytes (default: 1048576 = 1MB)\nmax_file_size = 1048576\n# Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false)\nsave_all = false\n\n[fuzz]\n# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false)\nallow_duplicate_txs = false\n# Trident will show statistics after the fuzzing session. This option forces use of honggfuzz parameter\n# `keep_output` as true in order to be able to catch fuzzer stdout. (default: false)\nfuzzing_with_stats = true\n

Or you can pass any parameter via environment variables.

A list of hongfuzz parameters can be found in honggfuzz usage documentation. The parameters passed via environment variables have higher priority. For example:

# Time-out: 10 secs\n# Number of concurrent fuzzing threads: 1\n# Number of fuzzing iterations: 10000\n# Display Solana logs in the terminal\nHFUZZ_RUN_ARGS=\"-t 10 -n 1 -N 10000 -Q\" trident fuzz run <TARGET_NAME>\n
"},{"location":"fuzzing/fuzzing-run-debug/#fuzzing-statistics","title":"Fuzzing statistics","text":"

Sometimes, it's useful to know how often a particular instruction has been invoked and how many times it has succeeded or failed. To display these statistics when fuzzing is finished or interrupted, set the fuzzing_with_stats option to true in the [fuzz] section of the Trident.toml configuration file. Please note that this option is disabled by default because it impacts performance.

The statistics show the total number of invocations for each instruction, which is the sum of successful and failed invocations. Successful invocations are those that return an Ok() result. Failed invocations are those that return an Err() result. Additionally, the statistics also show as Check Failed the number of successful invocations that did not pass the user-defined invariants check. Note that unhandled panics are currently logged only as crashes and are not displayed in the fuzzing statistics table.

Keep in mind that the number of fuzz iterations does not directly correspond to the total number of invocations. In one fuzz iteration, the fuzzer might be unable to deserialize fuzz data into instructions, causing the entire iteration to be skipped.

"},{"location":"fuzzing/fuzzing-run-debug/#debug","title":"Debug","text":"

To debug your program with values from a crash file:

# fuzzer will run the <TARGET_NAME> with the specified <CRASH_FILE_PATH>\ntrident fuzz run-debug <TARGET_NAME> <CRASH_FILE_PATH>\n# for example:\ntrident fuzz run-debug fuzz_0 trident-tests/fuzz_tests/fuzzing/fuzz_0/cr1.fuzz\n
"},{"location":"fuzzing/howto/fuzzing-howto-p0/","title":"First Steps","text":"

At the current development stage, there are some manual steps required to make your fuzz test compile:

  1. Add dependencies specific to your program to trident-tests/fuzz_tests/Cargo.toml (such as anchor-spl etc.).
  2. Add necessary use statements into trident-tests/fuzz_tests/<FUZZ_TEST_NAME>/accounts_snapshots.rs to import missing types.
"},{"location":"fuzzing/howto/fuzzing-howto-p1/","title":"Specify accounts to reuse","text":"

Trident fuzzer helps you to generate only a limited amount of pseudo-random accounts and reuse them in the instructions.

Always generating only random accounts would in most cases lead to a situation where the fuzzer would be stuck because the accounts would be almost every time rejected by your Anchor program. Therefore it is necessary to specify, what accounts should be used and also limit the number of newly created accounts to reduce the space complexity.

Then use the corresponding AccountsStorage types such as:

pub struct FuzzAccounts {\n    signer: AccountsStorage<Keypair>,\n    some_pda: AccountsStorage<PdaStore>,\n    token_vault: AccountsStorage<TokenStore>,\n    mint: AccountsStorage<MintStore>,\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p2/","title":"Specify instruction data","text":"

Trident fuzzer generates random instruction data for you.

Currently, it is however required, that you manually assign the random fuzzer data to the instruction data. It is done using the IxOps trait and its method get_data.

fn get_data(\n    &self,\n    _client: &mut impl FuzzClient,\n    _fuzz_accounts: &mut FuzzAccounts,\n) -> Result<Self::IxData, FuzzingError> {\n    let data = fuzz_example1::instruction::Invest {\n        amount: self.data.amount,\n    };\n    Ok(data)\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p3/","title":"Specify instruction accounts","text":"

Trident fuzzer generates random indexes of accounts to use in each instruction. Each created account is saved in the global FuzzAccounts structure which helps you to reuse already existing accounts across all instructions.

You are required to define, how these accounts should be created and which accounts should be passed to an instruction. It is done using the IxOps trait and its method get_accounts.

fn get_accounts(\n    &self,\n    client: &mut impl FuzzClient,\n    fuzz_accounts: &mut FuzzAccounts,\n) -> Result<(Vec<Keypair>, Vec<AccountMeta>), FuzzingError> {\n    let author = fuzz_accounts.author.get_or_create_account(\n        self.accounts.author,\n        client,\n        5000000000000,\n    );\n    let signers = vec![author.clone()];\n    let state = fuzz_accounts\n        .state\n        .get_or_create_account(\n            self.accounts.state,\n            &[author.pubkey().as_ref(), STATE_SEED.as_ref()],\n            &fuzz_example1::ID,\n        )\n        .ok_or(FuzzingError::CannotGetAccounts)?\n        .pubkey();\n    let acc_meta = fuzz_example1::accounts::EndRegistration {\n        author: author.pubkey(),\n        state,\n    }\n    .to_account_metas(None);\n    Ok((signers, acc_meta))\n}\n
Notice especially the helper method fuzz_accounts.<account_name>.get_or_create_account that is used to create a Keypair or retrieve the Public key of the already existing account.

"},{"location":"fuzzing/howto/fuzzing-howto-p3/#create-an-arbitrary-account","title":"Create an arbitrary account","text":"

The AccountsStorage<T> type provides an implementation of the get_or_create_account method that helps you create new or read already existing accounts. There are different implementations for different types of storage (Keypair, TokenStore, MintStore, PdaStore) to simplify the creation of new accounts.

However, there are cases when the provided implementation is not sufficient and it is necessary to create an account manually. These cases can be (but are not limited to) for example:

In that case, you can use the storage method of the AccountsStorage<T> struct that exposes the underlying HashMap<AccountId, T> and you can add new accounts directly to it.

It is possible to create and store any kind of account. For example:

let state = fuzz_accounts\n    .state\n    // gets the storage of all `state` account variants\n    .storage()\n    // returns the Keypair of the `state` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.state)\n    .or_insert_with(|| {\n        let space = State::SIZE;\n        let rent_exempt_lamports = client.get_rent().unwrap()\n                            .minimum_balance(space);\n        let keypair = Keypair::new();\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_lamports,\n            &[],\n            space,\n            &my_program::id(),\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&keypair.pubkey(), &account);\n        keypair\n    });\n
let rent_exempt_for_token_acc = client\n    .get_rent()\n    .unwrap()\n    .minimum_balance(anchor_spl::token::spl_token::state::Account::LEN);\n\nlet my_pda = fuzz_accounts\n    .my_pda\n    // gets the storage of all `my_pda` account variants\n    .storage()\n    // returns the PdaStore struct of the `my_pda` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.my_pda)\n    .or_insert_with(|| {\n        let seeds = &[b\"some-seeds\"];\n        let pda = Pubkey::find_program_address(seeds, &my_program::id()).0;\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_for_token_acc,\n            &[],\n            0,\n            &SYSTEM_PROGRAM_ID,\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&pda, &account);\n        let vec_of_seeds: Vec<Vec<u8>> = seeds.iter().map(|&seed| seed.to_vec())\n                            .collect();\n        PdaStore {\n            pubkey: pda,\n            seeds: vec_of_seeds,\n        }\n    }).pubkey();\n
"},{"location":"fuzzing/howto/fuzzing-howto-p4/","title":"Define invariants checks","text":"

After each successful instruction execution, the check() method is called to check the account data invariants.

For each instruction, you can compare the account data before and after the instruction execution such as:

fn check(\n    &self,\n    pre_ix: Self::IxSnapshot,\n    post_ix: Self::IxSnapshot,\n    _ix_data: Self::IxData,\n) -> Result<(), &'static str> {\n    if let Some(escrow_pre) = pre_ix.escrow {\n        // we can unwrap the receiver account because it\n        // has to be initialized before the instruction\n        // execution and it is not supposed to be closed\n        // after the instruction execution either\n        let receiver = pre_ix.receiver.unwrap();\n        let receiver_lamports_before = receiver.lamports();\n        let receiver_lamports_after = post_ix.receiver.unwrap().lamports();\n\n        if receiver.key() != escrow_pre.receiver.key()\n            && receiver_lamports_before < receiver_lamports_after\n        {\n            return Err(\"Un-authorized withdrawal\");\n        }\n    }\n\n    Ok(())\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p5/","title":"Customize instructions generation","text":"

It is possible to customize how the instructions are generated and which instructions will be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each fuzz iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.

impl FuzzDataBuilder<FuzzInstruction> for MyFuzzData {\n    fn pre_ixs(u: &mut arbitrary::Unstructured) ->\n        arbitrary::Result<Vec<FuzzInstruction>> {\n        let init_ix = FuzzInstruction::Initialize(Initialize::arbitrary(u)?);\n        Ok(vec![init_ix])\n    }\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p6/","title":"How to use Custom Data Types","text":"

If you use Custom Types as Instruction data arguments, you may encounter a problem that the Custom Type does not implement

For example:

#[program]\npub mod your_program {\n    pub fn init_vesting(\n            ctx: Context<InitVesting>,\n            recipient: Pubkey,\n            amount: u64,\n            start_at: u64,\n            end_at: u64,\n            interval: u64,\n            // Custom Type input argument\n            _input_variant: CustomEnumInput,\n    ) -> Result<()> {\n        _init_vesting(ctx, recipient, amount, start_at, end_at, interval)\n    }\n}\n\n...\n\n#[derive(AnchorDeserialize, AnchorSerialize)]\npub enum CustomEnumInput {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

Then inside the fuzz_instructions.rs, you may see:

#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n    /// IMPORTANT:\n    /// your_program::CustomEnumInput does not derive\n    /// Arbitrary, nor Debug trait\n    pub _input_variant: your_program::CustomEnumInput,\n}\n

To resolve this issue, you have two options.

"},{"location":"fuzzing/howto/fuzzing-howto-p6/#derive-debug-and-arbitrary-traits-inside-your-program","title":"Derive Debug and Arbitrary traits inside your program","text":"

This option necessitates updating the source code of your on-chain program, which might be undesirable. If you prefer not to modify your program, consider the alternative option provided below.

Inside programs/<YOUR_PROGRAM>/Cargo.toml file include:

...\n[dependencies]\n...\narbitrary = { version = \"1.3.0\", features = [\"derive\"] }\n...\n

and within the program source code:

// derive Debug, Arbitrary\n#[derive(AnchorDeserialize, AnchorSerialize, Debug, Arbitrary)]\npub enum CustomEnumInput {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

"},{"location":"fuzzing/howto/fuzzing-howto-p6/#derive-debug-and-arbitrary-traits-inside-the-fuzz-test","title":"Derive Debug and Arbitrary traits inside the Fuzz Test","text":"

You can redefine the custom type within the fuzz_instructions.rs file, along with all the necessary traits.

// Redefine the Custom Type inside the fuzz_instructions.rs,\n// but this time with all of the required traits.\n#[derive(Arbitrary,Debug, Clone, Copy)]\npub enum CustomEnumInputFuzz {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

And instead of using the input argument defined within the program, you should modify it as follows:

#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n    /// IMPORTANT:\n    /// redefined Custom Type\n    pub _input_variant: CustomEnumInputFuzz,\n}\n

Then, you would also need to implement the std::convert::From<T> trait to enable conversion between the newly defined Custom Type and the Custom Type used within your program.

// implement std::convert::From to convert between CustomEnumInputFuzz\n// and your_program::CustomEnumInput as these are distinc Data Types.\nimpl std::convert::From<CustomEnumInputFuzz> for your_program::CustomEnumInput {\n    fn from(val: CustomEnumInputFuzz) -> Self {\n        match val {\n            CustomEnumInputFuzz::InputVariant1 => {\n                your_program::CustomEnumInput::InputVariant1\n            }\n            CustomEnumInputFuzz::InputVariant2 => {\n                your_program::CustomEnumInput::InputVariant2\n            }\n            CustomEnumInputFuzz::InputVariant3 => {\n                your_program::CustomEnumInput::InputVariant3\n            }\n        }\n    }\n}\n
Finally, within the get_data function, you can proceed as follows:
impl<'info> IxOps<'info> for InitVestingData {\n    ...\n    fn get_data(\n        &self,\n        _client: &mut impl FuzzClient,\n        _fuzz_accounts: &mut FuzzAccounts,\n    ) -> Result<Self::IxData, FuzzingError> {\n        // cast variable into correct Data Type\n        let variant = self.data._input_variant.into();\n\n        let data = your_program::instruction::InitVestingData {\n        ...\n        _input_variant:variant,\n        ...\n        };\n        Ok(data)\n    }\n    ...\n}\n

"},{"location":"fuzzing/howto/fuzzing-howto-p6/#example","title":"Example","text":"

For a practical example, please refer to the Examples section.

"},{"location":"fuzzing/howto/fuzzing-howto-p7/","title":"How to use Arbitrary crate","text":"

The Arbitrary crate in Rust is used for generating well-typed, structured instances of data from raw byte buffers, making it useful for fuzzing by producing random but structured data for tests.

By implementing the Arbitrary trait for Instruction Data structures, you can guide the fuzzing tool to generate meaningful instances of these structures, thus ensuring a more effective and targeted fuzzing process.

Let`s say your Solana program contains instruction, with a similar logic as the example below:

#[program]\npub mod your_program {\n    pub fn init_vesting(\n        ctx: Context<InitVesting>,\n        recipient: Pubkey,\n        amount: u64,\n        start_at: u64,\n        end_at: u64,\n        interval: u64,\n    ) -> Result<()> {\n        _init_vesting(ctx, recipient, amount, start_at, end_at, interval)\n    }\n}\n...\npub fn _init_vesting(\n    ctx: Context<InitVesting>,\n    recipient: Pubkey,\n    amount: u64,\n    start_at: u64,\n    end_at: u64,\n    interval: u64,\n) -> Result<()> {\n    ...\n    // the Instruction Data arguments are not completely random\n    // and should have the following restrictions\n    require!(amount > 0, VestingError::InvalidAmount);\n    require!(end_at > start_at, VestingError::InvalidTimeRange);\n    require!(end_at - start_at > interval, VestingError::InvalidInterval);\n    require!(interval > 0, VestingError::InvalidInterval);\n    ...\n\n}\n

For this purpose, you can limit the generated Instruction Data structure that is sent to the instruction by the fuzzer, in the following ways

"},{"location":"fuzzing/howto/fuzzing-howto-p7/#customizing-single-fields","title":"Customizing single fields","text":"

You can limit the generated Instruction Input Data by customizing particular fields, Check Customizing single fields for more details.

For the example specified above, with the customization we can limit the particular fields such that

// Instruction Data structure automatically generated\n// inside fuzz_instructions.rs\n...\n#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    // specify the range for amount\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1_000_000)\n    )]\n    pub amount: u64,\n    // specify the range for start_at , this way it will\n    // be always smaller than end_at\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(0..=1_000_000)\n    )]\n    pub start_at: u64,\n    // specify the range for end_at\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured|\n        u.int_in_range(1_001_001..=1_050_000)\n    )]\n    pub end_at: u64,\n    // specify the range for interval\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1000)\n    )]\n    pub interval: u64,\n}\n...\n

"},{"location":"fuzzing/howto/fuzzing-howto-p7/#implementing-arbitrary-by-hand","title":"Implementing Arbitrary By Hand","text":"

Alternatively, you can write Arbitrary implementation by yourself.

// for the given example above, this structure is automatically generated\n// with the fuzzer inside fuzz_instructions.rs\n#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n}\n

Now, instead of using an automatically derived Arbitrary trait, you can implement the trait by hand

...\n#[derive(Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n}\nimpl<'a> Arbitrary<'a> for InitVestingData {\n    fn arbitrary(\n        u: &mut arbitrary::Unstructured<'a>\n    ) -> arbitrary::Result<Self> {\n        // obtain AccountId\n        let recipient = AccountId::arbitrary(u)?;\n\n        // limit the generated amount to the 1_000_000\n        let amount = u.int_in_range(1..=1_000_000)?;\n\n        // now we want to obtain\n        // - start_at\n        // - end_at\n        // - interval\n        // however we want to limit the data such that:\n        // - start_at < end_at\n        // - end_at - start_at > interval\n        // - interval has lower limit of 500 and upper limit of 1000.\n\n        let start_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let end_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let interval: u64 = u.int_in_range(500..=1000)?;\n\n        // ensure that start_at < end_at\n        if start_at >= end_at {\n            return Err(arbitrary::Error::IncorrectFormat);\n        }\n\n        // ensure that end_at - start_at > interval\n        match end_at.checked_sub(start_at) {\n            Some(diff) => {\n                if diff <= interval {\n                    return Err(arbitrary::Error::IncorrectFormat);\n                }\n            }\n            None => return Err(arbitrary::Error::IncorrectFormat),\n        }\n\n        Ok(InitVestingData {\n            recipient,\n            amount,\n            start_at,\n            end_at,\n            interval,\n        })\n    }\n}\n...\n

"},{"location":"fuzzing/howto/fuzzing-howto-p7/#example","title":"Example","text":"

For a practical example, please refer to the Examples section.

"},{"location":"fuzzing/initialization/fuzzing-initialization-all/","title":"All-Suite","text":"

To initialize Trident and generate all-suite test templates, navigate to your project's root directory and run

trident init both\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 .program_client\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_0 # particular fuzz test\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 accounts_snapshots.rs # generated accounts deserialization methods\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 test_fuzz.rs # the binary target of your fuzz test\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fuzz_instructions.rs # the definition of your fuzz test\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_1\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_X # possible multiple fuzz tests\n\u2502   \u2502   \u251c\u2500\u2500 fuzzing # compilations and crashes folder\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u2502   \u2514\u2500\u2500 poc_tests # integration tests folder\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"fuzzing/initialization/fuzzing-initialization-fuzz-add/","title":"Add new Fuzz Test","text":"

If you have already initialized Trident within your project, and you are interested in initializing a new fuzz test run.

trident fuzz add\n

The command will generate a new fuzz test as follows:

project-root\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_X # new fuzz test folder\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 accounts_snapshots.rs\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 test_fuzz.rs\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fuzz_instructions.rs\n\u2502   \u2502   \u251c\u2500\u2500 fuzzing\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml # already present\n\u251c\u2500\u2500 Trident.toml # already present\n\u2514\u2500\u2500 ...\n

"},{"location":"fuzzing/initialization/fuzzing-initialization-fuzz/","title":"Fuzz test-only","text":"

If you are interested only in generating templates for fuzz tests run

trident init fuzz\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_0 # particular fuzz test\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 accounts_snapshots.rs # generated accounts deserialization methods\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 test_fuzz.rs # the binary target of your fuzz test\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fuzz_instructions.rs # the definition of your fuzz test\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_1\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_X # possible multiple fuzz tests\n\u2502   \u2502   \u251c\u2500\u2500 fuzzing # compilations and crashes folder\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"home/home-installation/","title":"Installation","text":""},{"location":"home/home-installation/#dependencies","title":"Dependencies","text":"

Check supported versions section for further details.

"},{"location":"home/home-installation/#installation_1","title":"Installation","text":"
cargo install trident-cli\n\n# or the specific version\n\ncargo install --version <version> trident-cli\n
"},{"location":"home/home-installation/#supported-versions","title":"Supported versions","text":" Trident CLI Anchor Solana Rust v0.7.0 >=0.29.*1 ^1.17.4 nightly v0.6.0 >=0.29.*1 ^1.17 nightly v0.5.0 ~0.28.* =1.16.6 v0.4.0 ~0.27.* >=1.15 v0.3.0 ~0.25.* >=1.10 v0.2.0 ~0.24.* >=1.9
  1. To use Trident with Anchor 0.29.0, run the following commands from your project's root directory after Trident initialization:
    cargo update anchor-client@0.30.0 --precise 0.29.0\ncargo update anchor-spl@0.30.0 --precise 0.29.0\n
"},{"location":"integration-tests/poc-examples/","title":"Examples","text":""},{"location":"integration-tests/poc-introduction/","title":"Introduction","text":"

Trident supports writing Integration Tests in Rust.

marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tr marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tri marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trid marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tride marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ triden marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident te marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident tes marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test Finished test [unoptimized + debuginfo] target(s) in 0.17s Running unittests src/lib.rs (target/debug/deps/program_client-35df7129a7acbd48)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/test.rs (target/debug/deps/test-e1d41610f5897c1f)running 2 tests-------------- TEST START --------------program_keypair(1).pubkey = Po1RaS8BEDbNcn5oXsFryAeQ6Wn8fvmE111DJaKCgPCWaiting for fees to stabilize 1...test test_unhappy_path ... okWaiting for fees to stabilize 2...test test_happy_path ... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.89s Running unittests src/lib.rs (target/debug/deps/turnstile-451803623b25a701)running 1 testtest test_id ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests program_clientmarcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ exit"},{"location":"integration-tests/poc-run/","title":"Run","text":"

Once you have finished the implementation of the Integration Test, you can run the Test as follows:

trident test\n
"},{"location":"integration-tests/poc-run/#skipping-tests","title":"Skipping tests","text":"
#[trident_test]\n#[ignore]\nasync fn test() {}\n
"},{"location":"integration-tests/howto/poc-howto-p0/","title":"Init Fixture","text":"
// <my_project>/trident-tests/poc_tests/tests/test.rs\n// TODO: do not forget to add all necessary dependencies to the generated `trident-tests/poc_tests/Cargo.toml`\nuse program_client::my_instruction;\nuse trident_client::*;\nuse my_program;\n\n#[throws]\n#[fixture]\nasync fn init_fixture() -> Fixture {\n  // create a test fixture\n  let mut fixture = Fixture {\n    client: Client::new(system_keypair(0)),\n    // make sure to pass the correct name of your program\n    program: anchor_keypair(\"my_program_name\").unwrap(),\n    state: keypair(42),\n  };\n  // deploy the program to test\n  fixture.deploy().await?;\n  // call instruction init\n  my_instruction::initialize(\n    &fixture.client,\n    fixture.state.pubkey(),\n    fixture.client.payer().pubkey(),\n    System::id(),\n    Some(fixture.state.clone()),\n  ).await?;\n  fixture\n}\n\n#[trident_test]\nasync fn test_happy_path(#[future] init_fixture: Result<Fixture>) {\n  let fixture = init_fixture.await?;\n  // call the instruction\n  my_instruction::do_something(\n    &fixture.client,\n    \"dummy_string\".to_owned(),\n    fixture.state.pubkey(),\n    None,\n  ).await?;\n  // check the test result\n  let state = fixture.get_state().await?;\n  assert_eq!(state.something_changed, \"yes\");\n}\n
"},{"location":"integration-tests/howto/poc-howto-p1/","title":"Testing programs with associated token accounts","text":"
# <my-project>/trident-tests/poc_tests/Cargo.toml\n# import the correct versions manually\nanchor-spl = \"0.29.0\"\nspl-associated-token-account = \"2.0.0\"\n
// <my-project>/trident-tests/poc_tests/tests/test.rs\nuse anchor_spl::token::Token;\nuse spl_associated_token_account;\n\nasync fn init_fixture() -> Fixture {\n  // ...\n  let account = keypair(1);\n  let mint = keypair(2);\n  // constructs a token mint\n  client\n    .create_token_mint(&mint, mint.pubkey(), None, 0)\n    .await?;\n  // constructs associated token account\n  let token_account = client\n    .create_associated_token_account(&account, mint.pubkey())\n    .await?;\n  let associated_token_program = spl_associated_token_account::id();\n  // derives the associated token account address for the given wallet and mint\n  let associated_token_address = spl_associated_token_account::get_associated_token_address(&account.pubkey(), mint);\n  Fixture {\n    // ...\n    token_program: Token::id(),\n  }\n}\n
"},{"location":"integration-tests/initialization/poc-initialization-all/","title":"All-Suite","text":"

To initialize Trident and generate all-suite test templates, navigate to your project's root directory and run

trident init\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 .program_client\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u251c\u2500\u2500 poc_tests # integration tests folder\n\u2502   \u2502   \u251c\u2500\u2500 tests\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 test.rs # Integration Tests implementation\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"integration-tests/initialization/poc-initialization-poc/","title":"Integration test-only","text":"

If you are interested only in generating templates for Integration Tests run

trident init poc\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 .program_client\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 poc_tests # integration tests folder\n\u2502   \u2502   \u251c\u2500\u2500 tests\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 test.rs\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"integration-tests/initialization/poc-initialization-program-client/","title":"Program Client","text":"

By default, Integration Tests initialization generates also a .program_client crate with all necessary implementation for Instructions.

If you are interested in updating the .program_client implementation due to an update inside your program, run

trident build\n

This command will also initialize .program_client if the crate does not exist yet.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Trident","text":"

Trident is a Rust-based framework to fuzz and integration test Solana programs to help you ship secure code.

"},{"location":"#features","title":"Features","text":""},{"location":"CHANGELOG/","title":"Changelog","text":"

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (SemVer).

Note: Version 0 of Semantic Versioning is handled differently from version 1 and above. The minor version will be incremented upon a breaking change and the patch version will be incremented for features.

"},{"location":"CHANGELOG/#dev-unreleased","title":"[dev] - Unreleased","text":""},{"location":"CHANGELOG/#070-2024-08-14","title":"[0.7.0] - 2024-08-14","text":""},{"location":"CHANGELOG/#added","title":"Added","text":""},{"location":"CHANGELOG/#fixed","title":"Fixed","text":""},{"location":"CHANGELOG/#removed","title":"Removed","text":""},{"location":"CHANGELOG/#060-2024-05-20","title":"[0.6.0] - 2024-05-20","text":""},{"location":"CHANGELOG/#added_1","title":"Added","text":""},{"location":"CHANGELOG/#fixed_1","title":"Fixed","text":""},{"location":"CHANGELOG/#050-2023-08-28","title":"[0.5.0] - 2023-08-28","text":""},{"location":"CHANGELOG/#added_2","title":"Added","text":""},{"location":"CHANGELOG/#041-2023-08-21","title":"[0.4.1] - 2023-08-21","text":""},{"location":"CHANGELOG/#changed","title":"Changed","text":""},{"location":"CHANGELOG/#fixed_2","title":"Fixed","text":""},{"location":"CHANGELOG/#030-2022-09-23","title":"[0.3.0] - 2022-09-23","text":""},{"location":"CHANGELOG/#changed_1","title":"Changed","text":""},{"location":"CHANGELOG/#added_3","title":"Added","text":""},{"location":"CHANGELOG/#020-2022-07-27","title":"[0.2.0] - 2022-07-27","text":""},{"location":"CHANGELOG/#added_4","title":"Added","text":""},{"location":"fuzzing/fuzzing-current-limitations/","title":"Current limitations","text":"

This section summarizes some known limitations in the current development stage. Further development will be focused on resolving these limitations.

"},{"location":"fuzzing/fuzzing-examples/","title":"Fuzzing Examples","text":""},{"location":"fuzzing/fuzzing-examples/#hello-world-example","title":"Hello World! Example","text":""},{"location":"fuzzing/fuzzing-examples/#possible-vulnerabilities-and-bugs","title":"Possible vulnerabilities and bugs","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-arbitrary-trait-with-custom-data-type-as-instruction-parameter","title":"Example usage of Arbitrary Trait with Custom Data type as Instruction parameter","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-limiting-the-instruction-data-structure-with-the-arbitrary-trait","title":"Example usage of limiting the Instruction data structure with the Arbitrary trait","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-cpi-with-available-source-code-to-the-callee-program","title":"Example usage of CPI with available source code to the callee program","text":""},{"location":"fuzzing/fuzzing-examples/#example-usage-of-cpi-with-unavailable-source-code-to-the-callee-program-ie-callee-as-sbf","title":"Example usage of CPI with unavailable source code to the callee program (i.e. callee as SBF)","text":""},{"location":"fuzzing/fuzzing-introduction/","title":"Introduction","text":"

Fuzzing is a software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The aim is to uncover bugs and vulnerabilities that might not be detected with conventional testing strategies.

"},{"location":"fuzzing/fuzzing-introduction/#trident","title":"Trident","text":"

The Trident testing framework equips developers with tools to efficiently develop fuzz tests for Anchor-based programs. It streamlines the fuzz testing process through automation and comprehensive support:

Trident is built for customization, enabling developers to tailor their fuzz tests by adjusting:

This framework supports a detailed and methodical approach to fuzz testing, facilitating the identification and remediation of potential vulnerabilities in software applications.

"},{"location":"fuzzing/fuzzing-lifecycle/","title":"Fuzz Test Lifecycle","text":"

In the sequence diagram below you can see a simplified fuzz test lifecycle.

Some diagram states are labeled with emojis:

"},{"location":"fuzzing/fuzzing-lifecycle/#lifecycle","title":"Lifecycle","text":"
  1. The fuzzer is running until:
    1. The maximal number of iterations is reached (if specified).
    2. A crash was detected and the exit_upon_crash parameter was set.
    3. User interrupted the test manually (for example by hitting CTRL+C).
  2. In each iteration, the fuzzer generates a sequence of random instructions to execute.
    1. User can optionally customize how the instructions are generated and can specify the instructions that should be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.
  3. For each instruction:
    1. User defined mandatory method get_accounts() is called to collect necessary instruction accounts.
    2. User defined mandatory method get_data() is called to collect instruction data.
    3. A snapshot of all instruction accounts before the instruction execution is saved.
    4. The instruction is executed.
    5. A snapshot of all instruction accounts after the instruction execution is saved.
    6. User defined optional method check() is called to check accounts data and evaluate invariants.
fuzzer_iterations = 0fuzzer_iterations = 0fuzzer_iterations <\u00a0max_iterationsfuzzer_iterations <...donedonecreate pre-instruction\u00a0accounts snapshotscreate pre-instruction...execute instructionexecute instructioncreate post-instruction\u00a0accounts snapshotscreate post-instruction...check invariants \ud83d\udc64check invariants \ud83d\udc64fuzzer_iterations++fuzzer_iterations++Generate instructionspre_ixs \ud83d\udc64pre_ixs \ud83d\udc64ixs \ud83d\udc64ixs \ud83d\udc64post_ixs \ud83d\udc64post_ixs \ud83d\udc64endendfor ix in instructionsfor ix in instructionsget instruction accounts \u26a1get instruction accounts \u26a1get instruction data \u26a1get instruction data \u26a1next ixnext ixText is not SVG - cannot display"},{"location":"fuzzing/fuzzing-run-debug/","title":"Run and Debug","text":""},{"location":"fuzzing/fuzzing-run-debug/#run","title":"Run","text":"

Once you have finished the implementation of the Fuzz Test, you can run the Test as follows:

# Replace <TARGET_NAME> with the name of particular\n# fuzz test (for example: \"fuzz_0\")\ntrident fuzz run <TARGET_NAME>\n

Under the hood Trident uses honggfuzz-rs.

You can pass supported parameters via the Trident.toml configuration file:

# Content of Trident.toml\n[honggfuzz]\n# Timeout in seconds (default: 10)\ntimeout = 10\n# Number of fuzzing iterations (default: 0 [no limit])\niterations = 0\n# Number of concurrent fuzzing threads (default: 0 [number of CPUs / 2])\nthreads = 0\n# Don't close children's stdin, stdout, stderr; can be noisy (default: false)\nkeep_output = false\n# Disable ANSI console; use simple log output (default: false)\nverbose = false\n# Exit upon seeing the first crash (default: false)\nexit_upon_crash = false\n# Maximal number of mutations per one run (default: 6)\nmutations_per_run = 6\n# Target compilation directory, (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/hfuzz_target\"]).\n# To not clash with cargo build's default target directory.\ncargo_target_dir = \"\"\n# Honggfuzz working directory, (default: \"\" [\"trident-tests/fuzz_tests/fuzzing/hfuzz_workspace\"]).\nhfuzz_workspace = \"\"\n# Directory where crashes are saved to (default: \"\" [workspace directory])\ncrashdir = \"\"\n# Input file extension (e.g. 'swf'), (default: \"\" ['fuzz'])\nextension = \"\"\n# Number of seconds this fuzzing session will last (default: 0 [no limit])\nrun_time = 0\n# Maximal size of files processed by the fuzzer in bytes (default: 1048576 = 1MB)\nmax_file_size = 1048576\n# Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames (default: false)\nsave_all = false\n\n[fuzz]\n# Allow processing of duplicate transactions. Setting to true might speed up fuzzing but can cause false positive crashes (default: false)\nallow_duplicate_txs = false\n# Trident will show statistics after the fuzzing session. This option forces use of honggfuzz parameter\n# `keep_output` as true in order to be able to catch fuzzer stdout. (default: false)\nfuzzing_with_stats = true\n

Or you can pass any parameter via environment variables.

A list of hongfuzz parameters can be found in honggfuzz usage documentation. The parameters passed via environment variables have higher priority. For example:

# Time-out: 10 secs\n# Number of concurrent fuzzing threads: 1\n# Number of fuzzing iterations: 10000\n# Display Solana logs in the terminal\nHFUZZ_RUN_ARGS=\"-t 10 -n 1 -N 10000 -Q\" trident fuzz run <TARGET_NAME>\n
"},{"location":"fuzzing/fuzzing-run-debug/#fuzzing-statistics","title":"Fuzzing statistics","text":"

Sometimes, it's useful to know how often a particular instruction has been invoked and how many times it has succeeded or failed. To display these statistics when fuzzing is finished or interrupted, set the fuzzing_with_stats option to true in the [fuzz] section of the Trident.toml configuration file. Please note that this option is disabled by default because it impacts performance.

The statistics show the total number of invocations for each instruction, which is the sum of successful and failed invocations. Successful invocations are those that return an Ok() result. Failed invocations are those that return an Err() result. Additionally, the statistics also show as Check Failed the number of successful invocations that did not pass the user-defined invariants check. Note that unhandled panics are currently logged only as crashes and are not displayed in the fuzzing statistics table.

Keep in mind that the number of fuzz iterations does not directly correspond to the total number of invocations. In one fuzz iteration, the fuzzer might be unable to deserialize fuzz data into instructions, causing the entire iteration to be skipped.

"},{"location":"fuzzing/fuzzing-run-debug/#debug","title":"Debug","text":"

To debug your program with values from a crash file:

# fuzzer will run the <TARGET_NAME> with the specified <CRASH_FILE_PATH>\ntrident fuzz run-debug <TARGET_NAME> <CRASH_FILE_PATH>\n# for example:\ntrident fuzz run-debug fuzz_0 trident-tests/fuzz_tests/fuzzing/fuzz_0/cr1.fuzz\n
"},{"location":"fuzzing/howto/fuzzing-howto-p0/","title":"First Steps","text":"

At the current development stage, there are some manual steps required to make your fuzz test compile:

  1. Add dependencies specific to your program to trident-tests/fuzz_tests/Cargo.toml (such as anchor-spl etc.).
  2. Add necessary use statements into trident-tests/fuzz_tests/<FUZZ_TEST_NAME>/accounts_snapshots.rs to import missing types.
"},{"location":"fuzzing/howto/fuzzing-howto-p1/","title":"Specify accounts to reuse","text":"

Trident fuzzer helps you to generate only a limited amount of pseudo-random accounts and reuse them in the instructions.

Always generating only random accounts would in most cases lead to a situation where the fuzzer would be stuck because the accounts would be almost every time rejected by your Anchor program. Therefore it is necessary to specify, what accounts should be used and also limit the number of newly created accounts to reduce the space complexity.

Then use the corresponding AccountsStorage types such as:

pub struct FuzzAccounts {\n    signer: AccountsStorage<Keypair>,\n    some_pda: AccountsStorage<PdaStore>,\n    token_vault: AccountsStorage<TokenStore>,\n    mint: AccountsStorage<MintStore>,\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p2/","title":"Specify instruction data","text":"

Trident fuzzer generates random instruction data for you.

Currently, it is however required, that you manually assign the random fuzzer data to the instruction data. It is done using the IxOps trait and its method get_data.

fn get_data(\n    &self,\n    _client: &mut impl FuzzClient,\n    _fuzz_accounts: &mut FuzzAccounts,\n) -> Result<Self::IxData, FuzzingError> {\n    let data = fuzz_example1::instruction::Invest {\n        amount: self.data.amount,\n    };\n    Ok(data)\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p3/","title":"Specify instruction accounts","text":"

Trident fuzzer generates random indexes of accounts to use in each instruction. Each created account is saved in the global FuzzAccounts structure which helps you to reuse already existing accounts across all instructions.

You are required to define, how these accounts should be created and which accounts should be passed to an instruction. It is done using the IxOps trait and its method get_accounts.

fn get_accounts(\n    &self,\n    client: &mut impl FuzzClient,\n    fuzz_accounts: &mut FuzzAccounts,\n) -> Result<(Vec<Keypair>, Vec<AccountMeta>), FuzzingError> {\n    let author = fuzz_accounts.author.get_or_create_account(\n        self.accounts.author,\n        client,\n        5000000000000,\n    );\n    let signers = vec![author.clone()];\n    let state = fuzz_accounts\n        .state\n        .get_or_create_account(\n            self.accounts.state,\n            &[author.pubkey().as_ref(), STATE_SEED.as_ref()],\n            &fuzz_example1::ID,\n        )\n        .ok_or(FuzzingError::CannotGetAccounts)?\n        .pubkey();\n    let acc_meta = fuzz_example1::accounts::EndRegistration {\n        author: author.pubkey(),\n        state,\n    }\n    .to_account_metas(None);\n    Ok((signers, acc_meta))\n}\n
Notice especially the helper method fuzz_accounts.<account_name>.get_or_create_account that is used to create a Keypair or retrieve the Public key of the already existing account.

"},{"location":"fuzzing/howto/fuzzing-howto-p3/#create-an-arbitrary-account","title":"Create an arbitrary account","text":"

The AccountsStorage<T> type provides an implementation of the get_or_create_account method that helps you create new or read already existing accounts. There are different implementations for different types of storage (Keypair, TokenStore, MintStore, PdaStore) to simplify the creation of new accounts.

However, there are cases when the provided implementation is not sufficient and it is necessary to create an account manually. These cases can be (but are not limited to) for example:

In that case, you can use the storage method of the AccountsStorage<T> struct that exposes the underlying HashMap<AccountId, T> and you can add new accounts directly to it.

It is possible to create and store any kind of account. For example:

let state = fuzz_accounts\n    .state\n    // gets the storage of all `state` account variants\n    .storage()\n    // returns the Keypair of the `state` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.state)\n    .or_insert_with(|| {\n        let space = State::SIZE;\n        let rent_exempt_lamports = client.get_rent().unwrap()\n                            .minimum_balance(space);\n        let keypair = Keypair::new();\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_lamports,\n            &[],\n            space,\n            &my_program::id(),\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&keypair.pubkey(), &account);\n        keypair\n    });\n
let rent_exempt_for_token_acc = client\n    .get_rent()\n    .unwrap()\n    .minimum_balance(anchor_spl::token::spl_token::state::Account::LEN);\n\nlet my_pda = fuzz_accounts\n    .my_pda\n    // gets the storage of all `my_pda` account variants\n    .storage()\n    // returns the PdaStore struct of the `my_pda` account with\n    // the given `AccountId` if it has been added previously\n    .entry(self.accounts.my_pda)\n    .or_insert_with(|| {\n        let seeds = &[b\"some-seeds\"];\n        let pda = Pubkey::find_program_address(seeds, &my_program::id()).0;\n        let account = AccountSharedData::new_data_with_space::<[u8; 0]>(\n            rent_exempt_for_token_acc,\n            &[],\n            0,\n            &SYSTEM_PROGRAM_ID,\n        ).unwrap();\n        // insert the custom account also into the client\n        client.set_account_custom(&pda, &account);\n        let vec_of_seeds: Vec<Vec<u8>> = seeds.iter().map(|&seed| seed.to_vec())\n                            .collect();\n        PdaStore {\n            pubkey: pda,\n            seeds: vec_of_seeds,\n        }\n    }).pubkey();\n
"},{"location":"fuzzing/howto/fuzzing-howto-p4/","title":"Define invariants checks","text":"

After each successful instruction execution, the check() method is called to check the account data invariants.

For each instruction, you can compare the account data before and after the instruction execution such as:

fn check(\n    &self,\n    pre_ix: Self::IxSnapshot,\n    post_ix: Self::IxSnapshot,\n    _ix_data: Self::IxData,\n) -> Result<(), &'static str> {\n    if let Some(escrow_pre) = pre_ix.escrow {\n        // we can unwrap the receiver account because it\n        // has to be initialized before the instruction\n        // execution and it is not supposed to be closed\n        // after the instruction execution either\n        let receiver = pre_ix.receiver.unwrap();\n        let receiver_lamports_before = receiver.lamports();\n        let receiver_lamports_after = post_ix.receiver.unwrap().lamports();\n\n        if receiver.key() != escrow_pre.receiver.key()\n            && receiver_lamports_before < receiver_lamports_after\n        {\n            return Err(\"Un-authorized withdrawal\");\n        }\n    }\n\n    Ok(())\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p5/","title":"Customize instructions generation","text":"

It is possible to customize how the instructions are generated and which instructions will be executed at the beginning (pre_ixs), in the middle (ixs) and at the end (post_ixs) of each fuzz iteration. This can be useful for example if your program needs an initialization or you want to fuzz some specific program state.

impl FuzzDataBuilder<FuzzInstruction> for MyFuzzData {\n    fn pre_ixs(u: &mut arbitrary::Unstructured) ->\n        arbitrary::Result<Vec<FuzzInstruction>> {\n        let init_ix = FuzzInstruction::Initialize(Initialize::arbitrary(u)?);\n        Ok(vec![init_ix])\n    }\n}\n
"},{"location":"fuzzing/howto/fuzzing-howto-p6/","title":"How to use Custom Data Types","text":"

If you use Custom Types as Instruction data arguments, you may encounter a problem that the Custom Type does not implement

For example:

#[program]\npub mod your_program {\n    pub fn init_vesting(\n            ctx: Context<InitVesting>,\n            recipient: Pubkey,\n            amount: u64,\n            start_at: u64,\n            end_at: u64,\n            interval: u64,\n            // Custom Type input argument\n            _input_variant: CustomEnumInput,\n    ) -> Result<()> {\n        _init_vesting(ctx, recipient, amount, start_at, end_at, interval)\n    }\n}\n\n...\n\n#[derive(AnchorDeserialize, AnchorSerialize)]\npub enum CustomEnumInput {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

Then inside the fuzz_instructions.rs, you may see:

#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n    /// IMPORTANT:\n    /// your_program::CustomEnumInput does not derive\n    /// Arbitrary, nor Debug trait\n    pub _input_variant: your_program::CustomEnumInput,\n}\n

To resolve this issue, you have two options.

"},{"location":"fuzzing/howto/fuzzing-howto-p6/#derive-debug-and-arbitrary-traits-inside-your-program","title":"Derive Debug and Arbitrary traits inside your program","text":"

This option necessitates updating the source code of your on-chain program, which might be undesirable. If you prefer not to modify your program, consider the alternative option provided below.

Inside programs/<YOUR_PROGRAM>/Cargo.toml file include:

...\n[dependencies]\n...\narbitrary = { version = \"1.3.0\", features = [\"derive\"] }\n...\n

and within the program source code:

// derive Debug, Arbitrary\n#[derive(AnchorDeserialize, AnchorSerialize, Debug, Arbitrary)]\npub enum CustomEnumInput {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

"},{"location":"fuzzing/howto/fuzzing-howto-p6/#derive-debug-and-arbitrary-traits-inside-the-fuzz-test","title":"Derive Debug and Arbitrary traits inside the Fuzz Test","text":"

You can redefine the custom type within the fuzz_instructions.rs file, along with all the necessary traits.

// Redefine the Custom Type inside the fuzz_instructions.rs,\n// but this time with all of the required traits.\n#[derive(Arbitrary,Debug, Clone, Copy)]\npub enum CustomEnumInputFuzz {\n    InputVariant1,\n    InputVariant2,\n    InputVariant3,\n}\n

And instead of using the input argument defined within the program, you should modify it as follows:

#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n    /// IMPORTANT:\n    /// redefined Custom Type\n    pub _input_variant: CustomEnumInputFuzz,\n}\n

Then, you would also need to implement the std::convert::From<T> trait to enable conversion between the newly defined Custom Type and the Custom Type used within your program.

// implement std::convert::From to convert between CustomEnumInputFuzz\n// and your_program::CustomEnumInput as these are distinc Data Types.\nimpl std::convert::From<CustomEnumInputFuzz> for your_program::CustomEnumInput {\n    fn from(val: CustomEnumInputFuzz) -> Self {\n        match val {\n            CustomEnumInputFuzz::InputVariant1 => {\n                your_program::CustomEnumInput::InputVariant1\n            }\n            CustomEnumInputFuzz::InputVariant2 => {\n                your_program::CustomEnumInput::InputVariant2\n            }\n            CustomEnumInputFuzz::InputVariant3 => {\n                your_program::CustomEnumInput::InputVariant3\n            }\n        }\n    }\n}\n
Finally, within the get_data function, you can proceed as follows:
impl<'info> IxOps<'info> for InitVestingData {\n    ...\n    fn get_data(\n        &self,\n        _client: &mut impl FuzzClient,\n        _fuzz_accounts: &mut FuzzAccounts,\n    ) -> Result<Self::IxData, FuzzingError> {\n        // cast variable into correct Data Type\n        let variant = self.data._input_variant.into();\n\n        let data = your_program::instruction::InitVestingData {\n        ...\n        _input_variant:variant,\n        ...\n        };\n        Ok(data)\n    }\n    ...\n}\n

"},{"location":"fuzzing/howto/fuzzing-howto-p6/#example","title":"Example","text":"

For a practical example, please refer to the Examples section.

"},{"location":"fuzzing/howto/fuzzing-howto-p7/","title":"How to use Arbitrary crate","text":"

The Arbitrary crate in Rust is used for generating well-typed, structured instances of data from raw byte buffers, making it useful for fuzzing by producing random but structured data for tests.

By implementing the Arbitrary trait for Instruction Data structures, you can guide the fuzzing tool to generate meaningful instances of these structures, thus ensuring a more effective and targeted fuzzing process.

Let`s say your Solana program contains instruction, with a similar logic as the example below:

#[program]\npub mod your_program {\n    pub fn init_vesting(\n        ctx: Context<InitVesting>,\n        recipient: Pubkey,\n        amount: u64,\n        start_at: u64,\n        end_at: u64,\n        interval: u64,\n    ) -> Result<()> {\n        _init_vesting(ctx, recipient, amount, start_at, end_at, interval)\n    }\n}\n...\npub fn _init_vesting(\n    ctx: Context<InitVesting>,\n    recipient: Pubkey,\n    amount: u64,\n    start_at: u64,\n    end_at: u64,\n    interval: u64,\n) -> Result<()> {\n    ...\n    // the Instruction Data arguments are not completely random\n    // and should have the following restrictions\n    require!(amount > 0, VestingError::InvalidAmount);\n    require!(end_at > start_at, VestingError::InvalidTimeRange);\n    require!(end_at - start_at > interval, VestingError::InvalidInterval);\n    require!(interval > 0, VestingError::InvalidInterval);\n    ...\n\n}\n

For this purpose, you can limit the generated Instruction Data structure that is sent to the instruction by the fuzzer, in the following ways

"},{"location":"fuzzing/howto/fuzzing-howto-p7/#customizing-single-fields","title":"Customizing single fields","text":"

You can limit the generated Instruction Input Data by customizing particular fields, Check Customizing single fields for more details.

For the example specified above, with the customization we can limit the particular fields such that

// Instruction Data structure automatically generated\n// inside fuzz_instructions.rs\n...\n#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    // specify the range for amount\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1_000_000)\n    )]\n    pub amount: u64,\n    // specify the range for start_at , this way it will\n    // be always smaller than end_at\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(0..=1_000_000)\n    )]\n    pub start_at: u64,\n    // specify the range for end_at\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured|\n        u.int_in_range(1_001_001..=1_050_000)\n    )]\n    pub end_at: u64,\n    // specify the range for interval\n    #[arbitrary(\n        with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1000)\n    )]\n    pub interval: u64,\n}\n...\n

"},{"location":"fuzzing/howto/fuzzing-howto-p7/#implementing-arbitrary-by-hand","title":"Implementing Arbitrary By Hand","text":"

Alternatively, you can write Arbitrary implementation by yourself.

// for the given example above, this structure is automatically generated\n// with the fuzzer inside fuzz_instructions.rs\n#[derive(Arbitrary, Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n}\n

Now, instead of using an automatically derived Arbitrary trait, you can implement the trait by hand

...\n#[derive(Debug)]\npub struct InitVestingData {\n    pub recipient: AccountId,\n    pub amount: u64,\n    pub start_at: u64,\n    pub end_at: u64,\n    pub interval: u64,\n}\nimpl<'a> Arbitrary<'a> for InitVestingData {\n    fn arbitrary(\n        u: &mut arbitrary::Unstructured<'a>\n    ) -> arbitrary::Result<Self> {\n        // obtain AccountId\n        let recipient = AccountId::arbitrary(u)?;\n\n        // limit the generated amount to the 1_000_000\n        let amount = u.int_in_range(1..=1_000_000)?;\n\n        // now we want to obtain\n        // - start_at\n        // - end_at\n        // - interval\n        // however we want to limit the data such that:\n        // - start_at < end_at\n        // - end_at - start_at > interval\n        // - interval has lower limit of 500 and upper limit of 1000.\n\n        let start_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let end_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;\n        let interval: u64 = u.int_in_range(500..=1000)?;\n\n        // ensure that start_at < end_at\n        if start_at >= end_at {\n            return Err(arbitrary::Error::IncorrectFormat);\n        }\n\n        // ensure that end_at - start_at > interval\n        match end_at.checked_sub(start_at) {\n            Some(diff) => {\n                if diff <= interval {\n                    return Err(arbitrary::Error::IncorrectFormat);\n                }\n            }\n            None => return Err(arbitrary::Error::IncorrectFormat),\n        }\n\n        Ok(InitVestingData {\n            recipient,\n            amount,\n            start_at,\n            end_at,\n            interval,\n        })\n    }\n}\n...\n

"},{"location":"fuzzing/howto/fuzzing-howto-p7/#example","title":"Example","text":"

For a practical example, please refer to the Examples section.

"},{"location":"fuzzing/initialization/fuzzing-initialization-all/","title":"All-Suite","text":"

To initialize Trident and generate all-suite test templates, navigate to your project's root directory and run

trident init both\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 .program_client\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_0 # particular fuzz test\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 accounts_snapshots.rs # generated accounts deserialization methods\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 test_fuzz.rs # the binary target of your fuzz test\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fuzz_instructions.rs # the definition of your fuzz test\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_1\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_X # possible multiple fuzz tests\n\u2502   \u2502   \u251c\u2500\u2500 fuzzing # compilations and crashes folder\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u2502   \u2514\u2500\u2500 poc_tests # integration tests folder\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"fuzzing/initialization/fuzzing-initialization-fuzz-add/","title":"Add new Fuzz Test","text":"

If you have already initialized Trident within your project, and you are interested in initializing a new fuzz test run.

trident fuzz add\n

The command will generate a new fuzz test as follows:

project-root\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_X # new fuzz test folder\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 accounts_snapshots.rs\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 test_fuzz.rs\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fuzz_instructions.rs\n\u2502   \u2502   \u251c\u2500\u2500 fuzzing\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml # already present\n\u251c\u2500\u2500 Trident.toml # already present\n\u2514\u2500\u2500 ...\n

"},{"location":"fuzzing/initialization/fuzzing-initialization-fuzz/","title":"Fuzz test-only","text":"

If you are interested only in generating templates for fuzz tests run

trident init fuzz\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_0 # particular fuzz test\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 accounts_snapshots.rs # generated accounts deserialization methods\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 test_fuzz.rs # the binary target of your fuzz test\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fuzz_instructions.rs # the definition of your fuzz test\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_1\n\u2502   \u2502   \u251c\u2500\u2500 fuzz_X # possible multiple fuzz tests\n\u2502   \u2502   \u251c\u2500\u2500 fuzzing # compilations and crashes folder\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"home/home-installation/","title":"Installation","text":""},{"location":"home/home-installation/#dependencies","title":"Dependencies","text":"

Check supported versions section for further details.

"},{"location":"home/home-installation/#installation_1","title":"Installation","text":"
cargo install trident-cli\n\n# or the specific version\n\ncargo install --version <version> trident-cli\n
"},{"location":"home/home-installation/#supported-versions","title":"Supported versions","text":" Trident CLI Anchor Solana Rust v0.7.0 >=0.29.*1 ^1.17.4 nightly v0.6.0 >=0.29.*1 ^1.17 nightly v0.5.0 ~0.28.* =1.16.6 v0.4.0 ~0.27.* >=1.15 v0.3.0 ~0.25.* >=1.10 v0.2.0 ~0.24.* >=1.9
  1. To use Trident with Anchor 0.29.0, run the following commands from your project's root directory after Trident initialization:
    cargo update anchor-client@0.30.0 --precise 0.29.0\ncargo update anchor-spl@0.30.0 --precise 0.29.0\n
"},{"location":"integration-tests/poc-examples/","title":"Examples","text":""},{"location":"integration-tests/poc-introduction/","title":"Introduction","text":"

Trident supports writing Integration Tests in Rust.

marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tr marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tri marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trid marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ tride marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ triden marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident t marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident te marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident tes marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test marcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ trident test Finished test [unoptimized + debuginfo] target(s) in 0.17s Running unittests src/lib.rs (target/debug/deps/program_client-35df7129a7acbd48)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Running tests/test.rs (target/debug/deps/test-e1d41610f5897c1f)running 2 tests-------------- TEST START --------------program_keypair(1).pubkey = Po1RaS8BEDbNcn5oXsFryAeQ6Wn8fvmE111DJaKCgPCWaiting for fees to stabilize 1...test test_unhappy_path ... okWaiting for fees to stabilize 2...test test_happy_path ... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.89s Running unittests src/lib.rs (target/debug/deps/turnstile-451803623b25a701)running 1 testtest test_id ... oktest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s Doc-tests program_clientmarcinv@XPS15:~/Work/tooling/trident/examples/turnstile$ exit"},{"location":"integration-tests/poc-run/","title":"Run","text":"

Once you have finished the implementation of the Integration Test, you can run the Test as follows:

trident test\n
"},{"location":"integration-tests/poc-run/#skipping-tests","title":"Skipping tests","text":"
#[trident_test]\n#[ignore]\nasync fn test() {}\n
"},{"location":"integration-tests/howto/poc-howto-p0/","title":"Init Fixture","text":"
// <my_project>/trident-tests/poc_tests/tests/test.rs\n// TODO: do not forget to add all necessary dependencies to the generated `trident-tests/poc_tests/Cargo.toml`\nuse program_client::my_instruction;\nuse trident_client::*;\nuse my_program;\n\n#[throws]\n#[fixture]\nasync fn init_fixture() -> Fixture {\n  // create a test fixture\n  let mut fixture = Fixture {\n    client: Client::new(system_keypair(0)),\n    // make sure to pass the correct name of your program\n    program: anchor_keypair(\"my_program_name\").unwrap(),\n    state: keypair(42),\n  };\n  // deploy the program to test\n  fixture.deploy().await?;\n  // call instruction init\n  my_instruction::initialize(\n    &fixture.client,\n    fixture.state.pubkey(),\n    fixture.client.payer().pubkey(),\n    System::id(),\n    Some(fixture.state.clone()),\n  ).await?;\n  fixture\n}\n\n#[trident_test]\nasync fn test_happy_path(#[future] init_fixture: Result<Fixture>) {\n  let fixture = init_fixture.await?;\n  // call the instruction\n  my_instruction::do_something(\n    &fixture.client,\n    \"dummy_string\".to_owned(),\n    fixture.state.pubkey(),\n    None,\n  ).await?;\n  // check the test result\n  let state = fixture.get_state().await?;\n  assert_eq!(state.something_changed, \"yes\");\n}\n
"},{"location":"integration-tests/howto/poc-howto-p1/","title":"Testing programs with associated token accounts","text":"
# <my-project>/trident-tests/poc_tests/Cargo.toml\n# import the correct versions manually\nanchor-spl = \"0.29.0\"\nspl-associated-token-account = \"2.0.0\"\n
// <my-project>/trident-tests/poc_tests/tests/test.rs\nuse anchor_spl::token::Token;\nuse spl_associated_token_account;\n\nasync fn init_fixture() -> Fixture {\n  // ...\n  let account = keypair(1);\n  let mint = keypair(2);\n  // constructs a token mint\n  client\n    .create_token_mint(&mint, mint.pubkey(), None, 0)\n    .await?;\n  // constructs associated token account\n  let token_account = client\n    .create_associated_token_account(&account, mint.pubkey())\n    .await?;\n  let associated_token_program = spl_associated_token_account::id();\n  // derives the associated token account address for the given wallet and mint\n  let associated_token_address = spl_associated_token_account::get_associated_token_address(&account.pubkey(), mint);\n  Fixture {\n    // ...\n    token_program: Token::id(),\n  }\n}\n
"},{"location":"integration-tests/initialization/poc-initialization-all/","title":"All-Suite","text":"

To initialize Trident and generate all-suite test templates, navigate to your project's root directory and run

trident init both\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 .program_client\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 fuzz_tests # fuzz tests folder\n\u2502   \u251c\u2500\u2500 poc_tests # integration tests folder\n\u2502   \u2502   \u251c\u2500\u2500 tests\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 test.rs # Integration Tests implementation\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"integration-tests/initialization/poc-initialization-poc/","title":"Integration test-only","text":"

If you are interested only in generating templates for Integration Tests run

trident init poc\n

The command will generate the following folder structure:

project-root\n\u251c\u2500\u2500 .program_client\n\u251c\u2500\u2500 trident-tests\n\u2502   \u251c\u2500\u2500 poc_tests # integration tests folder\n\u2502   \u2502   \u251c\u2500\u2500 tests\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 test.rs\n\u2502   \u2502   \u2514\u2500\u2500 Cargo.toml\n\u251c\u2500\u2500 Trident.toml\n\u2514\u2500\u2500 ...\n

"},{"location":"integration-tests/initialization/poc-initialization-program-client/","title":"Program Client","text":"

By default, Integration Tests initialization generates also a .program_client crate with all necessary implementation for Instructions.

If you are interested in updating the .program_client implementation due to an update inside your program, run

trident build\n

This command will also initialize .program_client if the crate does not exist yet.

"}]} \ No newline at end of file diff --git a/dev/sitemap.xml b/dev/sitemap.xml index 9066efa9..abbde274 100644 --- a/dev/sitemap.xml +++ b/dev/sitemap.xml @@ -2,137 +2,137 @@ https://ackee.xyz/trident/docs/dev/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/CHANGELOG/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/fuzzing-current-limitations/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/fuzzing-examples/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/fuzzing-introduction/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/fuzzing-lifecycle/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/fuzzing-run-debug/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p0/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p1/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p2/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p3/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p4/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p5/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p6/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/howto/fuzzing-howto-p7/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/initialization/fuzzing-initialization-all/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/initialization/fuzzing-initialization-fuzz-add/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/fuzzing/initialization/fuzzing-initialization-fuzz/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/home/home-installation/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/poc-examples/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/poc-introduction/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/poc-run/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/howto/poc-howto-p0/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/howto/poc-howto-p1/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/initialization/poc-initialization-all/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/initialization/poc-initialization-poc/ - 2024-08-14 + 2024-08-16 daily https://ackee.xyz/trident/docs/dev/integration-tests/initialization/poc-initialization-program-client/ - 2024-08-14 + 2024-08-16 daily \ No newline at end of file diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz index 2fb287fd9bfbf6396f13ed50886780aa3b0d2e59..f899750f6eb95c7495f8b9b1d75ded5d09f3e7ec 100644 GIT binary patch literal 440 zcmV;p0Z0BHiwFpSmcC{J|8r?{Wo=<_E_iKh0Ns{dPs1P-hVS<)n%+~osq-UUjWIee zjNa%EKsalO6e>{n@$04STpHtrSz@v?E=oB$oIEeI@MhPmN=2VwoiutDkCQZxKyxFc zE@tuT%RRY>uk%sXSmnS+Az8awTzX$$QMz0%6DK`Xtagd91$Dd(14p+(nkJWVK8iwX z3-oVY%aq}HTg8PkjK2dUtM!I@D+Opzh2f40_@o(a6NmGd^OX_#B%Pd+^n#41A!QpI zr16sJ0u~lNa>1lpXLJzhf#{BCPqy2Ko2U8R6s6^ z?ojc=idD4&T+mW`Yed7_JaaaNg0{a2(eDcXTuc|9AXgn2}NU_d?5#!Z80qz!;f;AE#&k7ofV@ i_R$nV$EV4fFr^|~$+p(N%j-G^KK}xEEwIFW6aWBb)Y=aK literal 440 zcmV;p0Z0BHiwFpS^SfpO|8r?{Wo=<_E_iKh0Ns{dPs1P-hVT6qOYbS&oH|X{#TY*> zjNa%EKsalO6e`et{d#FTm&WYEEHT*`7p0sWPM#N9c+=UcRD%UrCyky)!#Ifspt%uJ z=hNu@?SV|9S$3AzRypv4kgVM_D!i|5C|xd>v6CK3R=L>NoH|~Ffuq|XP2%e)I~#=7 z6zJaCmMX)uriu$?820u%Ibz68rO*^Q95lC>f1OG9klpNZH1E zX}n-Mhq;BXOfad|DeXl%Ai5(uknQg2_T};Z`StOrDd+WO6ZZ0_C%m>cBoHM_>6s6^ z?ojc=ij|cDT+mW`YedbPhY3^agw!-)!h|?6Nk=WGFeS8)K<<3_ zp4idb%;1M1x83@BaNg0%a2(ctKROzNzaKvsW@Oaez0k0uFwB1tFhnNc*Xh~)1*opJ iLo|iZ>1nbiOeqOhvZ?j&^4iXU&%Oc51g92#6aWA`vEQ};