diff --git a/cli/src/cli.rs b/cli/src/cli.rs index ac0f27e3..187547ab 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -76,6 +76,7 @@ pub enum CliCommand { // Thread commands ThreadCreate { id: String, + domain: String, kickoff_instruction: SerializableInstruction, trigger: Trigger, }, diff --git a/cli/src/parser.rs b/cli/src/parser.rs index e097a02e..9c9184fd 100644 --- a/cli/src/parser.rs +++ b/cli/src/parser.rs @@ -195,6 +195,7 @@ fn parse_thread_command(matches: &ArgMatches) -> Result { Some(("crate-info", _)) => Ok(CliCommand::ThreadCrateInfo {}), Some(("create", matches)) => Ok(CliCommand::ThreadCreate { id: parse_string("id", matches)?, + domain: parse_string("domain", matches)?, kickoff_instruction: parse_instruction_file("kickoff_instruction", matches)?, trigger: parse_trigger(matches)?, }), diff --git a/cli/src/processor/localnet.rs b/cli/src/processor/localnet.rs index ed364c3d..f37ddfb6 100644 --- a/cli/src/processor/localnet.rs +++ b/cli/src/processor/localnet.rs @@ -164,7 +164,7 @@ fn register_worker(client: &Client, config: &CliConfig) -> Result<()> { fn create_threads(client: &Client, mint_pubkey: Pubkey) -> Result<()> { // Create epoch thread. let epoch_thread_id = "sablier.network.epoch"; - let epoch_thread_pubkey = Thread::pubkey(client.payer_pubkey(), epoch_thread_id.into()); + let epoch_thread_pubkey = Thread::pubkey(client.payer_pubkey(), epoch_thread_id.into(), None); let ix_a1 = Instruction { program_id: sablier_network_program::ID, accounts: sablier_network_program::accounts::DistributeFeesJob { @@ -237,6 +237,7 @@ fn create_threads(client: &Client, mint_pubkey: Pubkey) -> Result<()> { data: sablier_thread_program::instruction::ThreadCreate { amount: LAMPORTS_PER_SOL, id: epoch_thread_id.into(), + domain: None, instructions: vec![ ix_a1.into(), ix_a2.into(), @@ -255,7 +256,7 @@ fn create_threads(client: &Client, mint_pubkey: Pubkey) -> Result<()> { // Create hasher thread. let hasher_thread_id = "sablier.network.hasher"; - let hasher_thread_pubkey = Thread::pubkey(client.payer_pubkey(), hasher_thread_id.into()); + let hasher_thread_pubkey = Thread::pubkey(client.payer_pubkey(), hasher_thread_id.into(), None); let registry_hash_ix = Instruction { program_id: sablier_network_program::ID, accounts: sablier_network_program::accounts::RegistryNonceHash { @@ -278,6 +279,7 @@ fn create_threads(client: &Client, mint_pubkey: Pubkey) -> Result<()> { data: sablier_thread_program::instruction::ThreadCreate { amount: LAMPORTS_PER_SOL, id: hasher_thread_id.into(), + domain: None, instructions: vec![registry_hash_ix.into()], trigger: Trigger::Cron { schedule: "*/15 * * * * * *".into(), diff --git a/cli/src/processor/mod.rs b/cli/src/processor/mod.rs index 388f7a67..7195db05 100644 --- a/cli/src/processor/mod.rs +++ b/cli/src/processor/mod.rs @@ -99,9 +99,10 @@ pub fn process(matches: &ArgMatches) -> Result<(), CliError> { CliCommand::ThreadCrateInfo {} => thread::crate_info(&client), CliCommand::ThreadCreate { id, + domain, kickoff_instruction, trigger, - } => thread::create(&client, id, vec![kickoff_instruction], trigger), + } => thread::create(&client, id, domain, vec![kickoff_instruction], trigger), CliCommand::ThreadDelete { id } => thread::delete(&client, id), CliCommand::ThreadPause { id } => thread::pause(&client, id), CliCommand::ThreadResume { id } => thread::resume(&client, id), diff --git a/cli/src/processor/thread.rs b/cli/src/processor/thread.rs index 47e34fdd..7a1d8d28 100644 --- a/cli/src/processor/thread.rs +++ b/cli/src/processor/thread.rs @@ -27,10 +27,15 @@ pub fn crate_info(client: &Client) -> Result<(), CliError> { pub fn create( client: &Client, id: String, + domain: String, instructions: Vec, trigger: Trigger, ) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.clone().into_bytes()); + let thread_pubkey = Thread::pubkey( + client.payer_pubkey(), + id.clone().into_bytes(), + Some(domain.into_bytes()), + ); let ix = Instruction { program_id: sablier_thread_program::ID, accounts: sablier_thread_program::accounts::ThreadCreate { @@ -43,6 +48,7 @@ pub fn create( data: sablier_thread_program::instruction::ThreadCreate { amount: 0, id: id.into_bytes(), + domain: None, instructions, trigger, } @@ -54,7 +60,7 @@ pub fn create( } pub fn delete(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes(), None); let ix = Instruction { program_id: sablier_thread_program::ID, accounts: sablier_thread_program::accounts::ThreadDelete { @@ -77,7 +83,7 @@ pub fn get(client: &Client, address: Pubkey) -> Result<(), CliError> { } pub fn pause(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes(), None); let ix = Instruction { program_id: sablier_thread_program::ID, accounts: sablier_thread_program::accounts::ThreadPause { @@ -93,7 +99,7 @@ pub fn pause(client: &Client, id: String) -> Result<(), CliError> { } pub fn resume(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes(), None); let ix = Instruction { program_id: sablier_thread_program::ID, accounts: sablier_thread_program::accounts::ThreadResume { @@ -109,7 +115,7 @@ pub fn resume(client: &Client, id: String) -> Result<(), CliError> { } pub fn reset(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes(), None); let ix = Instruction { program_id: sablier_thread_program::ID, accounts: sablier_thread_program::accounts::ThreadReset { @@ -130,7 +136,7 @@ pub fn update( rate_limit: Option, schedule: Option, ) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes(), None); let trigger = schedule.map(|schedule| Trigger::Cron { schedule, skippable: true, @@ -162,6 +168,6 @@ pub fn parse_pubkey_from_id_or_address( id: Option, address: Option, ) -> Result { - let address_from_id = id.map(|str| Thread::pubkey(authority, str.into())); + let address_from_id = id.map(|str| Thread::pubkey(authority, str.into(), None)); address.or(address_from_id).ok_or(CliError::InvalidAddress) } diff --git a/programs/thread/src/instructions/thread_create.rs b/programs/thread/src/instructions/thread_create.rs index 5b8b78a7..51db658f 100644 --- a/programs/thread/src/instructions/thread_create.rs +++ b/programs/thread/src/instructions/thread_create.rs @@ -10,7 +10,7 @@ use crate::{constants::*, state::*}; /// Accounts required by the `thread_create` instruction. #[derive(Accounts)] -#[instruction(amount: u64, id: Vec, instructions: Vec, trigger: Trigger)] +#[instruction(amount: u64, id: Vec, domain: Option>, instructions: Vec, trigger: Trigger)] pub struct ThreadCreate<'info> { /// The authority (owner) of the thread. pub authority: Signer<'info>, @@ -29,6 +29,7 @@ pub struct ThreadCreate<'info> { SEED_THREAD, authority.key().as_ref(), id.as_slice(), + domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump, payer= payer, @@ -48,6 +49,7 @@ pub fn handler( ctx: Context, amount: u64, id: Vec, + domain: Option>, instructions: Vec, trigger: Trigger, ) -> Result<()> { @@ -65,6 +67,7 @@ pub fn handler( thread.exec_context = None; thread.fee = THREAD_MINIMUM_FEE; thread.id = id; + thread.domain = domain; thread.instructions = instructions; thread.name = String::new(); thread.next_instruction = None; diff --git a/programs/thread/src/instructions/thread_delete.rs b/programs/thread/src/instructions/thread_delete.rs index ff38a186..4f468a75 100644 --- a/programs/thread/src/instructions/thread_delete.rs +++ b/programs/thread/src/instructions/thread_delete.rs @@ -23,6 +23,7 @@ pub struct ThreadDelete<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, )] diff --git a/programs/thread/src/instructions/thread_exec.rs b/programs/thread/src/instructions/thread_exec.rs index 82192dc8..690442ef 100644 --- a/programs/thread/src/instructions/thread_exec.rs +++ b/programs/thread/src/instructions/thread_exec.rs @@ -42,6 +42,7 @@ pub struct ThreadExec<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, constraint = !thread.paused @ SablierError::ThreadPaused, @@ -93,6 +94,7 @@ pub fn handler(ctx: Context) -> Result<()> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice(), &[thread.bump], ]], )?; diff --git a/programs/thread/src/instructions/thread_instruction_add.rs b/programs/thread/src/instructions/thread_instruction_add.rs index 6f204d3d..c22b9184 100644 --- a/programs/thread/src/instructions/thread_instruction_add.rs +++ b/programs/thread/src/instructions/thread_instruction_add.rs @@ -24,6 +24,7 @@ pub struct ThreadInstructionAdd<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority diff --git a/programs/thread/src/instructions/thread_instruction_remove.rs b/programs/thread/src/instructions/thread_instruction_remove.rs index 37dfd409..66123824 100644 --- a/programs/thread/src/instructions/thread_instruction_remove.rs +++ b/programs/thread/src/instructions/thread_instruction_remove.rs @@ -17,6 +17,7 @@ pub struct ThreadInstructionRemove<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority diff --git a/programs/thread/src/instructions/thread_kickoff.rs b/programs/thread/src/instructions/thread_kickoff.rs index 3cc98eb1..5cb06631 100644 --- a/programs/thread/src/instructions/thread_kickoff.rs +++ b/programs/thread/src/instructions/thread_kickoff.rs @@ -27,12 +27,13 @@ pub struct ThreadKickoff<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, constraint = !thread.paused @ SablierError::ThreadPaused, constraint = thread.next_instruction.is_none() @ SablierError::ThreadBusy, )] - pub thread: Box>, + pub thread: Account<'info, Thread>, /// The worker. #[account(address = worker.pubkey())] @@ -117,6 +118,11 @@ pub fn handler(ctx: Context) -> Result<()> { // Verify the current timestamp is greater than or equal to the threshold timestamp. let threshold_timestamp = next_timestamp(reference_timestamp, schedule.clone()) .ok_or(SablierError::TriggerConditionFailed)?; + msg!( + "Threshold timestamp: {}, clock timestamp: {}", + threshold_timestamp, + clock.unix_timestamp + ); require!( clock.unix_timestamp.ge(&threshold_timestamp), SablierError::TriggerConditionFailed diff --git a/programs/thread/src/instructions/thread_pause.rs b/programs/thread/src/instructions/thread_pause.rs index 052946ef..81c8eef4 100644 --- a/programs/thread/src/instructions/thread_pause.rs +++ b/programs/thread/src/instructions/thread_pause.rs @@ -16,6 +16,7 @@ pub struct ThreadPause<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority diff --git a/programs/thread/src/instructions/thread_reset.rs b/programs/thread/src/instructions/thread_reset.rs index d28a949b..7e220d6f 100644 --- a/programs/thread/src/instructions/thread_reset.rs +++ b/programs/thread/src/instructions/thread_reset.rs @@ -16,6 +16,7 @@ pub struct ThreadReset<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority diff --git a/programs/thread/src/instructions/thread_resume.rs b/programs/thread/src/instructions/thread_resume.rs index 3ce45dd1..79a8a29e 100644 --- a/programs/thread/src/instructions/thread_resume.rs +++ b/programs/thread/src/instructions/thread_resume.rs @@ -16,6 +16,7 @@ pub struct ThreadResume<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority diff --git a/programs/thread/src/instructions/thread_update.rs b/programs/thread/src/instructions/thread_update.rs index 1b40c7df..edf3b0dd 100644 --- a/programs/thread/src/instructions/thread_update.rs +++ b/programs/thread/src/instructions/thread_update.rs @@ -24,6 +24,7 @@ pub struct ThreadUpdate<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority, diff --git a/programs/thread/src/instructions/thread_withdraw.rs b/programs/thread/src/instructions/thread_withdraw.rs index 1c58562c..8402aa04 100644 --- a/programs/thread/src/instructions/thread_withdraw.rs +++ b/programs/thread/src/instructions/thread_withdraw.rs @@ -21,6 +21,7 @@ pub struct ThreadWithdraw<'info> { SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice(), + thread.domain.as_ref().unwrap_or(&Vec::new()).as_slice() ], bump = thread.bump, has_one = authority, diff --git a/programs/thread/src/lib.rs b/programs/thread/src/lib.rs index 2f2d796b..fa543897 100644 --- a/programs/thread/src/lib.rs +++ b/programs/thread/src/lib.rs @@ -40,10 +40,11 @@ pub mod thread_program { ctx: Context, amount: u64, id: Vec, + domain: Option>, instructions: Vec, trigger: Trigger, ) -> Result<()> { - thread_create::handler(ctx, amount, id, instructions, trigger) + thread_create::handler(ctx, amount, id, domain, instructions, trigger) } /// Closes an existing thread account and returns the lamports to the owner. diff --git a/programs/thread/src/state/thread.rs b/programs/thread/src/state/thread.rs index adcd6268..f7a160a7 100644 --- a/programs/thread/src/state/thread.rs +++ b/programs/thread/src/state/thread.rs @@ -19,6 +19,7 @@ pub struct Thread { pub bump: u8, /// The cluster clock at the moment the thread was created. pub created_at: ClockData, + pub domain: Option>, /// The context of the thread's current execution state. pub exec_context: Option, /// The number of lamports to payout to workers per execution. @@ -41,9 +42,14 @@ pub struct Thread { impl Thread { /// Derive the pubkey of a thread account. - pub fn pubkey(authority: Pubkey, id: Vec) -> Pubkey { + pub fn pubkey(authority: Pubkey, id: Vec, domain: Option>) -> Pubkey { Pubkey::find_program_address( - &[SEED_THREAD, authority.as_ref(), id.as_slice()], + &[ + SEED_THREAD, + authority.as_ref(), + id.as_slice(), + domain.unwrap_or_default().as_slice(), + ], &crate::ID, ) .0 @@ -69,7 +75,7 @@ pub trait ThreadAccount { impl ThreadAccount for Account<'_, Thread> { fn pubkey(&self) -> Pubkey { - Thread::pubkey(self.authority, self.id.clone()) + Thread::pubkey(self.authority, self.id.clone(), self.domain.clone()) } fn realloc_account(&mut self) -> Result<()> { diff --git a/programs/thread/src/state/versioned_thread.rs b/programs/thread/src/state/versioned_thread.rs index 167fc0e5..d9fde909 100644 --- a/programs/thread/src/state/versioned_thread.rs +++ b/programs/thread/src/state/versioned_thread.rs @@ -31,6 +31,12 @@ impl VersionedThread { } } + pub fn domain(&self) -> Option> { + match self { + Self::V1(t) => t.domain.clone(), + } + } + pub fn next_instruction(&self) -> Option { match self { Self::V1(t) => t.next_instruction.clone(), @@ -51,7 +57,7 @@ impl VersionedThread { pub fn pubkey(&self) -> Pubkey { match self { - Self::V1(_) => Thread::pubkey(self.authority(), self.id()), + Self::V1(_) => Thread::pubkey(self.authority(), self.id(), self.domain()), } } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 78c85243..582c4f4e 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -26,10 +26,11 @@ pub mod cpi { ctx: CpiContext<'_, '_, '_, 'info, ThreadCreate<'info>>, amount: u64, id: Vec, + domain: Option>, instructions: Vec, trigger: crate::state::Trigger, ) -> Result<()> { - sablier_thread_program::cpi::thread_create(ctx, amount, id, instructions, trigger) + sablier_thread_program::cpi::thread_create(ctx, amount, id, domain, instructions, trigger) } pub fn thread_delete<'info>(