solana-flux-aggregator/src/processor.rs

409 lines
13 KiB
Rust
Raw Normal View History

2020-11-26 14:19:50 -08:00
//! Program state processor
use crate::{
error::Error,
instruction::{Instruction, PAYMENT_AMOUNT},
state::{Aggregator, Oracle, Submission},
2020-11-26 14:19:50 -08:00
};
2020-11-27 22:52:54 -08:00
2020-11-26 14:19:50 -08:00
use num_traits::FromPrimitive;
use solana_program::{
account_info::{next_account_info, AccountInfo},
2020-11-27 22:52:54 -08:00
clock::{Clock},
2020-11-26 14:19:50 -08:00
decode_error::DecodeError,
entrypoint::ProgramResult,
2020-11-27 01:38:49 -08:00
info,
2020-11-26 14:19:50 -08:00
program_pack::{Pack},
2020-12-04 04:11:10 -08:00
program::{invoke_signed},
2020-11-27 22:52:54 -08:00
program_error::{PrintProgramError, ProgramError},
2020-11-26 14:19:50 -08:00
pubkey::Pubkey,
sysvar::{rent::Rent, Sysvar},
};
/// Program state handler.
pub struct Processor {}
impl Processor {
/// Processes an [Instruction](enum.Instruction.html).
2020-12-01 19:42:09 -08:00
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
2020-11-26 14:19:50 -08:00
let instruction = Instruction::unpack(input)?;
match instruction {
Instruction::Initialize {
submit_interval,
2020-11-26 14:19:50 -08:00
min_submission_value,
max_submission_value,
2020-12-02 22:45:16 -08:00
description,
2020-11-26 14:19:50 -08:00
} => {
info!("Instruction: Initialize");
Self::process_initialize(
program_id, accounts, submit_interval, min_submission_value,
2020-12-02 22:45:16 -08:00
max_submission_value, description,
2020-11-26 14:19:50 -08:00
)
},
Instruction::AddOracle {
2020-11-30 09:41:09 -08:00
description,
2020-11-26 14:19:50 -08:00
} => {
info!("Instruction: AddOracle");
2020-11-27 01:38:49 -08:00
Self::process_add_oracle(
2020-12-01 02:50:31 -08:00
accounts, description,
2020-11-27 01:38:49 -08:00
)
2020-11-26 14:19:50 -08:00
},
Instruction::RemoveOracle {
2020-12-01 02:50:31 -08:00
oracle,
2020-11-26 14:19:50 -08:00
} => {
info!("Instruction: RemoveOracle");
2020-11-27 22:52:54 -08:00
Self::process_remove_oracle(
2020-12-01 02:50:31 -08:00
accounts, oracle,
2020-11-27 22:52:54 -08:00
)
2020-11-26 14:19:50 -08:00
},
Instruction::Submit {
submission,
} => {
info!("Instruction: Submit");
2020-11-27 22:52:54 -08:00
Self::process_submit(
accounts, submission,
)
2020-11-26 14:19:50 -08:00
},
2020-11-30 09:41:09 -08:00
Instruction::Withdraw {
amount,
2020-12-02 22:45:16 -08:00
seed,
2020-11-30 09:41:09 -08:00
} => {
info!("Instruction: Withdraw");
Self::process_withdraw(
2020-12-02 22:45:16 -08:00
accounts, amount, seed.as_slice(),
2020-11-30 09:41:09 -08:00
)
},
2020-11-26 14:19:50 -08:00
}
}
/// Processes an [Initialize](enum.Instruction.html) instruction.
2020-11-30 09:41:09 -08:00
///
/// Accounts expected by this instruction:
///
2020-12-04 04:11:10 -08:00
/// 1. `[]` Sysvar rent
/// 2. `[writable, signer]` The aggregator autority
2020-11-26 14:19:50 -08:00
pub fn process_initialize(
_program_id: &Pubkey,
2020-11-26 14:19:50 -08:00
accounts: &[AccountInfo],
submit_interval: u32,
2020-11-26 14:19:50 -08:00
min_submission_value: u64,
max_submission_value: u64,
2020-12-02 22:45:16 -08:00
description: [u8; 32],
2020-11-26 14:19:50 -08:00
) -> ProgramResult {
2020-12-01 19:42:09 -08:00
2020-11-26 14:19:50 -08:00
let account_info_iter = &mut accounts.iter();
2020-12-01 19:42:09 -08:00
2020-11-30 09:41:09 -08:00
let rent_info = next_account_info(account_info_iter)?;
2020-12-04 04:11:10 -08:00
let aggregator_info = next_account_info(account_info_iter)?;
2020-11-30 09:41:09 -08:00
// check signer
2020-12-04 04:11:10 -08:00
if !aggregator_info.is_signer {
2020-11-30 09:41:09 -08:00
return Err(ProgramError::MissingRequiredSignature);
}
2020-12-01 19:42:09 -08:00
2020-11-30 09:41:09 -08:00
let rent = &Rent::from_account_info(rent_info)?;
2020-11-26 14:19:50 -08:00
let mut aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
if aggregator.is_initialized {
return Err(Error::AlreadyInUse.into());
}
2020-11-30 09:41:09 -08:00
if !rent.is_exempt(aggregator_info.lamports(), aggregator_info.data_len()) {
2020-11-26 14:19:50 -08:00
return Err(Error::NotRentExempt.into());
}
2020-12-01 19:42:09 -08:00
aggregator.submit_interval = submit_interval;
2020-11-26 14:19:50 -08:00
aggregator.min_submission_value = min_submission_value;
aggregator.max_submission_value = max_submission_value;
aggregator.description = description;
aggregator.is_initialized = true;
2020-11-30 09:41:09 -08:00
2020-11-26 14:19:50 -08:00
Aggregator::pack(aggregator, &mut aggregator_info.data.borrow_mut())?;
2020-12-01 19:42:09 -08:00
2020-11-26 14:19:50 -08:00
Ok(())
}
/// Processes an [AddOracle](enum.Instruction.html) instruction.
2020-11-27 22:52:54 -08:00
///
2020-12-01 02:50:31 -08:00
/// @description: the oracle name
2020-11-27 22:52:54 -08:00
///
/// Accounts expected by this instruction:
///
2020-12-01 19:42:09 -08:00
/// 0. `[writable]` The oracle(key)
/// 1. `[]` Clock sysvar
/// 2. `[writable, signer]` The aggregator's authority.
2020-11-26 14:19:50 -08:00
pub fn process_add_oracle(
2020-11-27 01:38:49 -08:00
accounts: &[AccountInfo],
2020-11-30 09:41:09 -08:00
description: [u8; 32],
2020-11-26 14:19:50 -08:00
) -> ProgramResult {
2020-12-01 19:42:09 -08:00
2020-11-27 01:38:49 -08:00
let account_info_iter = &mut accounts.iter();
2020-12-01 02:50:31 -08:00
let oracle_info = next_account_info(account_info_iter)?;
2020-11-27 22:52:54 -08:00
let clock_sysvar_info = next_account_info(account_info_iter)?;
2020-12-01 19:42:09 -08:00
let aggregator_info = next_account_info(account_info_iter)?;
if !aggregator_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
2020-11-27 22:52:54 -08:00
2020-12-01 19:42:09 -08:00
let mut aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
2020-11-27 22:52:54 -08:00
2020-11-27 01:38:49 -08:00
if !aggregator.is_initialized {
return Err(Error::NotFoundAggregator.into());
}
2020-12-01 19:42:09 -08:00
2020-12-04 04:11:10 -08:00
let mut oracle = Oracle::unpack_unchecked(&oracle_info.data.borrow())?;
if oracle.is_initialized {
return Err(Error::AlreadyInUse.into());
}
// sys clock
let clock = &Clock::from_account_info(clock_sysvar_info)?;
let mut submissions = aggregator.submissions;
2020-12-01 19:42:09 -08:00
// default submission
for s in submissions.iter_mut() {
if s.oracle == Pubkey::default() {
*s = Submission {
time: clock.unix_timestamp,
value: 0,
oracle: *oracle_info.key,
};
2020-12-04 04:11:10 -08:00
break;
2020-12-01 02:50:31 -08:00
}
}
2020-11-27 01:38:49 -08:00
aggregator.submissions = submissions;
2020-11-27 01:38:49 -08:00
Aggregator::pack(aggregator, &mut aggregator_info.data.borrow_mut())?;
2020-12-01 19:42:09 -08:00
2020-12-01 02:50:31 -08:00
oracle.submission = 0;
oracle.next_submit_time = clock.unix_timestamp;
oracle.description = description;
oracle.is_initialized = true;
oracle.withdrawable = 0;
oracle.aggregator = *aggregator_info.key;
2020-12-01 02:50:31 -08:00
Oracle::pack(oracle, &mut oracle_info.data.borrow_mut())?;
2020-11-27 01:38:49 -08:00
2020-11-26 14:19:50 -08:00
Ok(())
}
/// Processes an [RemoveOracle](enum.Instruction.html) instruction.
2020-11-27 22:52:54 -08:00
///
/// @seat: the oracle's index of the aggregator
///
/// Accounts expected by this instruction:
///
2020-12-01 19:42:09 -08:00
/// 0. `[writable, signer]` The aggregator's authority.
2020-11-26 14:19:50 -08:00
pub fn process_remove_oracle(
2020-11-27 22:52:54 -08:00
accounts: &[AccountInfo],
2020-12-01 02:50:31 -08:00
oracle: Pubkey,
2020-11-26 14:19:50 -08:00
) -> ProgramResult {
2020-11-27 22:52:54 -08:00
let account_info_iter = &mut accounts.iter();
let aggregator_info = next_account_info(account_info_iter)?;
2020-12-01 19:42:09 -08:00
if !aggregator_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
2020-11-27 22:52:54 -08:00
let mut aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
if !aggregator.is_initialized {
return Err(Error::NotFoundAggregator.into());
}
// remove submission
let mut submissions = aggregator.submissions;
2020-11-27 22:52:54 -08:00
2020-12-04 04:11:10 -08:00
let mut found = false;
for s in submissions.iter_mut() {
if s.oracle == oracle {
*s = Submission::default();
2020-12-04 04:11:10 -08:00
found = true;
break;
2020-12-01 02:50:31 -08:00
}
}
2020-11-27 22:52:54 -08:00
2020-12-04 04:11:10 -08:00
if !found {
return Err(Error::NotFoundOracle.into());
}
aggregator.submissions = submissions;
2020-11-27 22:52:54 -08:00
Aggregator::pack(aggregator, &mut aggregator_info.data.borrow_mut())?;
2020-11-26 14:19:50 -08:00
Ok(())
}
/// Processes an [Submit](enum.Instruction.html) instruction.
2020-11-27 22:52:54 -08:00
/// @submission: the updated data that the oracle is submitting
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The aggregator(key).
/// 1. `[]` Clock sysvar
2020-12-01 02:50:31 -08:00
/// 1. `[signer, writable]` The oracle's authority.
2020-11-26 14:19:50 -08:00
pub fn process_submit(
2020-11-27 22:52:54 -08:00
accounts: &[AccountInfo],
submission: u64,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let aggregator_info = next_account_info(account_info_iter)?;
let clock_sysvar_info = next_account_info(account_info_iter)?;
2020-12-01 02:50:31 -08:00
let oracle_info = next_account_info(account_info_iter)?;
2020-11-27 22:52:54 -08:00
let mut aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
2020-11-27 22:52:54 -08:00
if !aggregator.is_initialized {
return Err(Error::NotFoundAggregator.into());
}
if submission < aggregator.min_submission_value || submission > aggregator.max_submission_value {
return Err(Error::SubmissonValueOutOfRange.into());
}
2020-12-01 02:50:31 -08:00
if !oracle_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
2020-11-27 22:52:54 -08:00
}
2020-12-01 02:50:31 -08:00
let mut oracle = Oracle::unpack_unchecked(&oracle_info.data.borrow())?;
if !oracle.is_initialized {
2020-11-27 22:52:54 -08:00
return Err(Error::NotFoundOracle.into());
}
if &oracle.aggregator != aggregator_info.key {
return Err(Error::AggregatorKeyNotMatch.into());
}
2020-12-01 02:50:31 -08:00
let clock = &Clock::from_account_info(clock_sysvar_info)?;
// check whether the aggregator owned this oracle
let mut found = false;
let mut submissions = aggregator.submissions;
for s in submissions.iter_mut() {
if &s.oracle == oracle_info.key {
s.value = submission;
s.time = clock.unix_timestamp;
found = true;
break;
}
}
if !found {
return Err(Error::NotFoundOracle.into());
}
aggregator.submissions = submissions;
Aggregator::pack(aggregator, &mut aggregator_info.data.borrow_mut())?;
2020-12-01 02:50:31 -08:00
if oracle.next_submit_time > clock.unix_timestamp {
return Err(Error::SubmissonCooling.into());
}
oracle.submission = submission;
oracle.withdrawable += PAYMENT_AMOUNT;
oracle.next_submit_time = clock.unix_timestamp + aggregator.submit_interval as i64;
2020-12-01 02:50:31 -08:00
Oracle::pack(oracle, &mut oracle_info.data.borrow_mut())?;
2020-11-27 22:52:54 -08:00
Ok(())
}
2020-11-30 09:41:09 -08:00
/// Processes an [Withdraw](enum.Instruction.html) instruction
/// Can only be called by the oracle admin
///
/// @to: the address to send the token to
/// @amount: the amount of token to send
///
/// Accounts expected by this instruction:
///
2020-12-02 22:45:16 -08:00
/// 0. `[writable]` The aggregator (key).
/// 1. `[writable]` The faucet (which token transfer from)
/// 2. `[writable]` The recevier (which token withdraw to)
2020-11-30 09:41:09 -08:00
/// 3. `[]` SPL Token program id
/// 4. `[]` The faucet owner
2020-12-01 02:50:31 -08:00
/// 5. `[signer, writable]` The oracle's authority.
2020-11-30 09:41:09 -08:00
pub fn process_withdraw(
accounts: &[AccountInfo],
amount: u64,
2020-12-02 22:45:16 -08:00
seed: &[u8],
2020-11-26 14:19:50 -08:00
) -> ProgramResult {
2020-11-30 09:41:09 -08:00
let account_info_iter = &mut accounts.iter();
let aggregator_info = next_account_info(account_info_iter)?;
2020-12-02 22:45:16 -08:00
let faucet_info = next_account_info(account_info_iter)?;
2020-11-30 09:41:09 -08:00
let receiver_info = next_account_info(account_info_iter)?;
let token_program_info = next_account_info(account_info_iter)?;
let faucet_owner_info = next_account_info(account_info_iter)?;
2020-12-01 02:50:31 -08:00
let oracle_info = next_account_info(account_info_iter)?;
2020-11-30 09:41:09 -08:00
2020-12-01 02:50:31 -08:00
if !oracle_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
let aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
2020-11-30 09:41:09 -08:00
if !aggregator.is_initialized {
return Err(Error::NotFoundAggregator.into());
}
2020-12-01 02:50:31 -08:00
let mut oracle = Oracle::unpack_unchecked(&oracle_info.data.borrow())?;
if !oracle.is_initialized {
2020-11-30 09:41:09 -08:00
return Err(Error::NotFoundOracle.into());
}
2020-12-01 02:50:31 -08:00
if oracle.withdrawable < amount {
2020-11-30 09:41:09 -08:00
return Err(Error::InsufficientWithdrawable.into());
}
2020-12-01 02:50:31 -08:00
2020-12-02 22:45:16 -08:00
info!("Create transfer instruction...");
2020-11-30 09:41:09 -08:00
let instruction = spl_token::instruction::transfer(
token_program_info.key,
2020-12-02 22:45:16 -08:00
faucet_info.key,
2020-11-30 09:41:09 -08:00
receiver_info.key,
faucet_owner_info.key,
&[],
amount,
)?;
info!("Invoke signed...");
invoke_signed(
&instruction,
&[
2020-12-02 22:45:16 -08:00
faucet_info.clone(),
2020-11-30 09:41:09 -08:00
token_program_info.clone(),
receiver_info.clone(),
faucet_owner_info.clone(),
],
2020-12-02 22:45:16 -08:00
&[&[seed]]
2020-11-30 09:41:09 -08:00
)?;
// update oracle
2020-12-01 02:50:31 -08:00
oracle.withdrawable -= amount;
Oracle::pack(oracle, &mut oracle_info.data.borrow_mut())?;
2020-11-30 09:41:09 -08:00
2020-11-26 14:19:50 -08:00
Ok(())
}
2020-11-30 09:41:09 -08:00
}
2020-11-26 14:19:50 -08:00
impl PrintProgramError for Error {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
{
match self {
Error::InvalidInstruction => info!("Error: Invalid instruction"),
Error::AlreadyInUse => info!("Error: Already in use"),
Error::NotRentExempt => info!("Error: No rent exempt"),
2020-11-27 01:38:49 -08:00
Error::NotFoundAggregator => info!("Error: no found aggregator"),
Error::OracleAdded => info!("Error: Oracle added"),
2020-11-27 22:52:54 -08:00
Error::OwnerMismatch => info!("Error: Owner mismatch"),
Error::NotFoundOracle => info!("Error: Not found oracle"),
Error::SubmissonValueOutOfRange => info!("Error: Submisson value out of range"),
Error::SubmissonCooling => info!("Submission cooling"),
2020-11-30 09:41:09 -08:00
Error::InsufficientWithdrawable => info!("Insufficient withdrawable"),
Error::AggregatorKeyNotMatch => info!("Aggregator key not match"),
2020-11-26 14:19:50 -08:00
}
}
}