use std::cmp::Ordering;
use rialo_s_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
msg,
program::{invoke, invoke_signed},
program_error::ProgramError,
pubkey::Pubkey,
rent::Rent,
system_instruction, system_program,
sysvar::Sysvar,
};
use rialo_types::Nonce;
pub mod reexports {
pub use base64;
pub use bincode;
pub use rialo_events_core;
pub use rialo_oracle_processor_interface;
pub use rialo_oracle_registry_interface;
pub use rialo_s_program;
pub use rialo_subscriber_interface;
pub use rialo_types;
pub use serde;
}
pub const WORKFLOW_SEED: &str = "rialo_workflow";
pub fn derive_workflow_address<NONCE: Into<Nonce>>(
program_id: &Pubkey,
payer_key: &Pubkey,
nonce: NONCE,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[
WORKFLOW_SEED.as_bytes(),
payer_key.as_array(),
nonce.into().as_bytes(),
],
program_id,
)
}
pub fn write_to_storage<'account_info, D: serde::Serialize, NONCE: Into<Nonce>>(
program_id: &Pubkey,
slug: NONCE,
payer_account: &AccountInfo<'account_info>,
workflow_account: &AccountInfo<'account_info>,
system_program_account: &AccountInfo<'account_info>,
data: &D,
) -> ProgramResult {
if !system_program::check_id(system_program_account.key) {
msg!("Error: Incorrect system program ID");
return Err(ProgramError::IncorrectProgramId);
}
let slug: Nonce = slug.into();
let slug_bytes = slug.as_bytes();
let (workflow_pubkey, bump_seed) =
derive_workflow_address(program_id, payer_account.key, slug_bytes);
if workflow_pubkey != *workflow_account.key {
msg!(
"Error: Provided workflow account key {} doesn't match derived address {}",
workflow_account.key,
workflow_pubkey
);
return Err(ProgramError::InvalidAccountData);
}
let data_bytes = bincode::serialize(data).map_err(|_| ProgramError::InvalidAccountData)?;
let data_len = data_bytes.len();
let rent = Rent::get()?;
let minimum_balance_required = rent.minimum_balance(data_len);
if workflow_account.kelvins() == 0 {
let seeds = &[
WORKFLOW_SEED.as_bytes(),
payer_account.key.as_array(),
slug_bytes,
&[bump_seed],
];
msg!(
"Creating workflow account {} with {} bytes and {} kelvin",
workflow_account.key,
data_len,
minimum_balance_required,
);
invoke_signed(
&system_instruction::create_account(
payer_account.key,
workflow_account.key,
minimum_balance_required,
data_len as u64,
program_id,
),
&[payer_account.clone(), workflow_account.clone()],
&[seeds],
)?;
msg!(
"Workflow account {} initialized successfully",
workflow_account.key
);
} else {
if workflow_account.try_borrow_data()?.len() != data_len {
let original_data_len = workflow_account.data_len();
msg!(
"Resizing workflow account {} need {} bytes != allocated {}",
workflow_account.key,
data_len,
original_data_len
);
let current_balance = workflow_account.kelvins();
match minimum_balance_required.cmp(¤t_balance) {
Ordering::Greater => {
msg!(
"Workflow account {} received {} kelvin because it has grown",
workflow_account.key,
minimum_balance_required - current_balance,
);
invoke(
&system_instruction::transfer(
payer_account.key,
workflow_account.key,
minimum_balance_required - current_balance,
),
&[
payer_account.clone(),
workflow_account.clone(),
system_program_account.clone(),
],
)?;
}
Ordering::Less => {
msg!(
"Workflow account {} returned {} kelvin because it has shrunk",
workflow_account.key,
current_balance - minimum_balance_required,
);
let from_account = workflow_account;
let to_account = payer_account;
let kelvin = current_balance - minimum_balance_required;
if **from_account.try_borrow_mut_kelvins()? < kelvin {
return Err(ProgramError::InsufficientFunds);
}
**from_account.try_borrow_mut_kelvins()? -= kelvin;
**to_account.try_borrow_mut_kelvins()? += kelvin;
}
Ordering::Equal => {}
}
workflow_account.realloc(data_len, true)?;
msg!(
"Resized workflow account {} from {} to {} bytes",
workflow_account.key,
original_data_len,
data_len,
);
}
}
workflow_account
.try_borrow_mut_data()?
.copy_from_slice(&data_bytes);
Ok(())
}
pub fn read_from_storage<D: for<'de> serde::Deserialize<'de>>(
data: &[u8],
) -> Result<D, ProgramError> {
bincode::deserialize::<D>(data).map_err(|_| ProgramError::InvalidAccountData)
}
pub fn close_account(
payer_account: &AccountInfo<'_>,
workflow_account: &AccountInfo<'_>,
) -> ProgramResult {
workflow_account.try_borrow_mut_data()?.fill(0);
let workflow_kelvin = workflow_account.kelvins();
**workflow_account.try_borrow_mut_kelvins()? = 0;
**payer_account.try_borrow_mut_kelvins()? += workflow_kelvin;
Ok(())
}