Skip to content

Commit

Permalink
Use an anchor account for RemoteTaskTransactionV0 so that it can be p…
Browse files Browse the repository at this point in the history
…arsed in explorer
  • Loading branch information
ChewingGlass committed Jan 17, 2025
1 parent 19eaa5f commit 6b36413
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 67 deletions.
13 changes: 6 additions & 7 deletions solana-programs/packages/remote-example-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,15 @@ server.post<{
accounts: remainingAccounts.map((acc) => acc.pubkey),
},
});
const serialized = await RemoteTaskTransactionV0.serialize(
program.coder.accounts,
remoteTx
);
const resp = {
transaction: RemoteTaskTransactionV0.serialize(
program.coder.types,
remoteTx
).toString("base64"),
transaction: serialized.toString("base64"),
signature: Buffer.from(
sign.detached(
Uint8Array.from(
RemoteTaskTransactionV0.serialize(program.coder.types, remoteTx)
),
Uint8Array.from(serialized),
serverWallet.secretKey
)
).toString("base64"),
Expand Down
9 changes: 3 additions & 6 deletions solana-programs/packages/tuktuk-sdk/src/transaction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AccountsCoder,
BN,
CustomAccountResolver,
Idl,
Expand Down Expand Up @@ -75,12 +76,8 @@ export class RemoteTaskTransactionV0 {
this.transaction = { ...fields.transaction, accounts: [] };
}

static serialize(coder: TypesCoder, value: RemoteTaskTransactionV0): Buffer {
return Buffer.concat([
sighash("tuktuk", "RemoteTaskTransactionV0"),
value.verificationHash,
coder.encode("compiledTransactionV0", value.transaction),
]);
static async serialize(coder: AccountsCoder, value: RemoteTaskTransactionV0): Promise<Buffer> {
return coder.encode("remoteTaskTransactionV0", value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub fn handler(ctx: Context<InitializeCronJobV0>, args: InitializeCronJobArgsV0)
current_transaction_id: 0,
next_transaction_id: 0,
bump_seed: ctx.bumps.cron_job,
removed_from_queue: false,
num_transactions: 0,
});
ctx.accounts.user_cron_jobs.next_cron_job_id += 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ use tuktuk_program::{
RunTaskReturnV0, TaskQueueV0, TaskReturnV0, TransactionSourceV0, TriggerV0,
};

use crate::{
error::ErrorCode,
state::{CronJobTransactionV0, CronJobV0},
};
use crate::state::{CronJobTransactionV0, CronJobV0};

pub const QUEUED_TASKS_PER_QUEUE: u8 = 3;
// Queue tasks 5 minutes before the cron job is scheduled to run
Expand Down Expand Up @@ -166,25 +163,33 @@ pub fn handler(ctx: Context<QueueCronTasksV0>) -> Result<RunTaskReturnV0> {
system_program: ctx.accounts.system_program.to_account_info(),
tasks,
})?;

msg!("Queued {} tasks", total_tasks);

// Transfer needed lamports from the cron job to the task queue
let cron_job_info = ctx.accounts.cron_job.to_account_info();
let cron_job_min_lamports = Rent::get()?.minimum_balance(cron_job_info.data_len());
let lamports = ctx.accounts.task_queue.min_crank_reward * total_tasks as u64;
require_gt!(
cron_job_info.lamports(),
cron_job_min_lamports + lamports,
ErrorCode::InsufficientFunds
);

cron_job_info.sub_lamports(lamports)?;
ctx.accounts
.task_queue
.to_account_info()
.add_lamports(lamports)?;
if cron_job_info.lamports() < cron_job_min_lamports + lamports {
msg!(
"Not enough lamports to fund tasks. Please requeue cron job when you have enough lamports. {}",
cron_job_info.lamports()
);
ctx.accounts.cron_job.removed_from_queue = true;
Ok(RunTaskReturnV0 {
tasks: vec![],
accounts: vec![],
})
} else {
cron_job_info.sub_lamports(lamports)?;
ctx.accounts
.task_queue
.to_account_info()
.add_lamports(lamports)?;

Ok(RunTaskReturnV0 {
tasks: vec![],
accounts: used_accounts,
})
Ok(RunTaskReturnV0 {
tasks: vec![],
accounts: used_accounts,
})
}
}
3 changes: 3 additions & 0 deletions solana-programs/programs/cron/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub struct CronJobV0 {
pub current_transaction_id: u32,
pub num_transactions: u32,
pub next_transaction_id: u32,
// A cron job is removed from the queue when it no longer has enough lamports to fund tasks
// Once this is set, you need to requeue the cron job.
pub removed_from_queue: bool,
pub bump_seed: u8,
}

Expand Down
4 changes: 2 additions & 2 deletions solana-programs/programs/tuktuk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ pub enum ErrorCode {
InvalidRentRefund,
#[msg("Invalid task id")]
InvalidTaskId,
#[msg("Invalid discriminator")]
InvalidDiscriminator,
#[msg("Don't use the dummy instruction")]
DummyInstruction,
}
22 changes: 4 additions & 18 deletions solana-programs/programs/tuktuk/src/instructions/run_task_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ impl<'a> Iterator for TasksIterator<'a> {
}
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
// This isn't actually an account, but we want anchor to put it in the IDL and serialize it with a discriminator
#[account]
#[derive(Default)]
pub struct RemoteTaskTransactionV0 {
// A hash of [task, task_queued_at, ...remaining_accounts]
pub verification_hash: [u8; 32],
Expand Down Expand Up @@ -359,14 +361,8 @@ pub fn handler<'info>(
ix_index.checked_sub(1).unwrap() as usize,
&ctx.accounts.sysvar_instructions,
)?;
let expected_sighash = sighash("tuktuk", "RemoteTaskTransactionV0");
let data = utils::ed25519::verify_ed25519_ix(&ix, signer.to_bytes().as_slice())?;
let mut remote_tx = RemoteTaskTransactionV0::deserialize(&mut &data[8..])?;

require!(
data[..8] == expected_sighash,
ErrorCode::InvalidDiscriminator
);
let mut remote_tx = RemoteTaskTransactionV0::try_deserialize(&mut &data[..])?;

let num_accounts = remote_tx
.transaction
Expand Down Expand Up @@ -461,13 +457,3 @@ pub fn handler<'info>(

Ok(())
}

pub fn sighash(namespace: &str, name: &str) -> [u8; 8] {
let preimage = format!("{}:{}", namespace, name);

let mut sighash = [0u8; 8];
sighash.copy_from_slice(
&anchor_lang::solana_program::hash::hash(preimage.as_bytes()).to_bytes()[..8],
);
sighash
}
10 changes: 10 additions & 0 deletions solana-programs/programs/tuktuk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,14 @@ pub mod tuktuk {
pub fn close_task_queue_v0(ctx: Context<CloseTaskQueueV0>) -> Result<()> {
close_task_queue_v0::handler(ctx)
}

pub fn dummy_ix(ctx: Context<DummyIx>) -> Result<()> {
Err(error!(crate::error::ErrorCode::DummyInstruction))
}
}

#[derive(Accounts)]
pub struct DummyIx<'info> {
#[account(mut)]
pub dummy: Account<'info, RemoteTaskTransactionV0>,
}
17 changes: 7 additions & 10 deletions solana-programs/tests/tuktuk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ describe("tuktuk", () => {

it("allows running a task", async () => {
const taskAcc = await program.account.taskV0.fetch(task);

const ixs = await runTask({
program,
task,
Expand All @@ -227,20 +228,16 @@ describe("tuktuk", () => {
accounts: remainingAccounts.map((acc) => acc.pubkey),
},
});
const serialized = await RemoteTaskTransactionV0.serialize(
program.coder.accounts,
remoteTx
);
return {
remoteTaskTransaction: RemoteTaskTransactionV0.serialize(
program.coder.types,
remoteTx
),
remoteTaskTransaction: serialized,
remainingAccounts: remainingAccounts,
signature: Buffer.from(
sign.detached(
Uint8Array.from(
RemoteTaskTransactionV0.serialize(
program.coder.types,
remoteTx
)
),
Uint8Array.from(serialized),
signer.secretKey
)
),
Expand Down
2 changes: 1 addition & 1 deletion test-crons.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#! /bin/bash

cargo run -p tuktuk-cli -- -u http://127.0.0.1:8899 -w /Users/noahprince/.config/solana/id.json tuktuk-config create --min-deposit 10
cargo run -p tuktuk-cli -- -u http://127.0.0.1:8899 -w /Users/noahprince/.config/solana/id.json task-queue create --capacity 10 --name Noah --funding-amount 100000000 --min-crank-reward 10000
cargo run -p tuktuk-cli -- -u http://127.0.0.1:8899 -w /Users/noahprince/.config/solana/id.json task-queue create --capacity 10 --name Noah --funding-amount 100000000 --min-crank-reward 100000000
cargo run -p tuktuk-cli -- -u http://127.0.0.1:8899 -w /Users/noahprince/.config/solana/id.json cron create --name Noah --task-queue-name Noah --schedule "0 * * * * *" --free-tasks-per-transaction 0 --funding-amount 1000000000 --num-tasks-per-queue-call 8

cargo run -p tuktuk-cli -- -u http://127.0.0.1:8899 -w /Users/noahprince/.config/solana/id.json cron-transaction create-remote --url http://localhost:3002/remote --signer $(solana address) --cron-name Noah --index 0
Expand Down
Loading

0 comments on commit 6b36413

Please sign in to comment.