This commit is contained in:
De Facto 2021-02-03 10:42:53 +08:00
parent 995b05fafb
commit 89c1e56aa9
8 changed files with 3404 additions and 884 deletions

2413
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[workspace]
members = [
"program",
]

View File

@ -16,11 +16,10 @@ thiserror = "1.0"
num-derive = "0.3"
num-traits = "0.2"
num_enum = "0.5.1"
hex = "0.4"
[dev-dependencies]
solana-sdk = "1.4.8"
anyhow = "1.0"
hex = "0.4"
[lib]
crate-type = ["cdylib", "lib"]

View File

@ -10,9 +10,9 @@ use solana_program::{
entrypoint!(process_instruction);
// Program entrypoint's implementation
fn process_instruction(
fn process_instruction<'a>(
program_id: &Pubkey,
accounts: &[AccountInfo],
accounts: &'a [AccountInfo<'a>],
instruction_data: &[u8],
) -> ProgramResult {
if let Err(error) = Processor::process(program_id, accounts, instruction_data) {

View File

@ -95,20 +95,20 @@ pub enum Instruction {
},
}
impl Sealed for Instruction {}
impl Pack for Instruction {
const LEN: usize = 54;
// impl Sealed for Instruction {}
// impl Pack for Instruction {
// const LEN: usize = 54;
fn pack_into_slice(&self, dst: &mut [u8]) {
let data = self.pack_into_vec();
dst[..data.len()].copy_from_slice(&data);
}
// fn pack_into_slice(&self, dst: &mut [u8]) {
// let data = self.pack_into_vec();
// dst[..data.len()].copy_from_slice(&data);
// }
fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
let mut mut_src: &[u8] = src;
Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidInstructionData)
}
}
// fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
// let mut mut_src: &[u8] = src;
// Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidInstructionData)
// }
// }
impl Instruction {
fn pack_into_vec(&self) -> Vec<u8> {
@ -116,154 +116,123 @@ impl Instruction {
}
}
/// Below is for test
// /// Creates a `add_oracle` instruction
// pub fn add_oracle(
// program_id: &Pubkey,
// oracle_pubkey: &Pubkey,
// oracle_owner_pubkey: &Pubkey,
// aggregator_pubkey: &Pubkey,
// aggregator_owner_pubkey: &Pubkey,
// description: [u8; 32],
// ) -> SolInstruction {
// let accounts = vec![
// AccountMeta::new(*oracle_pubkey, false),
// AccountMeta::new(*oracle_owner_pubkey, false),
// AccountMeta::new_readonly(sysvar::clock::id(), false),
// AccountMeta::new(*aggregator_pubkey, false),
// AccountMeta::new_readonly(*aggregator_owner_pubkey, true),
// ];
/// Creates a `intialize` instruction.
pub fn initialize(
program_id: &Pubkey,
aggregator_pubkey: &Pubkey,
aggregator_owner_pubkey: &Pubkey,
submit_interval: u32,
min_submission_value: u64,
max_submission_value: u64,
submission_decimals: u8,
description: [u8; 32],
) -> SolInstruction {
let accounts = vec![
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new(*aggregator_pubkey, false),
AccountMeta::new_readonly(*aggregator_owner_pubkey, true),
];
// SolInstruction {
// program_id: *program_id,
// accounts,
// data: Instruction::AddOracle { description }.pack_into_vec(),
// }
// }
SolInstruction {
program_id: *program_id,
accounts,
data: Instruction::Initialize {
submit_interval,
min_submission_value,
max_submission_value,
submission_decimals,
description,
}
.pack_into_vec(),
}
}
// /// Creates a `remove_oracle` instruction
// pub fn remove_oracle(
// program_id: &Pubkey,
// aggregator_pubkey: &Pubkey,
// aggregator_owner_pubkey: &Pubkey,
// pubkey: &Pubkey,
// ) -> SolInstruction {
// let accounts = vec![
// AccountMeta::new(*aggregator_pubkey, false),
// AccountMeta::new_readonly(*aggregator_owner_pubkey, true),
// ];
/// Creates a `add_oracle` instruction
pub fn add_oracle(
program_id: &Pubkey,
oracle_pubkey: &Pubkey,
oracle_owner_pubkey: &Pubkey,
aggregator_pubkey: &Pubkey,
aggregator_owner_pubkey: &Pubkey,
description: [u8; 32],
) -> SolInstruction {
let accounts = vec![
AccountMeta::new(*oracle_pubkey, false),
AccountMeta::new(*oracle_owner_pubkey, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new(*aggregator_pubkey, false),
AccountMeta::new_readonly(*aggregator_owner_pubkey, true),
];
// SolInstruction {
// program_id: *program_id,
// accounts,
// data: Instruction::RemoveOracle {
// pubkey: pubkey.to_bytes(),
// }
// .pack_into_vec(),
// }
// }
SolInstruction {
program_id: *program_id,
accounts,
data: Instruction::AddOracle { description }.pack_into_vec(),
}
}
// /// Creates a `submit` instruction
// pub fn submit(
// program_id: &Pubkey,
// aggregator_pubkey: &Pubkey,
// oracle_pubkey: &Pubkey,
// oracle_owner_pubkey: &Pubkey,
// submission: u64,
// ) -> SolInstruction {
// let accounts = vec![
// AccountMeta::new(*aggregator_pubkey, false),
// AccountMeta::new_readonly(sysvar::clock::id(), false),
// AccountMeta::new(*oracle_pubkey, false),
// AccountMeta::new_readonly(*oracle_owner_pubkey, true),
// ];
/// Creates a `remove_oracle` instruction
pub fn remove_oracle(
program_id: &Pubkey,
aggregator_pubkey: &Pubkey,
aggregator_owner_pubkey: &Pubkey,
pubkey: &Pubkey,
) -> SolInstruction {
let accounts = vec![
AccountMeta::new(*aggregator_pubkey, false),
AccountMeta::new_readonly(*aggregator_owner_pubkey, true),
];
SolInstruction {
program_id: *program_id,
accounts,
data: Instruction::RemoveOracle {
pubkey: pubkey.to_bytes(),
}
.pack_into_vec(),
}
}
/// Creates a `submit` instruction
pub fn submit(
program_id: &Pubkey,
aggregator_pubkey: &Pubkey,
oracle_pubkey: &Pubkey,
oracle_owner_pubkey: &Pubkey,
submission: u64,
) -> SolInstruction {
let accounts = vec![
AccountMeta::new(*aggregator_pubkey, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new(*oracle_pubkey, false),
AccountMeta::new_readonly(*oracle_owner_pubkey, true),
];
SolInstruction {
program_id: *program_id,
accounts,
data: Instruction::Submit { submission }.pack_into_vec(),
}
}
// SolInstruction {
// program_id: *program_id,
// accounts,
// data: Instruction::Submit { submission }.pack_into_vec(),
// }
// }
#[cfg(test)]
mod tests {
use super::*;
use solana_program::{
entrypoint::ProgramResult,
};
use crate::borsh_utils;
use anyhow::Result;
use hex;
#[test]
fn test_get_packed_len() {
assert_eq!(
Instruction::get_packed_len(),
borsh_utils::get_packed_len::<Instruction>()
)
}
// #[test]
// fn test_get_packed_len() {
// assert_eq!(
// Instruction::get_packed_len(),
// borsh_utils::get_packed_len::<Instruction>()
// )
// }
#[test]
fn test_serialize_bytes() -> Result<()> {
let test_instruction = Instruction::Initialize {
submit_interval: 0x11221122,
min_submission_value: 0xaabbaabbaabbaabb,
max_submission_value: 0xccddccddccddccdd,
submission_decimals: 6,
description: [0xff; 32],
};
fn test_serialize_bytes() -> ProgramResult {
// let test_instruction = Instruction::Initialize {
// submit_interval: 0x11221122,
// min_submission_value: 0xaabbaabbaabbaabb,
// max_submission_value: 0xccddccddccddccdd,
// submission_decimals: 6,
// description: [0xff; 32],
// };
let bytes = test_instruction.try_to_vec()?;
// let bytes = test_instruction.try_to_vec()?;
assert_eq!(
"0022112211bbaabbaabbaabbaaddccddccddccddcc06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
hex::encode(bytes),
);
// assert_eq!(
// "0022112211bbaabbaabbaabbaaddccddccddccddcc06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
// hex::encode(bytes),
// );
Ok(())
}
#[test]
fn state_deserialize_invalid() -> Result<()> {
assert_eq!(
Instruction::unpack_from_slice(&hex::decode("0022112211bbaabbaabbaabbaaddccddccddccddcc06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")?),
Ok(Instruction::Initialize {
submit_interval: 0x11221122,
min_submission_value: 0xaabbaabbaabbaabb,
max_submission_value: 0xccddccddccddccdd,
submission_decimals: 6,
description: [0xff; 32],
}),
);
fn state_deserialize_invalid() -> ProgramResult {
// assert_eq!(
// Instruction::unpack_from_slice(&hex::decode("0022112211bbaabbaabbaabbaaddccddccddccddcc06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")?),
// Ok(Instruction::Initialize {
// submit_interval: 0x11221122,
// min_submission_value: 0xaabbaabbaabbaabb,
// max_submission_value: 0xccddccddccddccdd,
// submission_decimals: 6,
// description: [0xff; 32],
// }),
// );
// assert_eq!(
// Instruction::unpack_from_slice(&[

View File

@ -1,4 +1,3 @@
#![deny(missing_docs)]
#![forbid(unsafe_code)]
//! An Flux Aggregator program for the Solana blockchain
@ -18,34 +17,34 @@ use error::Error;
use state::Aggregator;
/// Get median value from the aggregator account
pub fn get_median(aggregator_info: &AccountInfo) -> Result<u64, ProgramError> {
let aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
if !aggregator.is_initialized {
return Err(Error::NotFoundAggregator.into());
}
// pub fn get_median(aggregator_info: &AccountInfo) -> Result<u64, ProgramError> {
// let aggregator = Aggregator::unpack_unchecked(&aggregator_info.data.borrow())?;
// if !aggregator.is_initialized {
// return Err(Error::NotFoundAggregator.into());
// }
let submissions = aggregator.submissions;
// let submissions = aggregator.submissions;
let mut values = vec![];
// let mut values = vec![];
// if the submission value is 0, maybe the oracle is not initialized
for s in &submissions {
if s.value != 0 {
values.push(s.value);
}
}
// // if the submission value is 0, maybe the oracle is not initialized
// for s in &submissions {
// if s.value != 0 {
// values.push(s.value);
// }
// }
// get median value
values.sort();
// // get median value
// values.sort();
let l = values.len();
let i = l / 2;
if l % 2 == 0 {
return Ok((values[i] + values[i - 1]) / 2);
} else {
return Ok(values[i]);
}
}
// let l = values.len();
// let i = l / 2;
// if l % 2 == 0 {
// return Ok((values[i] + values[i - 1]) / 2);
// } else {
// return Ok(values[i]);
// }
// }
// Export current sdk types for downstream users building with a different
pub use solana_program;

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,58 @@ use solana_program::{
clock::UnixTimestamp,
program_error::ProgramError,
program_pack::{IsInitialized, Pack, Sealed},
account_info::AccountInfo,
entrypoint::ProgramResult,
sysvar::rent::Rent,
msg,
};
pub trait BorshState: BorshDeserialize + BorshSerialize {
fn load(account: &AccountInfo) -> Result<Self, ProgramError> {
let data = (*account.data).borrow();
Self::try_from_slice(&data).map_err(|_| ProgramError::InvalidAccountData)
}
fn save(&self, account: &AccountInfo) -> ProgramResult {
let data = self.try_to_vec().map_err(|_| ProgramError::InvalidAccountData)?;
// FIXME: looks like there is association precedence issue that prevents
// RefMut from being automatically dereferenced.
//
// let dst = &mut account.data.borrow_mut();
//
// Why does it work in an SPL token program though?
//
// Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
let mut dst = (*account.data).borrow_mut();
if dst.len() != data.len() {
return Err(ProgramError::InvalidAccountData);
}
dst.copy_from_slice(&data);
Ok(())
}
fn save_exempt(&self, account: &AccountInfo, rent: &Rent) -> ProgramResult {
let data = self.try_to_vec().map_err(|_| ProgramError::InvalidAccountData)?;
if !rent.is_exempt(account.lamports(), data.len()) {
// FIXME: return a custom error
return Err(ProgramError::InvalidAccountData);
}
let mut dst = (*account.data).borrow_mut();
if dst.len() != data.len() {
// FIXME: return a custom error
return Err(ProgramError::InvalidAccountData);
}
dst.copy_from_slice(&data);
Ok(())
}
}
/// Aggregator data.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
pub struct Aggregator {
@ -30,27 +80,19 @@ pub struct Aggregator {
pub submissions: [Submission; MAX_ORACLES],
}
impl IsInitialized for Aggregator {
fn is_initialized(&self) -> bool {
self.is_initialized
}
impl BorshState for Aggregator {}
/// Submission data.
#[derive(Clone, Copy, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
pub struct Submission {
/// submit time
pub time: UnixTimestamp,
/// value
pub value: u64,
/// oracle
pub oracle: [u8; 32],
}
impl Sealed for Aggregator {}
impl Pack for Aggregator {
// 48 is submission packed length
const LEN: usize = 86 + MAX_ORACLES * 48;
fn pack_into_slice(&self, dst: &mut [u8]) {
let data = self.try_to_vec().unwrap();
dst[..data.len()].copy_from_slice(&data);
}
fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
let mut mut_src: &[u8] = src;
Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidAccountData)
}
}
/// Oracle data.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
@ -75,84 +117,75 @@ impl IsInitialized for Oracle {
}
}
impl Sealed for Oracle {}
impl Pack for Oracle {
const LEN: usize = 113;
// impl Sealed for Oracle {}
// impl Pack for Oracle {
// const LEN: usize = 113;
fn pack_into_slice(&self, dst: &mut [u8]) {
let data = self.try_to_vec().unwrap();
dst[..data.len()].copy_from_slice(&data);
}
// fn pack_into_slice(&self, dst: &mut [u8]) {
// let data = self.try_to_vec().unwrap();
// dst[..data.len()].copy_from_slice(&data);
// }
fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
let mut mut_src: &[u8] = src;
Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidAccountData)
}
}
// fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
// let mut mut_src: &[u8] = src;
// Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidAccountData)
// }
// }
/// Submission data.
#[derive(Clone, Copy, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
pub struct Submission {
/// submit time
pub time: UnixTimestamp,
/// value
pub value: u64,
/// oracle
pub oracle: [u8; 32],
}
impl Sealed for Submission {}
impl Pack for Submission {
const LEN: usize = 48;
fn pack_into_slice(&self, dst: &mut [u8]) {
let data = self.try_to_vec().unwrap();
dst[..data.len()].copy_from_slice(&data);
}
// impl Sealed for Submission {}
// impl Pack for Submission {
// const LEN: usize = 48;
fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
let mut mut_src: &[u8] = src;
Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidAccountData)
}
}
// fn pack_into_slice(&self, dst: &mut [u8]) {
// let data = self.try_to_vec().unwrap();
// dst[..data.len()].copy_from_slice(&data);
// }
#[cfg(test)]
mod tests {
use super::*;
use crate::borsh_utils;
// fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
// let mut mut_src: &[u8] = src;
// Self::deserialize(&mut mut_src).map_err(|_| ProgramError::InvalidAccountData)
// }
// }
#[test]
fn test_get_packed_len() {
assert_eq!(
Aggregator::get_packed_len(),
borsh_utils::get_packed_len::<Aggregator>()
);
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::borsh_utils;
assert_eq!(
Oracle::get_packed_len(),
borsh_utils::get_packed_len::<Oracle>()
);
// #[test]
// fn test_get_packed_len() {
// assert_eq!(
// Aggregator::get_packed_len(),
// borsh_utils::get_packed_len::<Aggregator>()
// );
assert_eq!(
Submission::get_packed_len(),
borsh_utils::get_packed_len::<Submission>()
);
}
// assert_eq!(
// Oracle::get_packed_len(),
// borsh_utils::get_packed_len::<Oracle>()
// );
#[test]
fn test_serialize_bytes() {
assert_eq!(
Submission {
time: 0,
value: 1,
oracle: [1; 32]
}
.try_to_vec()
.unwrap(),
vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
]
);
}
}
// assert_eq!(
// Submission::get_packed_len(),
// borsh_utils::get_packed_len::<Submission>()
// );
// }
// #[test]
// fn test_serialize_bytes() {
// assert_eq!(
// Submission {
// time: 0,
// value: 1,
// oracle: [1; 32]
// }
// .try_to_vec()
// .unwrap(),
// vec![
// 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
// ]
// );
// }
// }