Skip to content

Commit

Permalink
chore: add dynamic priority fees to forester (#1481)
Browse files Browse the repository at this point in the history
* use elements from helius-sdk for send logic

rm helius error

rm unused code

add get_priority_fee_estimate

wip

fmt, use forester_epoch_derivation pubkey

10_000 localhost prio fee response

remove deadcode

* add debug stack trace to forester tests

* add light slot timeout, optimze configs

* adjust intervals

* extend test for capped-prio-fee

* clean

* add doc

* add doc
  • Loading branch information
SwenSchaeferjohann authored Jan 17, 2025
1 parent 79321b3 commit 8b38899
Show file tree
Hide file tree
Showing 16 changed files with 834 additions and 112 deletions.
36 changes: 29 additions & 7 deletions .github/workflows/forester-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,40 @@ concurrency:
cancel-in-progress: true

env:
RUST_BACKTRACE: "1"
RUSTFLAGS: "--cfg tokio_unstable -D warnings"

jobs:
test:
strategy:
matrix:
test-name: [
{name: "address-batched", command: "test_address_batched", timeout: 60, needs-test-program: true},
{name: "state-batched", command: "test_state_batched", timeout: 60, needs-test-program: false},
{name: "2-foresters", command: "test_epoch_monitor_with_2_foresters", timeout: 60, needs-test-program: false},
{name: "double-registration", command: "test_epoch_double_registration", timeout: 60, needs-test-program: false}
]
test-name:
[
{
name: "address-batched",
command: "test_address_batched",
timeout: 60,
needs-test-program: true,
},
{
name: "state-batched",
command: "test_state_batched",
timeout: 60,
needs-test-program: false,
},
{
name: "2-foresters",
command: "test_epoch_monitor_with_2_foresters",
timeout: 60,
needs-test-program: false,
},
{
name: "double-registration",
command: "test_epoch_double_registration",
timeout: 60,
needs-test-program: false,
},
]
name: test-${{ matrix.test-name.name }}
runs-on: ubuntu-latest
timeout-minutes: ${{ matrix.test-name.timeout }}
Expand Down Expand Up @@ -63,4 +85,4 @@ jobs:
- name: Run ${{ matrix.test-name.name }} tests
run: |
source ./scripts/devenv.sh
cargo test --package forester ${{ matrix.test-name.command }} -- --nocapture
cargo test --package forester ${{ matrix.test-name.command }} -- --nocapture
58 changes: 58 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ solana-cli-output = "=1.18.22"
solana-transaction-status = "=1.18.22"
solana-account-decoder = "=1.18.22"
solana-rpc = "=1.18.22"
solana-rpc-client-api = "=1.18.22"
spl-token = "=4.0.0"
spl-token-2022 = {version="3.0.5", no-default-features = true, features = ["no-entrypoint"]}

Expand Down
7 changes: 7 additions & 0 deletions forester/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ light-client = { workspace = true }
light-merkle-tree-metadata = { workspace = true }
light-sdk = { workspace = true }
light-program-test = { workspace = true}
solana-transaction-status = { workspace = true }
bincode = "1.3"
url = "2.2"
tokio-tungstenite = "0.16"
bb8 = { workspace = true }

serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
Expand All @@ -48,3 +54,4 @@ serial_test = "3.2.0"
light-prover-client = { workspace = true }
light-test-utils = { workspace = true }
light-program-test = { workspace = true, features = ["devenv"] }
dotenvy = "0.15"
23 changes: 11 additions & 12 deletions forester/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,35 @@ It subscribes to the nullifier queue and nullifies merkle tree leaves.
## Configuration

Forester requires a configuration file, `forester.toml`, specifying necessary keys:

- `STATE_MERKLE_TREE_PUBKEY`: Address of the State Merkle tree.
- `NULLIFIER_QUEUE_PUBKEY`: Address of the State Nullifier queue.
- `ADDRESS_MERKLE_TREE_PUBKEY`: Address of the Address Merkle tree.
- `ADDRESS_MERKLE_TREE_QUEUE_PUBKEY`: Address of the Address queue.
- `REGISTRY_PUBKEY`: Address of the Registry program.

To setup your environment properly, copy `.env.example` to `.env`
and update the `FORESTER_PAYER` field with your appropriate key.

To setup your environment properly, copy `.env.example` to `.env`
and update the `FORESTER_PAYER` field with your appropriate key.

Alternatively, if you prefer to use a terminal profile file,
add the key to your `~/.zshrc` (zsh) or `~/.bashrc` (bash)
Alternatively, if you prefer to use a terminal profile file,
add the key to your `~/.zshrc` (zsh) or `~/.bashrc` (bash)
by including this line: `export FORESTER_PAYER=your_value_here`.
Substitute `your_value_here` with your actual key.
Substitute `your_value_here` with your actual key.

Remember to restart your terminal or source your terminal profile for the changes to take effect.

## Usage

1. Run the service:
To subscribe to nullify the state merkle tree, use the following command:
`cargo run -- subscribe`
To subscribe to nullify the state merkle tree, use the following command:
`cargo run -- subscribe`
2. To manually nullify state merkle tree leaves, use the following command:
`cargo run -- nullify-state`
`cargo run -- nullify-state`
3. To manually nullify address merkle tree leaves, use the following command:
`cargo run -- nullify-addresses`
4. To manually nullify state *and* address merkle tree leaves, use the following command:
`cargo run -- nullify-addresses`
4. To manually nullify state _and_ address merkle tree leaves, use the following command:
`cargo run -- nullify`


## TODO

1. Add indexer URL to the configuration file.
Expand Down
17 changes: 13 additions & 4 deletions forester/src/epoch_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@ impl<R: RpcConnection, I: Indexer<R> + IndexerType<R>> EpochManager<R, I> {
)
.await?;

// light slot length in s
let light_slot_timeout = {
let slot_length_u32 = u32::try_from(epoch_pda.protocol_config.slot_length)
.map_err(|_| ConfigurationError::SlotLengthOverflow {
Expand Down Expand Up @@ -907,14 +908,21 @@ impl<R: RpcConnection, I: Indexer<R> + IndexerType<R>> EpochManager<R, I> {
}
}
} else {
// TODO: measure accuracy
// Optional replace with shutdown signal for all child processes
// TODO: measure accuracy Optional replace with shutdown
// signal for all child processes
//
// Note: as of now, this executes all batches sequentially:
// a single batch must fully complete before the next batch
// is sent. We can either limit num_batches to 1 and
// increase batch_size (quick fix) and require another
// rate-limiting mechanism (with more control). Or rework
// the send logic to not await confirmations.
let batched_tx_config = SendBatchedTransactionsConfig {
num_batches: 10,
build_transaction_batch_config: BuildTransactionBatchConfig {
batch_size: 50, // TODO: make batch size configurable and or dynamic based on queue usage
compute_unit_price: None, // Make dynamic based on queue usage
compute_unit_limit: Some(1_000_000),
compute_unit_price: Some(10_000), // Is dynamic. Sets max.
compute_unit_limit: Some(180_000),
},
queue_config: self.config.queue_config,
retry_config: RetryConfig {
Expand All @@ -932,6 +940,7 @@ impl<R: RpcConnection, I: Indexer<R> + IndexerType<R>> EpochManager<R, I> {
};

debug!("Sending transactions...");
// sequential
let start_time = Instant::now();
let batch_tx_future = send_batched_transactions(
&self.config.payer_keypair,
Expand Down
74 changes: 74 additions & 0 deletions forester/src/helius_priority_fee_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// adapted from https://github.com/helius-labs/helius-rust-sdk/blob/dev/src/types/types.rs
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub enum PriorityLevel {
Min,
Low,
Medium,
High,
VeryHigh,
UnsafeMax,
Default,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum UiTransactionEncoding {
Binary,
Base64,
Base58,
Json,
JsonParsed,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct RpcRequest<T> {
pub jsonrpc: String,
pub id: String,
pub method: String,
#[serde(rename = "params")]
pub parameters: T,
}

impl<T> RpcRequest<T> {
pub fn new(method: String, parameters: T) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: "1".to_string(),
method,
parameters,
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
pub struct RpcResponse<T> {
pub jsonrpc: String,
pub id: String,
pub result: T,
}

#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct GetPriorityFeeEstimateOptions {
pub priority_level: Option<PriorityLevel>,
pub include_all_priority_fee_levels: Option<bool>,
pub transaction_encoding: Option<UiTransactionEncoding>,
pub lookback_slots: Option<u8>,
pub recommended: Option<bool>,
pub include_vote: Option<bool>,
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct GetPriorityFeeEstimateRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub transaction: Option<String>,
#[serde(rename = "accountKeys", skip_serializing_if = "Option::is_none")]
pub account_keys: Option<Vec<String>>,
pub options: Option<GetPriorityFeeEstimateOptions>,
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct GetPriorityFeeEstimateResponse {
#[serde(rename = "priorityFeeEstimate")]
pub priority_fee_estimate: Option<f64>,
}
2 changes: 2 additions & 0 deletions forester/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod config;
pub mod epoch_manager;
pub mod errors;
pub mod forester_status;
pub mod helius_priority_fee_types;
mod indexer_type;
pub mod metrics;
pub mod pagerduty;
Expand All @@ -15,6 +16,7 @@ pub mod queue_helpers;
pub mod rollover;
pub mod send_transaction;
mod slot_tracker;
pub mod smart_transaction;
pub mod telemetry;
pub mod tree_data_sync;
pub mod tree_finder;
Expand Down
Loading

0 comments on commit 8b38899

Please sign in to comment.