Skip to content

Commit

Permalink
📝 Update documentation, removed integration tests and better describe…
Browse files Browse the repository at this point in the history
… Trident Features
  • Loading branch information
lukacan committed Sep 20, 2024
1 parent f62e372 commit 3053af6
Show file tree
Hide file tree
Showing 36 changed files with 1,261 additions and 1,113 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ incremented upon a breaking change and the patch version will be incremented for
- impr/ instead of parsing source code and creating our IDL, read anchor IDL ([198](https://github.com/Ackee-Blockchain/trident/pull/196))

**Removed**

- del/remove integration tests supported by Trident, this feature adds more unnecessary overhead compared to its value ([196](https://github.com/Ackee-Blockchain/trident/pull/198))

## [0.7.0] - 2024-08-14
Expand Down
51 changes: 51 additions & 0 deletions documentation/docs/commands/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
hide:
- navigation
---

---


## trident init

- This command Initializes Trident Workspace and generates new Fuzz Test Template.

- The command will generate the following folder structure:
```bash
project-root
├── trident-tests
│ ├── fuzz_tests # fuzz tests folder
│ │ ├── fuzz_0 # particular fuzz test
│ │ │ ├── test_fuzz.rs # the binary target of your fuzz test
│ │ │ └── fuzz_instructions.rs # the definition of your fuzz test
│ │ ├── fuzz_1
│ │ ├── fuzz_X # possible multiple fuzz tests
│ │ ├── fuzzing # compilations and crashes folder
│ │ └── Cargo.toml
├── Trident.toml
└── ...
```

---

## trident fuzz

- This command behavior depends on the subcommands.

### trident fuzz run

- Run Fuzzer on the specified Fuzz Target (i.e. the Fuzz Template, for example fuzz_0).

### trident fuzz run-debug

- Run debug on the specified Fuzz Target (i.e. the Fuzz Template, for example fuzz_0), with specified crash file, to see where the crash file found an issue.

### trident fuzz add

- Adds new Fuzz Test Template.

---

## trident clean

- Calls `anchor clean` and cleans targets created by the underlying Honggfuzz. Crashfiles and Fuzzing Inputs are preserved.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
hide:
- navigation
- toc
---

Expand Down
37 changes: 37 additions & 0 deletions documentation/docs/faq/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
hide:
- navigation
---

# FAQ

### Is Trident supported only with Anchor ?

- Currently yes, Trident under the hood obtains data from the IDL generated by Anchor and it has to have access to the AccountsSnapshots derived for each Instruction Context.


### I created the Fuzz Test what should I do next ?

- Start here [Writing Fuzz Tests](../writing-fuzz-test/writing-fuzz-test.md). For additional features check [Features](../features/features.md). If you are not sure about anything check [Get Help](../get-help/get-help.md)


### My program Instruction contains custom type such as Struct or Enum on its input, but it does not derive Arbitrary.

- In this case you need to specify same type in the Fuzz Test (with the same fields). And implement From Trait to convert to your type. Check [Custom Data Types](../features/arbitrary-data.md/#custom-data-types) or [Examples of Arbitrary](../examples/examples.md).


### Is Trident open-source ?

- Yes, here [Trident](https://github.com/Ackee-Blockchain/trident)

### I would like to report Issue with Trident, what should I do ?

- Write Issue [Issues](https://github.com/Ackee-Blockchain/trident/issues)

### Is Trident deployed on Mainnet / Devnet / Testenet ?

- No, Trident is Fuzz Testing Framework, not Solana Program.

### What type of Fuzzer Trident is ?

- Currently, we refer to it as *"coverage guided gray box fuzzer"*.
53 changes: 53 additions & 0 deletions documentation/docs/features/account-storages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Account Storages


Trident allows developers to generate random accounts for fuzzing.

However, the Accounts are not completely random, and neither are the Account addresses.

Instead, Trident generates random **AccountIDs** which are indexes to **Account Storages**. Each unique Account contained within the Anchor generated IDL has its own AccountStorage. The FuzzAccounts containing the Accounts Storages is global to all Instructions to use.


??? note

**Details:**

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.

!!! important

Currently, supported types of Account Storages:

- Signer
- PDA
- Token Account
- Program account

Then use the corresponding AccountsStorage.

```rust
pub struct FuzzAccounts {
signer: AccountsStorage<Keypair>,
some_pda: AccountsStorage<PdaStore>,
token_vault: AccountsStorage<TokenStore>,
mint: AccountsStorage<MintStore>,
// ...
}
```

!!! tip

Keep in mind:

- You do not need to specify every `AccountStorage`, some accounts do not necessarily need to be stored in their corresponding storage.
- For example `System Program` does not need to be stored, rather can be used from the `solana_sdk`.
- If you are about to Initialize `Mint` or `Token Account` in your Solana Program.
- use `Keypair` or `PdaStore` (not `MintStore` or `TokenStore`).
- If you are going to initialize `Associated Token Account` in your Solana Program.
- use `PdaStore`.
- You can rename fields of `FuzzAccounts` to whatever you want. The default names are generated based on the Program's `IDL`.


!!! tip

Consider checking the [Examples](../examples/examples.md) section for more tips.
121 changes: 121 additions & 0 deletions documentation/docs/features/arbitrary-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Arbitrary Data


Trident allows you to customize Instruction Data to provide structure.

For example your Initialize Instruction expects two arguments `start_at` and `end_at` you know that in order for the Instruction to make sense, it is required that the `start_at` < `end_at`. Moreover, there should be significant difference between these two. This can be utilized with the **Arbitrary crate**.


```rust
#[derive(Arbitrary, Debug)]
pub struct InitVestingData {
pub recipient: AccountId,
#[arbitrary(
with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1_000_000)
)]
pub amount: u64,
// we want start_at smaller than end_at
// and for testing purposes we can run tests with times from the past
#[arbitrary(
with = |u: &mut arbitrary::Unstructured| u.int_in_range(0..=1_000_000)
)]
pub start_at: u64,
#[arbitrary(
with = |u: &mut arbitrary::Unstructured| u.int_in_range(1_001_001..=1_050_000)
)]
pub end_at: u64,
#[arbitrary(
with = |u: &mut arbitrary::Unstructured| u.int_in_range(1..=1000)
)]
pub interval: u64,
}
```

## Implement Arbitrary

There are macros available to use with Arbitrary, however, it is possible to Implement the arbitrary function by yourself.


```rust
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Implement Arbitrary
impl<'a> Arbitrary<'a> for InitVestingData {
fn arbitrary(
u: &mut arbitrary::Unstructured<'a>
) -> arbitrary::Result<Self> {
// obtain AccountId
let recipient = AccountId::arbitrary(u)?;

// limit the generated amount to the 1_000_000
let amount = u.int_in_range(1..=1_000_000)?;

// now we want to obtain
// - start_at
// - end_at
// - interval
// however we want to limit the data such that:
// - start_at < end_at
// - end_at - start_at > interval
// - interval has lower limit of 500 and upper limit of 1000.

let start_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;
let end_at: u64 = u.int_in_range(1_000_000..=5_000_000)?;
let interval: u64 = u.int_in_range(500..=1000)?;

// ensure that start_at < end_at
if start_at >= end_at {
return Err(arbitrary::Error::IncorrectFormat);
}

// ensure that end_at - start_at > interval
match end_at.checked_sub(start_at) {
Some(diff) => {
if diff <= interval {
return Err(arbitrary::Error::IncorrectFormat);
}
}
None => return Err(arbitrary::Error::IncorrectFormat),
}

Ok(InitVestingData {
recipient,
amount,
start_at,
end_at,
interval,
})
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
}
```

## Custom Data Types

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

- [Debug](https://doc.rust-lang.org/std/fmt/trait.Debug.html) trait
- [Arbitrary](https://docs.rs/arbitrary/latest/arbitrary/trait.Arbitrary.html) trait

### Derive Debug and Arbitrary traits inside the Fuzz Test

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

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


Then, you would also need to implement the [`std::convert::From<T>`](https://doc.rust-lang.org/std/convert/trait.From.html) trait to enable conversion between the newly defined Custom Type and the Custom Type used within your program.

!!! tip

Consider checking the [Examples](../examples/examples.md) section for more tips.
38 changes: 38 additions & 0 deletions documentation/docs/features/error-handlers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Error Handler

Trident allows you to specify custom error handler for each Instruction.

This can be particularly helpful:

- If Transaction returns Error, you can specify to omit this error and continue with the fuzzing instruction.
- Using the `tx_error_handler` you can check if the error returned is desired based on the Accounts and Input data that were used.

!!! tip

The default behavior of the function is that the error is returned.

```rust
/// default implementation
fn tx_error_handler(
&self,
e: FuzzClientErrorWithOrigin,
ix_data: Self::IxData,
pre_ix_acc_infos: &'info mut [Option<AccountInfo<'info>>],
) -> Result<(), FuzzClientErrorWithOrigin> {
Err(e)
}
```

To omit the Error and continue with the next Instruction in the iteration, you can do

```rust
/// default implementation
fn tx_error_handler(
&self,
e: FuzzClientErrorWithOrigin,
ix_data: Self::IxData,
pre_ix_acc_infos: &'info mut [Option<AccountInfo<'info>>],
) -> Result<(), FuzzClientErrorWithOrigin> {
Ok(())
}
```
Loading

0 comments on commit 3053af6

Please sign in to comment.