Skip to content

Commit 50778df

Browse files
authored
fix: osec audit fixes (#1882)
* fix: batched-merkle-tree assign tree capacity * chore: add root history array capacity check * fix: include zkp batch size in merkle tree update capacity check * fix: add mut checks for sol token pool, decompression recpient account infos * chore: add validate_batched_address_tree_params * fix: check for duplicate input and read only accounts * test: check for duplicate input and read only accounts
1 parent 8773c73 commit 50778df

File tree

15 files changed

+317
-41
lines changed

15 files changed

+317
-41
lines changed

anchor-programs/system/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,6 @@ pub enum SystemProgramError {
8888
TooManyOutputAccounts,
8989
#[msg("Failed to borrow account data")]
9090
BorrowingDataFailed,
91+
#[msg("DuplicateAccountInInputsAndReadOnly")]
92+
DuplicateAccountInInputsAndReadOnly,
9193
}

program-libs/batched-merkle-tree/src/batch.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -989,10 +989,9 @@ mod tests {
989989
num_iters,
990990
bloom_filter_capacity,
991991
Pubkey::new_unique(),
992+
16, // Tree height 4 -> capacity 16
992993
)
993994
.unwrap();
994-
// Tree height 4 -> capacity 16
995-
account.tree_capacity = 16;
996995
assert_eq!(account.get_num_inserted_in_current_batch(), 0);
997996
// Fill first batch
998997
for i in 1..=batch_size {

program-libs/batched-merkle-tree/src/initialize_address_tree.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{
1111
DEFAULT_BATCH_ROOT_HISTORY_LEN, DEFAULT_BATCH_SIZE,
1212
},
1313
errors::BatchedMerkleTreeError,
14-
initialize_state_tree::match_circuit_size,
1514
merkle_tree::{get_merkle_tree_account_size, BatchedMerkleTreeAccount},
1615
BorshDeserialize, BorshSerialize,
1716
};
@@ -129,6 +128,7 @@ pub fn init_batched_address_merkle_tree_account(
129128
)
130129
}
131130

131+
/// Only used for testing. For production use the default config.
132132
pub fn validate_batched_address_tree_params(params: InitAddressTreeAccountsInstructionData) {
133133
assert!(params.input_queue_batch_size > 0);
134134
assert_eq!(
@@ -138,7 +138,7 @@ pub fn validate_batched_address_tree_params(params: InitAddressTreeAccountsInstr
138138
);
139139
assert!(
140140
match_circuit_size(params.input_queue_zkp_batch_size),
141-
"Zkp batch size not supported. Supported 1, 10, 100, 500, 1000"
141+
"Zkp batch size not supported. Supported: 10, 250"
142142
);
143143

144144
assert!(params.bloom_filter_num_iters > 0);
@@ -151,10 +151,24 @@ pub fn validate_batched_address_tree_params(params: InitAddressTreeAccountsInstr
151151
assert!(params.bloom_filter_capacity > 0);
152152
assert!(params.root_history_capacity > 0);
153153
assert!(params.input_queue_batch_size > 0);
154+
155+
// Validate root_history_capacity is sufficient for input operations
156+
// (address trees only have input queues, no output queues)
157+
let required_capacity = params.input_queue_batch_size / params.input_queue_zkp_batch_size;
158+
assert!(
159+
params.root_history_capacity >= required_capacity as u32,
160+
"root_history_capacity ({}) must be >= {} (input_queue_batch_size / input_queue_zkp_batch_size)",
161+
params.root_history_capacity,
162+
required_capacity
163+
);
164+
154165
assert_eq!(params.close_threshold, None);
155166
assert_eq!(params.height, DEFAULT_BATCH_ADDRESS_TREE_HEIGHT);
156167
}
157-
168+
/// Only 10 and 250 are supported.
169+
pub fn match_circuit_size(size: u64) -> bool {
170+
matches!(size, 10 | 250)
171+
}
158172
pub fn get_address_merkle_tree_account_size_from_params(
159173
params: InitAddressTreeAccountsInstructionData,
160174
) -> usize {
@@ -330,3 +344,23 @@ fn test_height_not_40() {
330344
};
331345
validate_batched_address_tree_params(params);
332346
}
347+
348+
#[test]
349+
fn test_validate_root_history_capacity_address_tree() {
350+
// Test with valid params (default should pass)
351+
let params = InitAddressTreeAccountsInstructionData::default();
352+
validate_batched_address_tree_params(params); // Should not panic
353+
}
354+
355+
#[test]
356+
#[should_panic(expected = "root_history_capacity")]
357+
fn test_validate_root_history_capacity_insufficient_address_tree() {
358+
let params = InitAddressTreeAccountsInstructionData {
359+
root_history_capacity: 1, // Much too small
360+
input_queue_batch_size: 1000,
361+
input_queue_zkp_batch_size: 10,
362+
// Required: 1000/10 = 100, but we set only 1
363+
..Default::default()
364+
};
365+
validate_batched_address_tree_params(params); // Should panic
366+
}

program-libs/batched-merkle-tree/src/initialize_state_tree.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ pub fn init_batched_state_merkle_tree_accounts<'a>(
162162
0,
163163
0,
164164
output_queue_pubkey,
165+
2u64.pow(params.height),
165166
)?;
166167
}
167168
let metadata = MerkleTreeMetadata {
@@ -198,6 +199,7 @@ pub fn init_batched_state_merkle_tree_accounts<'a>(
198199
)
199200
}
200201

202+
/// Only used for testing. For production use the default config.
201203
pub fn validate_batched_tree_params(params: InitStateTreeAccountsInstructionData) {
202204
assert!(params.input_queue_batch_size > 0);
203205
assert!(params.output_queue_batch_size > 0);
@@ -213,11 +215,11 @@ pub fn validate_batched_tree_params(params: InitStateTreeAccountsInstructionData
213215
);
214216
assert!(
215217
match_circuit_size(params.input_queue_zkp_batch_size),
216-
"Zkp batch size not supported. Supported 1, 10, 100, 500, 1000"
218+
"Zkp batch size not supported. Supported 10, 500"
217219
);
218220
assert!(
219221
match_circuit_size(params.output_queue_zkp_batch_size),
220-
"Zkp batch size not supported. Supported 1, 10, 100, 500, 1000"
222+
"Zkp batch size not supported. Supported 10, 500"
221223
);
222224

223225
assert!(params.bloom_filter_num_iters > 0);
@@ -230,12 +232,46 @@ pub fn validate_batched_tree_params(params: InitStateTreeAccountsInstructionData
230232
assert!(params.bloom_filter_capacity > 0);
231233
assert!(params.root_history_capacity > 0);
232234
assert!(params.input_queue_batch_size > 0);
235+
236+
// Validate root_history_capacity is sufficient for both input and output operations
237+
let required_capacity = (params.output_queue_batch_size / params.output_queue_zkp_batch_size)
238+
+ (params.input_queue_batch_size / params.input_queue_zkp_batch_size);
239+
assert!(
240+
params.root_history_capacity >= required_capacity as u32,
241+
"root_history_capacity ({}) must be >= {} (output_queue_batch_size / output_queue_zkp_batch_size + input_queue_batch_size / input_queue_zkp_batch_size)",
242+
params.root_history_capacity,
243+
required_capacity
244+
);
245+
233246
assert_eq!(params.close_threshold, None);
234247
assert_eq!(params.height, DEFAULT_BATCH_STATE_TREE_HEIGHT);
235248
}
236249

250+
/// Only 10 and 500 are supported.
237251
pub fn match_circuit_size(size: u64) -> bool {
238-
matches!(size, 10 | 100 | 250 | 500 | 1000)
252+
matches!(size, 10 | 500)
253+
}
254+
255+
#[test]
256+
fn test_validate_root_history_capacity_state_tree() {
257+
// Test with valid params (default should pass)
258+
let params = InitStateTreeAccountsInstructionData::default();
259+
validate_batched_tree_params(params); // Should not panic
260+
}
261+
262+
#[test]
263+
#[should_panic(expected = "root_history_capacity")]
264+
fn test_validate_root_history_capacity_insufficient_state_tree() {
265+
let params = InitStateTreeAccountsInstructionData {
266+
root_history_capacity: 1, // Much too small
267+
input_queue_batch_size: 1000,
268+
output_queue_batch_size: 1000,
269+
input_queue_zkp_batch_size: 10,
270+
output_queue_zkp_batch_size: 10,
271+
// Required: (1000/10) + (1000/10) = 200, but we set only 1
272+
..Default::default()
273+
};
274+
validate_batched_tree_params(params); // Should panic
239275
}
240276
#[cfg(feature = "test-only")]
241277
pub mod test_utils {

program-libs/batched-merkle-tree/src/merkle_tree.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ impl<'a> BatchedMerkleTreeAccount<'a> {
376376
queue_account: &mut BatchedQueueAccount,
377377
instruction_data: InstructionDataBatchAppendInputs,
378378
) -> Result<MerkleTreeEvent, BatchedMerkleTreeError> {
379-
self.check_tree_is_full()?;
379+
self.check_tree_is_full(Some(queue_account.batch_metadata.zkp_batch_size))?;
380380
let pending_batch_index = queue_account.batch_metadata.pending_batch_index as usize;
381381
let new_root = instruction_data.new_root;
382382
let circuit_batch_size = queue_account.batch_metadata.zkp_batch_size;
@@ -465,7 +465,7 @@ impl<'a> BatchedMerkleTreeAccount<'a> {
465465
if self.tree_type != TreeType::AddressV2 as u64 {
466466
return Err(MerkleTreeMetadataError::InvalidTreeType.into());
467467
}
468-
self.check_tree_is_full()?;
468+
self.check_tree_is_full(Some(self.metadata.queue_batches.zkp_batch_size))?;
469469
Ok(MerkleTreeEvent::BatchAddressAppend(
470470
self.update_input_queue::<ADDRESS_QUEUE_TYPE_V2>(instruction_data)?,
471471
))
@@ -886,8 +886,10 @@ impl<'a> BatchedMerkleTreeAccount<'a> {
886886
Ok(())
887887
}
888888

889-
pub fn tree_is_full(&self) -> bool {
890-
self.next_index >= self.capacity
889+
/// Checks if the tree is full, optionally for a batch size.
890+
/// If batch_size is provided, checks if there is enough space for the batch.
891+
pub fn tree_is_full(&self, batch_size: Option<u64>) -> bool {
892+
self.next_index + batch_size.unwrap_or_default() >= self.capacity
891893
}
892894

893895
pub fn check_queue_next_index_reached_tree_capacity(
@@ -899,8 +901,13 @@ impl<'a> BatchedMerkleTreeAccount<'a> {
899901
Ok(())
900902
}
901903

902-
pub fn check_tree_is_full(&self) -> Result<(), BatchedMerkleTreeError> {
903-
if self.tree_is_full() {
904+
/// Checks if the tree is full, optionally for a batch size.
905+
/// If batch_size is provided, checks if there is enough space for the batch.
906+
pub fn check_tree_is_full(
907+
&self,
908+
batch_size: Option<u64>,
909+
) -> Result<(), BatchedMerkleTreeError> {
910+
if self.tree_is_full(batch_size) {
904911
return Err(BatchedMerkleTreeError::TreeIsFull);
905912
}
906913
Ok(())
@@ -1578,7 +1585,7 @@ mod test {
15781585
)
15791586
.unwrap();
15801587
// 1. empty tree is not full
1581-
assert!(!account.tree_is_full());
1588+
assert!(!account.tree_is_full(None));
15821589

15831590
let mut inserted_elements = vec![];
15841591
let rng = &mut rand::rngs::StdRng::from_seed([0u8; 32]);
@@ -1677,17 +1684,30 @@ mod test {
16771684
)
16781685
.unwrap();
16791686
// 1. empty tree is not full
1680-
assert!(!account.tree_is_full());
1681-
assert!(account.check_tree_is_full().is_ok());
1687+
assert!(!account.tree_is_full(None));
1688+
assert!(account.check_tree_is_full(None).is_ok());
1689+
assert!(!account.tree_is_full(Some(1)));
1690+
assert!(account.check_tree_is_full(Some(1)).is_ok());
1691+
account.next_index = account.capacity - 2;
1692+
assert!(!account.tree_is_full(None));
1693+
assert!(account.check_tree_is_full(None).is_ok());
1694+
assert!(!account.tree_is_full(Some(1)));
1695+
assert!(account.check_tree_is_full(Some(1)).is_ok());
16821696
account.next_index = account.capacity - 1;
1683-
assert!(!account.tree_is_full());
1684-
assert!(account.check_tree_is_full().is_ok());
1697+
assert!(!account.tree_is_full(None));
1698+
assert!(account.check_tree_is_full(None).is_ok());
1699+
assert!(account.tree_is_full(Some(1)));
1700+
assert!(account.check_tree_is_full(Some(1)).is_err());
16851701
account.next_index = account.capacity;
1686-
assert!(account.tree_is_full());
1687-
assert!(account.check_tree_is_full().is_err());
1702+
assert!(account.tree_is_full(None));
1703+
assert!(account.check_tree_is_full(None).is_err());
1704+
assert!(account.tree_is_full(Some(1)));
1705+
assert!(account.check_tree_is_full(Some(1)).is_err());
16881706
account.next_index = account.capacity + 1;
1689-
assert!(account.tree_is_full());
1690-
assert!(account.check_tree_is_full().is_err());
1707+
assert!(account.tree_is_full(None));
1708+
assert!(account.check_tree_is_full(None).is_err());
1709+
assert!(account.tree_is_full(Some(1)));
1710+
assert!(account.check_tree_is_full(Some(1)).is_err());
16911711
}
16921712
#[test]
16931713
fn test_increment_next_index() {

0 commit comments

Comments
 (0)