@@ -12,24 +12,18 @@ use tuktuk_program::{
1212 RunTaskReturnV0 , TaskQueueV0 , TaskReturnV0 , TransactionSourceV0 , TriggerV0 ,
1313} ;
1414
15- use crate :: {
16- error:: ErrorCode ,
17- state:: { CronJobTransactionV0 , CronJobV0 } ,
18- } ;
15+ use crate :: state:: { CronJobTransactionV0 , CronJobV0 } ;
1916
20- pub const QUEUED_TASKS_PER_QUEUE : u8 = 3 ;
2117// Queue tasks 5 minutes before the cron job is scheduled to run
2218// This way we don't take up space in the task queue for tasks that are running
2319// A long time from now
2420pub const QUEUE_TASK_DELAY : i64 = 60 * 5 ;
2521
2622#[ derive( Accounts ) ]
2723pub struct QueueCronTasksV0 < ' info > {
28- #[ account(
29- mut ,
30- has_one = task_queue
31- ) ]
32- pub cron_job : Box < Account < ' info , CronJobV0 > > ,
24+ /// CHECK: Checked via require in handler
25+ #[ account( mut ) ]
26+ pub cron_job : UncheckedAccount < ' info > ,
3327 pub task_queue : Box < Account < ' info , TaskQueueV0 > > ,
3428 /// CHECK: Used to write return data
3529 #[ account(
@@ -48,58 +42,82 @@ pub struct QueueCronTasksV0<'info> {
4842 pub system_program : Program < ' info , System > ,
4943}
5044
45+ #[ macro_export]
46+ macro_rules! try_from {
47+ ( $ty: ty, $acc: expr) => { {
48+ let account_info = $acc. as_ref( ) ;
49+ <$ty>:: try_from( unsafe {
50+ core:: mem:: transmute:: <
51+ & anchor_lang:: prelude:: AccountInfo <' _>,
52+ & anchor_lang:: prelude:: AccountInfo <' _>,
53+ >( account_info)
54+ } )
55+ } } ;
56+ }
57+
5158pub fn handler ( ctx : Context < QueueCronTasksV0 > ) -> Result < RunTaskReturnV0 > {
5259 let stale_task_age = ctx. accounts . task_queue . stale_task_age ;
5360 let now = Clock :: get ( ) ?. unix_timestamp ;
61+ if ctx. accounts . cron_job . data_is_empty ( ) {
62+ msg ! ( "Cron job was closed, completing task" ) ;
63+ return Ok ( RunTaskReturnV0 {
64+ tasks : vec ! [ ] ,
65+ accounts : vec ! [ ] ,
66+ } ) ;
67+ }
68+ let mut cron_job = try_from ! ( Account <CronJobV0 >, & ctx. accounts. cron_job) ?;
69+ require_eq ! ( cron_job. task_queue, ctx. accounts. task_queue. key( ) ) ;
5470
5571 // Only proceed if we're within the queue window of the next execution
56- if ( now + QUEUE_TASK_DELAY ) < ctx . accounts . cron_job . current_exec_ts {
72+ if ( now + QUEUE_TASK_DELAY ) < cron_job. current_exec_ts {
5773 msg ! ( "Too early to queue tasks, current time {} is not within {} seconds of next execution {}" ,
58- now, QUEUE_TASK_DELAY , ctx. accounts. cron_job. current_exec_ts) ;
59- return Err ( error ! ( ErrorCode :: TooEarly ) ) ;
74+ now, QUEUE_TASK_DELAY , cron_job. current_exec_ts) ;
75+
76+ // Return Ok so this task closes
77+ return Ok ( RunTaskReturnV0 {
78+ tasks : vec ! [ ] ,
79+ accounts : vec ! [ ] ,
80+ } ) ;
6081 }
6182
62- if now - ctx . accounts . cron_job . current_exec_ts > stale_task_age as i64 {
83+ if now - cron_job. current_exec_ts > stale_task_age as i64 {
6384 msg ! ( "Cron job is stale, resetting" ) ;
64- ctx . accounts . cron_job . current_exec_ts = now;
65- ctx . accounts . cron_job . current_transaction_id = 0 ;
85+ cron_job. current_exec_ts = now;
86+ cron_job. current_transaction_id = 0 ;
6687 }
6788
68- let max_num_tasks_remaining =
69- ctx. accounts . cron_job . next_transaction_id - ctx. accounts . cron_job . current_transaction_id ;
89+ let max_num_tasks_remaining = cron_job. next_transaction_id - cron_job. current_transaction_id ;
7090 let num_tasks_to_queue =
71- ( ctx . accounts . cron_job . num_tasks_per_queue_call as u32 ) . min ( max_num_tasks_remaining) ;
72- ctx . accounts . cron_job . current_transaction_id += num_tasks_to_queue;
91+ ( cron_job. num_tasks_per_queue_call as u32 ) . min ( max_num_tasks_remaining) ;
92+ cron_job. current_transaction_id += num_tasks_to_queue;
7393
74- let trigger = TriggerV0 :: Timestamp ( ctx . accounts . cron_job . current_exec_ts ) ;
94+ let trigger = TriggerV0 :: Timestamp ( cron_job. current_exec_ts ) ;
7595
7696 // If we reached the end this time, reset to 0 and move the next execution time forward
77- if ctx . accounts . cron_job . current_transaction_id == ctx . accounts . cron_job . next_transaction_id {
78- ctx . accounts . cron_job . current_transaction_id = 0 ;
79- let schedule = Schedule :: from_str ( & ctx . accounts . cron_job . schedule ) . unwrap ( ) ;
97+ if cron_job. current_transaction_id == cron_job. next_transaction_id {
98+ cron_job. current_transaction_id = 0 ;
99+ let schedule = Schedule :: from_str ( & cron_job. schedule ) . unwrap ( ) ;
80100 // Find the next execution time after the last one
81- let ts = ctx . accounts . cron_job . current_exec_ts ;
101+ let ts = cron_job. current_exec_ts ;
82102 let date_time_ts = & DateTime :: < Utc > :: from_naive_utc_and_offset (
83103 DateTime :: from_timestamp ( ts, 0 ) . unwrap ( ) . naive_utc ( ) ,
84104 Utc ,
85105 ) ;
86- ctx. accounts . cron_job . current_exec_ts =
87- schedule. next_after ( date_time_ts) . unwrap ( ) . timestamp ( ) ;
106+ cron_job. current_exec_ts = schedule. next_after ( date_time_ts) . unwrap ( ) . timestamp ( ) ;
88107 msg ! (
89108 "Will have finished execution ts: {}, moving to {}" ,
90109 ts,
91- ctx . accounts . cron_job. current_exec_ts
110+ cron_job. current_exec_ts
92111 ) ;
93112 }
94113
95- let remaining_accounts = ( ctx. accounts . cron_job . current_transaction_id
96- ..ctx. accounts . cron_job . current_transaction_id
97- + ctx. accounts . cron_job . num_tasks_per_queue_call as u32 )
114+ let remaining_accounts = ( cron_job. current_transaction_id
115+ ..cron_job. current_transaction_id + cron_job. num_tasks_per_queue_call as u32 )
98116 . map ( |i| {
99117 Pubkey :: find_program_address (
100118 & [
101119 b"cron_job_transaction" ,
102- ctx . accounts . cron_job . key ( ) . as_ref ( ) ,
120+ cron_job. key ( ) . as_ref ( ) ,
103121 & i. to_le_bytes ( ) ,
104122 ] ,
105123 & crate :: ID ,
@@ -113,7 +131,7 @@ pub fn handler(ctx: Context<QueueCronTasksV0>) -> Result<RunTaskReturnV0> {
113131 program_id: crate :: ID ,
114132 accounts: [
115133 crate :: __cpi_client_accounts_queue_cron_tasks_v0:: QueueCronTasksV0 {
116- cron_job: ctx . accounts . cron_job. to_account_info( ) ,
134+ cron_job: cron_job. to_account_info( ) ,
117135 task_queue: ctx. accounts. task_queue. to_account_info( ) ,
118136 task_return_account_1: ctx. accounts. task_return_account_1. to_account_info( ) ,
119137 task_return_account_2: ctx. accounts. task_return_account_2. to_account_info( ) ,
@@ -130,19 +148,13 @@ pub fn handler(ctx: Context<QueueCronTasksV0>) -> Result<RunTaskReturnV0> {
130148 } ] ,
131149 vec ! [ ] ,
132150 ) ?;
133- let free_tasks_per_transaction = ctx. accounts . cron_job . free_tasks_per_transaction ;
134- let trunc_name = ctx
135- . accounts
136- . cron_job
137- . name
138- . chars ( )
139- . take ( 32 )
140- . collect :: < String > ( ) ;
151+ let free_tasks_per_transaction = cron_job. free_tasks_per_transaction ;
152+ let trunc_name = cron_job. name . chars ( ) . take ( 32 ) . collect :: < String > ( ) ;
141153 let tasks = std:: iter:: once ( TaskReturnV0 {
142- trigger : TriggerV0 :: Timestamp ( ctx . accounts . cron_job . current_exec_ts - QUEUE_TASK_DELAY ) ,
154+ trigger : TriggerV0 :: Timestamp ( cron_job. current_exec_ts - QUEUE_TASK_DELAY ) ,
143155 transaction : TransactionSourceV0 :: CompiledV0 ( queue_tx) ,
144156 crank_reward : None ,
145- free_tasks : ctx . accounts . cron_job . num_tasks_per_queue_call + 1 ,
157+ free_tasks : cron_job. num_tasks_per_queue_call + 1 ,
146158 description : format ! ( "queue {}" , trunc_name) ,
147159 } )
148160 . chain ( ( 0 ..num_tasks_to_queue as usize ) . filter_map ( |i| {
@@ -164,26 +176,26 @@ pub fn handler(ctx: Context<QueueCronTasksV0>) -> Result<RunTaskReturnV0> {
164176 } ) ) ;
165177
166178 // Past all the CronJobTransaction are the free tasks
167- ctx . accounts . cron_job . next_schedule_task =
168- ctx. remaining_accounts [ ctx . accounts . cron_job . num_tasks_per_queue_call as usize ] . key ( ) ;
179+ cron_job. next_schedule_task =
180+ ctx. remaining_accounts [ cron_job. num_tasks_per_queue_call as usize ] . key ( ) ;
169181
170182 let res = write_return_tasks ( WriteReturnTasksArgs {
171183 program_id : crate :: ID ,
172- payer_info : PayerInfo :: PdaPayer ( ctx . accounts . cron_job . to_account_info ( ) ) ,
184+ payer_info : PayerInfo :: PdaPayer ( cron_job. to_account_info ( ) ) ,
173185 accounts : vec ! [
174186 AccountWithSeeds {
175187 account: ctx. accounts. task_return_account_1. to_account_info( ) ,
176188 seeds: vec![
177189 b"task_return_account_1" . to_vec( ) ,
178- ctx . accounts . cron_job. key( ) . as_ref( ) . to_vec( ) ,
190+ cron_job. key( ) . as_ref( ) . to_vec( ) ,
179191 vec![ ctx. bumps. task_return_account_1] ,
180192 ] ,
181193 } ,
182194 AccountWithSeeds {
183195 account: ctx. accounts. task_return_account_2. to_account_info( ) ,
184196 seeds: vec![
185197 b"task_return_account_2" . to_vec( ) ,
186- ctx . accounts . cron_job. key( ) . as_ref( ) . to_vec( ) ,
198+ cron_job. key( ) . as_ref( ) . to_vec( ) ,
187199 vec![ ctx. bumps. task_return_account_2] ,
188200 ] ,
189201 } ,
@@ -200,28 +212,30 @@ pub fn handler(ctx: Context<QueueCronTasksV0>) -> Result<RunTaskReturnV0> {
200212 msg ! ( "Queued {} tasks" , total_tasks) ;
201213
202214 // Transfer needed lamports from the cron job to the task queue
203- let cron_job_info = ctx . accounts . cron_job . to_account_info ( ) ;
215+ let cron_job_info = cron_job. to_account_info ( ) ;
204216 let cron_job_min_lamports = Rent :: get ( ) ?. minimum_balance ( cron_job_info. data_len ( ) ) ;
205217 let lamports = ctx. accounts . task_queue . min_crank_reward * total_tasks as u64 ;
206218 if cron_job_info. lamports ( ) < cron_job_min_lamports + lamports {
207219 msg ! (
208220 "Not enough lamports to fund tasks. Please requeue cron job when you have enough lamports. {}" ,
209221 cron_job_info. lamports( )
210222 ) ;
211- ctx. accounts . cron_job . removed_from_queue = true ;
212- ctx. accounts . cron_job . next_schedule_task = Pubkey :: default ( ) ;
223+ cron_job. removed_from_queue = true ;
224+ cron_job. next_schedule_task = Pubkey :: default ( ) ;
225+ cron_job. exit ( & crate :: ID ) ?;
213226 Ok ( RunTaskReturnV0 {
214227 tasks : vec ! [ ] ,
215228 accounts : vec ! [ ] ,
216229 } )
217230 } else {
218- ctx . accounts . cron_job . removed_from_queue = false ;
231+ cron_job. removed_from_queue = false ;
219232 cron_job_info. sub_lamports ( lamports) ?;
220233 ctx. accounts
221234 . task_queue
222235 . to_account_info ( )
223236 . add_lamports ( lamports) ?;
224237
238+ cron_job. exit ( & crate :: ID ) ?;
225239 Ok ( RunTaskReturnV0 {
226240 tasks : vec ! [ ] ,
227241 accounts : used_accounts,
@@ -231,10 +245,11 @@ pub fn handler(ctx: Context<QueueCronTasksV0>) -> Result<RunTaskReturnV0> {
231245 Err ( e) if e. to_string ( ) . contains ( "rent exempt" ) => {
232246 msg ! (
233247 "Not enough lamports to fund tasks. Please requeue cron job when you have enough lamports. {}" ,
234- ctx . accounts . cron_job. to_account_info( ) . lamports( )
248+ cron_job. to_account_info( ) . lamports( )
235249 ) ;
236- ctx. accounts . cron_job . removed_from_queue = true ;
237- ctx. accounts . cron_job . next_schedule_task = Pubkey :: default ( ) ;
250+ cron_job. removed_from_queue = true ;
251+ cron_job. next_schedule_task = Pubkey :: default ( ) ;
252+ cron_job. exit ( & crate :: ID ) ?;
238253 Ok ( RunTaskReturnV0 {
239254 tasks : vec ! [ ] ,
240255 accounts : vec ! [ ] ,
0 commit comments