@@ -294,12 +294,43 @@ impl<'a, 'info> TaskProcessor<'a, 'info> {
294294 }
295295
296296 fn create_new_task ( & mut self , task : TaskReturnV0 ) -> Result < ( ) > {
297+ require_gte ! (
298+ 40 ,
299+ task. description. len( ) ,
300+ ErrorCode :: InvalidDescriptionLength
301+ ) ;
302+ require_gte ! (
303+ task. crank_reward
304+ . unwrap_or( self . ctx. accounts. task_queue. min_crank_reward) ,
305+ self . ctx. accounts. task_queue. min_crank_reward,
306+ ErrorCode :: InvalidCrankReward
307+ ) ;
308+ require_gte ! (
309+ self . ctx. accounts. task_queue. capacity,
310+ ( task. free_tasks + 1 ) as u16 ,
311+ ErrorCode :: FreeTasksGreaterThanCapacity
312+ ) ;
313+
297314 let free_task_account = & self . ctx . remaining_accounts [ self . free_task_index ] ;
298315 self . free_task_index += 1 ;
299316 let task_queue = & mut self . ctx . accounts . task_queue ;
300317 let task_queue_key = task_queue. key ( ) ;
301318
302- let task_id = self . free_task_ids . pop ( ) . unwrap ( ) ;
319+ let task_id = self
320+ . free_task_ids
321+ . pop ( )
322+ . ok_or ( error ! ( ErrorCode :: TooManyReturnedTasks ) ) ?;
323+
324+ require ! (
325+ !task_queue. task_exists( task_id) ,
326+ ErrorCode :: TaskIdAlreadyInUse
327+ ) ;
328+
329+ // Verify the account is empty
330+ require ! (
331+ free_task_account. data_is_empty( ) ,
332+ ErrorCode :: FreeTaskAccountNotEmpty
333+ ) ;
303334
304335 let seeds = [ b"task" , task_queue_key. as_ref ( ) , & task_id. to_le_bytes ( ) ] ;
305336 let ( key, bump_seed) = Pubkey :: find_program_address ( & seeds, self . ctx . program_id ) ;
@@ -324,10 +355,10 @@ impl<'a, 'info> TaskProcessor<'a, 'info> {
324355 let task_size = task_data. try_to_vec ( ) ?. len ( ) + 8 + 60 ;
325356 let rent_lamports = Rent :: get ( ) ?. minimum_balance ( task_size) ;
326357 let lamports = rent_lamports + task_data. crank_reward ;
327- task_data. rent_amount = lamports ;
358+ task_data. rent_amount = rent_lamports ;
328359
329360 let task_queue_info = self . ctx . accounts . task_queue . to_account_info ( ) ;
330- let task_queue_min_lamports = Rent :: get ( ) ?. minimum_balance ( task_queue_info. data_len ( ) + 60 ) ;
361+ let task_queue_min_lamports = Rent :: get ( ) ?. minimum_balance ( task_queue_info. data_len ( ) ) ;
331362
332363 require_gt ! (
333364 task_queue_info. lamports( ) ,
@@ -349,8 +380,13 @@ impl<'a, 'info> TaskProcessor<'a, 'info> {
349380 free_task_account. realloc ( task_size, false ) ?;
350381
351382 let task_info = self . ctx . accounts . task . to_account_info ( ) ;
352- let task_remaining_lamports = self . ctx . accounts . task . to_account_info ( ) . lamports ( )
353- - self . ctx . accounts . task . crank_reward ;
383+ let task_remaining_lamports = self
384+ . ctx
385+ . accounts
386+ . task
387+ . to_account_info ( )
388+ . lamports ( )
389+ . saturating_sub ( self . ctx . accounts . task . crank_reward ) ;
354390 let lamports_from_task = task_remaining_lamports. min ( lamports) ;
355391 let lamports_needed_from_queue = lamports. saturating_sub ( lamports_from_task) ;
356392
@@ -379,13 +415,23 @@ pub fn handler<'info>(
379415 TriggerV0 :: Timestamp ( timestamp) => timestamp,
380416 } ;
381417 ctx. accounts . task_queue . updated_at = now;
418+ // Check for duplicate task IDs
419+ let mut seen_ids = std:: collections:: HashSet :: new ( ) ;
382420 for id in args. free_task_ids . clone ( ) {
383421 require_gt ! (
384422 ctx. accounts. task_queue. capacity,
385423 id,
386424 ErrorCode :: InvalidTaskId
387425 ) ;
426+ // Ensure ID is not already in use in the task queue
427+ require ! (
428+ !ctx. accounts. task_queue. task_exists( id) ,
429+ ErrorCode :: TaskIdAlreadyInUse
430+ ) ;
431+ // Check for duplicates in provided IDs
432+ require ! ( seen_ids. insert( id) , ErrorCode :: DuplicateTaskIds ) ;
388433 }
434+
389435 let remaining_accounts = ctx. remaining_accounts ;
390436
391437 let transaction = match ctx. accounts . task . transaction . clone ( ) {
@@ -399,6 +445,11 @@ pub fn handler<'info>(
399445 ) ?;
400446 let data = utils:: ed25519:: verify_ed25519_ix ( & ix, signer. to_bytes ( ) . as_slice ( ) ) ?;
401447 let mut remote_tx = RemoteTaskTransactionV0 :: try_deserialize ( & mut & data[ ..] ) ?;
448+ require_eq ! (
449+ remote_tx. transaction. accounts. len( ) ,
450+ 0 ,
451+ ErrorCode :: MalformedRemoteTransaction
452+ ) ;
402453
403454 let num_accounts = remote_tx
404455 . transaction
@@ -431,7 +482,9 @@ pub fn handler<'info>(
431482 + remote_tx. transaction . num_rw_signers ;
432483 // The rent refund account may make an account that shouldn't be writable appear writable
433484 if i >= writable_end_idx as usize
434- && * acc. key == ctx. accounts . rent_refund . key ( )
485+ && ( * acc. key == ctx. accounts . rent_refund . key ( )
486+ || * acc. key == ctx. accounts . task_queue . key ( )
487+ || * acc. key == ctx. accounts . task . key ( ) )
435488 {
436489 data. push ( 0 ) ;
437490 } else {
@@ -470,22 +523,27 @@ pub fn handler<'info>(
470523 . task_queue
471524 . set_task_exists ( ctx. accounts . task . id , false ) ;
472525
473- let free_tasks = ctx. accounts . task . free_tasks ;
474-
475- // Validate that all free task accounts are empty
526+ // Validate that all free task accounts are empty and are valid PDAs
476527 let free_tasks_start_index = transaction. accounts . len ( ) ;
477- for i in 0 ..free_tasks {
478- let free_task_index = free_tasks_start_index + i as usize ;
479- let free_task_account = & remaining_accounts[ free_task_index] ;
480- require ! (
481- free_task_account. data_is_empty( ) ,
482- ErrorCode :: FreeTaskAccountNotEmpty
483- ) ;
484- }
528+ // Validate number of free task accounts matches number of task IDs
529+ require_eq ! (
530+ args. free_task_ids. len( ) ,
531+ ctx. remaining_accounts. len( ) - free_tasks_start_index,
532+ ErrorCode :: MismatchedFreeTaskCounts
533+ ) ;
485534
486535 if now. saturating_sub ( task_time) <= ctx. accounts . task_queue . stale_task_age as i64 {
487536 let mut processor = TaskProcessor :: new ( ctx, & transaction, args. free_task_ids ) ?;
488537
538+ // Validate account keys match
539+ for ( i, account) in transaction. accounts . iter ( ) . enumerate ( ) {
540+ require_eq ! (
541+ account,
542+ remaining_accounts[ i] . key,
543+ ErrorCode :: InvalidAccountKey
544+ ) ;
545+ }
546+
489547 // Process each instruction
490548 for ix in & transaction. instructions {
491549 processor. process_instruction ( ix, remaining_accounts) ?;
0 commit comments