157 lines
4.3 KiB
Rust
157 lines
4.3 KiB
Rust
//! # 收益数据源预言机
|
|
//!
|
|
//! Oracle for dividend data sources
|
|
|
|
use super::*;
|
|
use crate::primitives::{Address, Timestamp};
|
|
|
|
/// 收益数据源类型
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum DataSourceType {
|
|
/// 租金收益
|
|
Rent,
|
|
/// 股息分红
|
|
Dividend,
|
|
/// 利息收益
|
|
Interest,
|
|
}
|
|
|
|
/// 预言机数据
|
|
#[derive(Debug, Clone)]
|
|
pub struct OracleData {
|
|
/// 数据源类型
|
|
pub source_type: DataSourceType,
|
|
/// 资产地址
|
|
pub asset_address: Address,
|
|
/// 收益金额
|
|
pub amount: u128,
|
|
/// 数据时间戳
|
|
pub timestamp: Timestamp,
|
|
/// 数据签名
|
|
pub signature: Vec<u8>,
|
|
}
|
|
|
|
/// 收益预言机
|
|
pub struct DividendOracle {
|
|
/// 预言机节点列表
|
|
pub oracle_nodes: Vec<Address>,
|
|
/// 最小共识节点数
|
|
pub min_consensus: usize,
|
|
}
|
|
|
|
impl DividendOracle {
|
|
/// 创建新的预言机
|
|
pub fn new(oracle_nodes: Vec<Address>, min_consensus: usize) -> Self {
|
|
DividendOracle {
|
|
oracle_nodes,
|
|
min_consensus,
|
|
}
|
|
}
|
|
|
|
/// 获取收益数据
|
|
pub fn fetch_dividend_data(
|
|
&self,
|
|
asset_address: Address,
|
|
source_type: DataSourceType,
|
|
) -> Result<OracleData, String> {
|
|
// 简化实现:模拟从多个预言机节点获取数据
|
|
if self.oracle_nodes.len() < self.min_consensus {
|
|
return Err("Insufficient oracle nodes".to_string());
|
|
}
|
|
|
|
// 模拟数据获取
|
|
Ok(OracleData {
|
|
source_type,
|
|
asset_address,
|
|
amount: 1000000, // 模拟金额
|
|
timestamp: Timestamp::now(),
|
|
signature: vec![0u8; 64],
|
|
})
|
|
}
|
|
|
|
/// 验证预言机数据
|
|
pub fn verify_data(&self, data: &OracleData) -> bool {
|
|
// 简化实现:验证签名和时间戳
|
|
!data.signature.is_empty() && data.amount > 0
|
|
}
|
|
|
|
/// 聚合多个预言机数据
|
|
pub fn aggregate_data(&self, data_list: Vec<OracleData>) -> Result<u128, String> {
|
|
if data_list.len() < self.min_consensus {
|
|
return Err("Insufficient data for consensus".to_string());
|
|
}
|
|
|
|
// 计算中位数
|
|
let mut amounts: Vec<u128> = data_list.iter().map(|d| d.amount).collect();
|
|
amounts.sort_unstable();
|
|
|
|
let median = if amounts.len() % 2 == 0 {
|
|
(amounts[amounts.len() / 2 - 1] + amounts[amounts.len() / 2]) / 2
|
|
} else {
|
|
amounts[amounts.len() / 2]
|
|
};
|
|
|
|
Ok(median)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_oracle_creation() {
|
|
let nodes = vec![Address::zero(), Address::zero(), Address::zero()];
|
|
let oracle = DividendOracle::new(nodes, 2);
|
|
|
|
assert_eq!(oracle.oracle_nodes.len(), 3);
|
|
assert_eq!(oracle.min_consensus, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_fetch_dividend_data() {
|
|
let nodes = vec![Address::zero(), Address::zero(), Address::zero()];
|
|
let oracle = DividendOracle::new(nodes, 2);
|
|
|
|
let result = oracle.fetch_dividend_data(Address::zero(), DataSourceType::Rent);
|
|
assert!(result.is_ok());
|
|
|
|
let data = result.expect("FIX-006: unexpected None/Err");
|
|
assert!(data.amount > 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_data_aggregation() {
|
|
let nodes = vec![Address::zero(), Address::zero(), Address::zero()];
|
|
let oracle = DividendOracle::new(nodes, 2);
|
|
|
|
let data_list = vec![
|
|
OracleData {
|
|
source_type: DataSourceType::Rent,
|
|
asset_address: Address::zero(),
|
|
amount: 1000,
|
|
timestamp: Timestamp::now(),
|
|
signature: vec![0u8; 64],
|
|
},
|
|
OracleData {
|
|
source_type: DataSourceType::Rent,
|
|
asset_address: Address::zero(),
|
|
amount: 1200,
|
|
timestamp: Timestamp::now(),
|
|
signature: vec![0u8; 64],
|
|
},
|
|
OracleData {
|
|
source_type: DataSourceType::Rent,
|
|
asset_address: Address::zero(),
|
|
amount: 1100,
|
|
timestamp: Timestamp::now(),
|
|
signature: vec![0u8; 64],
|
|
},
|
|
];
|
|
|
|
let result = oracle.aggregate_data(data_list);
|
|
assert!(result.is_ok());
|
|
assert_eq!(result.expect("mainnet: handle error"), 1100); // 中位数
|
|
}
|
|
}
|