From 88303c85450e6caf89fe98b4761db414dd73e7d8 Mon Sep 17 00:00:00 2001 From: De Facto Date: Thu, 4 Feb 2021 17:35:34 +0800 Subject: [PATCH] implement withdraw without test --- program/src/instruction.rs | 18 +------- program/src/lib.rs | 5 --- program/src/processor.rs | 86 +++++++++++++++++++++++++------------- program/src/state.rs | 40 ++++++++++++++++-- 4 files changed, 97 insertions(+), 52 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 1741c32..9738059 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -14,17 +14,14 @@ pub const PAYMENT_AMOUNT: u64 = 10; /// Instructions supported by the program #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq)] pub enum Instruction { - /// Initializes a new Aggregator Initialize { config: AggregatorConfig, }, - /// Add an oracle AddOracle { description: [u8; 32], }, - /// Remove an oracle RemoveOracle, Submit { @@ -32,19 +29,8 @@ pub enum Instruction { value: u64, }, - /// Oracle withdraw token - /// - /// Accounts expected by this instruction: - /// 0. `[writable]` The aggregator (key). - /// 1. `[writable]` The faucet (which token transfer from) - /// 2. `[writable]` The recevier (which token withdraw to) - /// 3. `[]` SPL Token program id - /// 4. `[]` The faucet owner - /// 5. `[signer, writable]` The oracle's authority. Withdraw { - /// withdraw amount - amount: u64, - /// program account nonced seed - seed: [u8; 32], + // FIXME: why 32 bytes seed? could be a vec? + faucet_owner_seed: [u8; 32], }, } diff --git a/program/src/lib.rs b/program/src/lib.rs index d213f57..30a8ce0 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -2,8 +2,6 @@ //! An Flux Aggregator program for the Solana blockchain - - pub mod borsh_state; pub mod borsh_utils; pub mod error; @@ -14,9 +12,6 @@ pub mod state; #[cfg(not(feature = "no-entrypoint"))] pub mod entrypoint; - - - /// Get median value from the aggregator account // pub fn get_median(aggregator_info: &AccountInfo) -> Result { // let aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?; diff --git a/program/src/processor.rs b/program/src/processor.rs index 81ccd46..c595e10 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -3,7 +3,7 @@ use crate::{ error::Error, instruction::Instruction, - state::{Aggregator, AggregatorConfig, Oracle, Round}, + state::{Aggregator, AggregatorConfig, Authority, Oracle, Round}, }; use solana_program::{ @@ -77,14 +77,8 @@ struct AddOracleContext<'a> { impl<'a> AddOracleContext<'a> { fn process(&self) -> ProgramResult { // Note: there can in fact be more oracles than max_submissions - if !self.aggregator_owner.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - let aggregator = Aggregator::load_initialized(self.aggregator)?; - if aggregator.owner != self.aggregator_owner.key.to_bytes() { - return Err(Error::OwnerMismatch)?; - } + aggregator.authorize(self.aggregator_owner)?; let mut oracle = Oracle::init_uninitialized(self.oracle)?; oracle.is_initialized = true; @@ -105,14 +99,8 @@ struct RemoveOracleContext<'a> { impl<'a> RemoveOracleContext<'a> { fn process(&self) -> ProgramResult { - if !self.aggregator_owner.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - let aggregator = Aggregator::load_initialized(self.aggregator)?; - if aggregator.owner != self.aggregator_owner.key.to_bytes() { - return Err(Error::OwnerMismatch)?; - } + aggregator.authorize(self.aggregator_owner)?; let oracle = Oracle::load_initialized(self.oracle)?; if oracle.aggregator != self.aggregator.key.to_bytes() { @@ -142,10 +130,7 @@ impl<'a> SubmitContext<'a> { fn process(&self) -> ProgramResult { let mut aggregator = Aggregator::load_initialized(self.aggregator)?; let mut oracle = Oracle::load_initialized(self.oracle)?; - - if !self.oracle_owner.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } + oracle.authorize(self.oracle_owner)?; if oracle.aggregator != self.aggregator.key.to_bytes() { return Err(Error::AggregatorMismatch)?; @@ -256,15 +241,53 @@ impl<'a> SubmitContext<'a> { } // Withdraw token from reward faucet to receiver account, deducting oracle's withdrawable credit. -struct WithdrawContext<'a> { - token: &'a AccountInfo<'a>, +struct WithdrawContext<'a, 'b> { + token_program: &'a AccountInfo<'a>, faucet: &'a AccountInfo<'a>, faucet_owner: &'a AccountInfo<'a>, // program signed - oracle: &'a AccountInfo<'a>, oracle_owner: &'a AccountInfo<'a>, // signed - receiver: &'a AccountInfo<'a>, + + faucet_owner_seed: &'b [u8], +} + +impl<'a, 'b> WithdrawContext<'a, 'b> { + fn process(&self) -> ProgramResult { + let mut oracle = Oracle::load_initialized(self.oracle)?; + oracle.authorize(&self.oracle_owner)?; + + if oracle.withdrawable == 0 { + return Err(Error::InsufficientWithdrawable)?; + } + + let amount = oracle.withdrawable; + + oracle.withdrawable = 0; + oracle.save(self.oracle)?; + + let inx = spl_token::instruction::transfer( + self.token_program.key, + self.faucet.key, + self.receiver.key, + self.faucet_owner.key, + &[], + amount, + )?; + + invoke_signed( + &inx, + &[ + self.token_program.clone(), + self.faucet.clone(), + self.faucet_owner.clone(), + self.receiver.clone(), + ], + &[&[self.faucet_owner_seed]], + )?; + + Ok(()) + } } /// Program state handler. @@ -314,11 +337,18 @@ impl Processor { value, } .process(), - _ => Err(ProgramError::InvalidInstructionData), - // Instruction::Withdraw { amount, seed } => { - // msg!("Instruction: Withdraw"); - // Self::process_withdraw(accounts, amount, seed) - // } + // _ => Err(ProgramError::InvalidInstructionData), + Instruction::Withdraw { faucet_owner_seed } => WithdrawContext { + token_program: accounts.get(0)?, + faucet: accounts.get(1)?, + faucet_owner: accounts.get(2)?, + oracle: accounts.get(3)?, + oracle_owner: accounts.get(4)?, + receiver: accounts.get(5)?, + + faucet_owner_seed: &faucet_owner_seed[..], + } + .process(), } } } diff --git a/program/src/state.rs b/program/src/state.rs index 2cf4ee4..a5a0b5a 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -1,10 +1,35 @@ //! State transition types use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use crate::borsh_state::{BorshState, InitBorshState}; use crate::instruction::MAX_ORACLES; +use crate::{ + borsh_state::{BorshState, InitBorshState}, + error::Error, +}; -use solana_program::program_pack::IsInitialized; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + program_pack::IsInitialized, +}; + +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)] +pub struct Pubkey([u8; 32]); + +pub trait Authority { + fn authority(&self) -> Pubkey; + + fn authorize(&self, account: &AccountInfo) -> ProgramResult { + if !account.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + + if self.authority().0 != account.key.to_bytes() { + return Err(Error::OwnerMismatch)?; + } + + Ok(()) + } +} #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)] pub struct AggregatorConfig { @@ -62,12 +87,16 @@ pub struct Aggregator { pub answer: Answer, } +impl Authority for Aggregator { + fn authority(&self) -> Pubkey { + Pubkey(self.owner) + } +} impl IsInitialized for Aggregator { fn is_initialized(&self) -> bool { self.is_initialized } } - impl BorshState for Aggregator {} impl InitBorshState for Aggregator {} @@ -106,6 +135,11 @@ pub struct Oracle { /// owner pub owner: [u8; 32], } +impl Authority for Oracle { + fn authority(&self) -> Pubkey { + Pubkey(self.owner) + } +} impl BorshState for Oracle {} impl IsInitialized for Oracle { fn is_initialized(&self) -> bool {