(ctx: Context<ThreadKickoff>)
| 42 | } |
| 43 | |
| 44 | pub fn handler(ctx: Context<ThreadKickoff>) -> Result<()> { |
| 45 | // Get accounts. |
| 46 | let signatory = &mut ctx.accounts.signatory; |
| 47 | let thread = &mut ctx.accounts.thread; |
| 48 | let clock = Clock::get().unwrap(); |
| 49 | |
| 50 | match thread.trigger.clone() { |
| 51 | Trigger::Account { |
| 52 | address, |
| 53 | offset, |
| 54 | size, |
| 55 | } => { |
| 56 | // Verify proof that account data has been updated. |
| 57 | match ctx.remaining_accounts.first() { |
| 58 | None => { |
| 59 | return Err(ClockworkError::TriggerConditionFailed.into()); |
| 60 | } |
| 61 | Some(account_info) => { |
| 62 | // Verify the remaining account is the account this thread is listening for. |
| 63 | require!( |
| 64 | address.eq(account_info.key), |
| 65 | ClockworkError::TriggerConditionFailed |
| 66 | ); |
| 67 | |
| 68 | // Begin computing the data hash of this account. |
| 69 | let mut hasher = DefaultHasher::new(); |
| 70 | let data = &account_info.try_borrow_data().unwrap(); |
| 71 | let offset = offset as usize; |
| 72 | let range_end = offset.checked_add(size as usize).unwrap() as usize; |
| 73 | if data.len().gt(&range_end) { |
| 74 | data[offset..range_end].hash(&mut hasher); |
| 75 | } else { |
| 76 | data[offset..].hash(&mut hasher) |
| 77 | } |
| 78 | let data_hash = hasher.finish(); |
| 79 | |
| 80 | // Verify the data hash is different than the prior data hash. |
| 81 | if let Some(exec_context) = thread.exec_context { |
| 82 | match exec_context.trigger_context { |
| 83 | TriggerContext::Account { |
| 84 | data_hash: prior_data_hash, |
| 85 | } => { |
| 86 | require!( |
| 87 | data_hash.ne(&prior_data_hash), |
| 88 | ClockworkError::TriggerConditionFailed |
| 89 | ) |
| 90 | } |
| 91 | _ => return Err(ClockworkError::InvalidThreadState.into()), |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | // Set a new exec context with the new data hash and slot number. |
| 96 | thread.exec_context = Some(ExecContext { |
| 97 | exec_index: 0, |
| 98 | execs_since_reimbursement: 0, |
| 99 | execs_since_slot: 0, |
| 100 | last_exec_at: clock.slot, |
| 101 | trigger_context: TriggerContext::Account { data_hash }, |
nothing calls this directly
no test coverage detected