Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Run linters
run: |
set -euxo pipefail
RUSTFMT_NIGHTLY_TOOLCHAIN=nightly-2025-10-26
RUSTFMT_NIGHTLY_TOOLCHAIN=nightly
rustup install "$RUSTFMT_NIGHTLY_TOOLCHAIN"
rustup target add x86_64-unknown-linux-gnu --toolchain "$RUSTFMT_NIGHTLY_TOOLCHAIN"
rustup component add --toolchain "$RUSTFMT_NIGHTLY_TOOLCHAIN" rustfmt
Expand Down
40 changes: 22 additions & 18 deletions forester/src/forester_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,20 +670,22 @@ fn parse_tree_status(
let fullness = next_index as f64 / capacity as f64 * 100.0;

let (queue_len, queue_cap) = queue_account
.map(|acc| {
unsafe { parse_hash_set_from_bytes::<QueueAccount>(&acc.data) }
.ok()
.map(|hs| {
.map(
|acc| match unsafe { parse_hash_set_from_bytes::<QueueAccount>(&acc.data) } {
Ok(hs) => {
let len = hs
.iter()
.filter(|(_, cell)| cell.sequence_number.is_none())
.count() as u64;
let cap = hs.get_capacity() as u64;
(len, cap)
})
.unwrap_or((0, 0))
})
.map(|(l, c)| (Some(l), Some(c)))
(Some(len), Some(cap))
}
Err(error) => {
warn!(?error, "Failed to parse StateV1 queue hash set");
(None, None)
}
},
)
.unwrap_or((None, None));

(
Expand Down Expand Up @@ -725,20 +727,22 @@ fn parse_tree_status(
let fullness = next_index as f64 / capacity as f64 * 100.0;

let (queue_len, queue_cap) = queue_account
.map(|acc| {
unsafe { parse_hash_set_from_bytes::<QueueAccount>(&acc.data) }
.ok()
.map(|hs| {
.map(
|acc| match unsafe { parse_hash_set_from_bytes::<QueueAccount>(&acc.data) } {
Ok(hs) => {
let len = hs
.iter()
.filter(|(_, cell)| cell.sequence_number.is_none())
.count() as u64;
let cap = hs.get_capacity() as u64;
(len, cap)
})
.unwrap_or((0, 0))
})
.map(|(l, c)| (Some(l), Some(c)))
(Some(len), Some(cap))
}
Err(error) => {
warn!(?error, "Failed to parse AddressV1 queue hash set");
(None, None)
}
},
)
.unwrap_or((None, None));

(
Expand Down
42 changes: 41 additions & 1 deletion forester/src/processor/v2/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,47 @@ impl StreamingAddressQueue {

let addresses = data.addresses[start..actual_end].to_vec();
if addresses.is_empty() {
return Err(anyhow!("Empty batch at start={}", start));
return Ok(None);
}
let expected_len = addresses.len();
let Some(low_element_values) = data
.low_element_values
.get(start..end)
.map(|slice| slice.to_vec())
else {
return Ok(None);
};
let Some(low_element_next_values) = data
.low_element_next_values
.get(start..end)
.map(|slice| slice.to_vec())
else {
return Ok(None);
};
let Some(low_element_indices) = data
.low_element_indices
.get(start..end)
.map(|slice| slice.to_vec())
else {
return Ok(None);
};
let Some(low_element_next_indices) = data
.low_element_next_indices
.get(start..end)
.map(|slice| slice.to_vec())
else {
return Ok(None);
};
if [
low_element_values.len(),
low_element_next_values.len(),
low_element_indices.len(),
low_element_next_indices.len(),
]
.iter()
.any(|&len| len != expected_len)
{
return Ok(None);
}

let leaves_hashchain = match data.leaves_hash_chains.get(hashchain_idx).copied() {
Expand Down
23 changes: 11 additions & 12 deletions forester/src/processor/v2/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ where
self.proof_cache = Some(cache);
}

fn ensure_worker_pool(&mut self) -> crate::Result<()> {
if self.worker_pool.is_none() {
let job_tx = spawn_proof_workers(&self.context.prover_config)?;
self.worker_pool = Some(WorkerPool { job_tx });
}
Ok(())
}

pub async fn process(&mut self) -> std::result::Result<ProcessingResult, ForesterError> {
let queue_size = self.zkp_batch_size * self.context.max_batches_per_tree as u64;
self.process_queue_update(queue_size).await
Expand All @@ -131,10 +139,7 @@ where
return Ok(ProcessingResult::default());
}

if self.worker_pool.is_none() {
let job_tx = spawn_proof_workers(&self.context.prover_config);
self.worker_pool = Some(WorkerPool { job_tx });
}
self.ensure_worker_pool()?;

if let Some(cached) = self.cached_state.take() {
let actual_available = self
Expand Down Expand Up @@ -531,10 +536,7 @@ where
let max_batches =
((queue_size / self.zkp_batch_size) as usize).min(self.context.max_batches_per_tree);

if self.worker_pool.is_none() {
let job_tx = spawn_proof_workers(&self.context.prover_config);
self.worker_pool = Some(WorkerPool { job_tx });
}
self.ensure_worker_pool()?;

let queue_data = match self
.strategy
Expand All @@ -560,10 +562,7 @@ where

let max_batches = max_batches.min(self.context.max_batches_per_tree);

if self.worker_pool.is_none() {
let job_tx = spawn_proof_workers(&self.context.prover_config);
self.worker_pool = Some(WorkerPool { job_tx });
}
self.ensure_worker_pool()?;

let queue_data = match self
.strategy
Expand Down
20 changes: 11 additions & 9 deletions forester/src/processor/v2/proof_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,27 +132,27 @@ struct ProofClients {
}

impl ProofClients {
fn new(config: &ProverConfig) -> Self {
Self {
fn new(config: &ProverConfig) -> crate::Result<Self> {
Ok(Self {
append_client: ProofClient::with_config(
config.append_url.clone(),
config.polling_interval,
config.max_wait_time,
config.api_key.clone(),
),
)?,
nullify_client: ProofClient::with_config(
config.update_url.clone(),
config.polling_interval,
config.max_wait_time,
config.api_key.clone(),
),
)?,
address_append_client: ProofClient::with_config(
config.address_append_url.clone(),
config.polling_interval,
config.max_wait_time,
config.api_key.clone(),
),
}
)?,
})
}

fn get_client(&self, input: &ProofInput) -> &ProofClient {
Expand All @@ -164,11 +164,13 @@ impl ProofClients {
}
}

pub fn spawn_proof_workers(config: &ProverConfig) -> async_channel::Sender<ProofJob> {
pub fn spawn_proof_workers(
config: &ProverConfig,
) -> crate::Result<async_channel::Sender<ProofJob>> {
let (job_tx, job_rx) = async_channel::bounded::<ProofJob>(256);
let clients = Arc::new(ProofClients::new(config));
let clients = Arc::new(ProofClients::new(config)?);
tokio::spawn(async move { run_proof_pipeline(job_rx, clients).await });
job_tx
Ok(job_tx)
}

async fn run_proof_pipeline(
Expand Down
17 changes: 11 additions & 6 deletions js/token-interface/src/instructions/mint-to-compressed.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
SystemProgram,
TransactionInstruction,
} from '@solana/web3.js';
import { SystemProgram, TransactionInstruction } from '@solana/web3.js';
import { Buffer } from 'buffer';
import {
LIGHT_TOKEN_PROGRAM_ID,
Expand Down Expand Up @@ -125,9 +122,17 @@ export function createMintToCompressedInstruction({
isSigner: false,
isWritable: false,
},
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
},
{ pubkey: outputQueue, isSigner: false, isWritable: true },
{ pubkey: merkleContext.treeInfo.tree, isSigner: false, isWritable: true },
{
pubkey: merkleContext.treeInfo.tree,
isSigner: false,
isWritable: true,
},
{
pubkey: merkleContext.treeInfo.queue,
isSigner: false,
Expand Down
6 changes: 5 additions & 1 deletion js/token-interface/tests/e2e/ata-read.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { describe, expect, it } from 'vitest';
import { newAccountWithLamports } from '@lightprotocol/stateless.js';
import { createAtaInstructions, getAta, getAssociatedTokenAddress } from '../../src';
import {
createAtaInstructions,
getAta,
getAssociatedTokenAddress,
} from '../../src';
import { createMintFixture, sendInstructions } from './helpers';

describe('ata creation and reads', () => {
Expand Down
14 changes: 12 additions & 2 deletions js/token-interface/tests/e2e/mint-to-compressed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ describe('mint-to-compressed instruction', () => {
[COMPRESSED_MINT_SEED, mintSigner.publicKey.toBuffer()],
LIGHT_TOKEN_PROGRAM_ID,
);
const mintInfo = await getMint(rpc, mint, undefined, LIGHT_TOKEN_PROGRAM_ID);
const mintInfo = await getMint(
rpc,
mint,
undefined,
LIGHT_TOKEN_PROGRAM_ID,
);
if (!mintInfo.merkleContext || !mintInfo.mintContext) {
throw new Error('Light mint context missing.');
}
Expand Down Expand Up @@ -103,7 +108,12 @@ describe('mint-to-compressed instruction', () => {
recipientB.publicKey,
{ mint },
);
const mintAfter = await getMint(rpc, mint, undefined, LIGHT_TOKEN_PROGRAM_ID);
const mintAfter = await getMint(
rpc,
mint,
undefined,
LIGHT_TOKEN_PROGRAM_ID,
);

const amountA = aAccounts.items.reduce(
(sum, account) => sum + BigInt(account.parsed.amount.toString()),
Expand Down
4 changes: 3 additions & 1 deletion js/token-interface/tests/unit/instruction-builders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,9 @@ describe('instruction builders', () => {
mintSigner: Keypair.generate().publicKey.toBytes(),
bump: 255,
},
recipients: [{ recipient: Keypair.generate().publicKey, amount: 42n }],
recipients: [
{ recipient: Keypair.generate().publicKey, amount: 42n },
],
});

expect(instruction.programId.equals(LIGHT_TOKEN_PROGRAM_ID)).toBe(true);
Expand Down
Loading
Loading