NAC_Blockchain/protocol/nac-cbpp-l1/src/reputation.rs

553 lines
16 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! CBP声誉更新机制
//!
//! 实现CBP节点的声誉计算、衰减、恢复和查询功能
use crate::{CbpNode, CbppL1Error};
use nac_udm::primitives::Address;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// 声誉事件类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ReputationEvent {
/// 成功出块
BlockProduced,
/// 错过出块
BlockMissed,
/// 双签
DoubleSign,
/// 违规行为
Violation,
/// 长时间在线
LongUptime,
/// 宪法测试通过
ConstitutionTestPassed,
/// 社区贡献
CommunityContribution,
}
/// 声誉变化记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReputationChange {
/// CBP地址
pub address: Address,
/// 事件类型
pub event: ReputationEvent,
/// 变化前声誉
pub old_reputation: f64,
/// 变化后声誉
pub new_reputation: f64,
/// 变化量
pub delta: f64,
/// 时间戳
pub timestamp: u64,
/// 备注
pub note: Option<String>,
}
/// 声誉配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReputationConfig {
/// 初始声誉
pub initial_reputation: f64,
/// 最小声誉
pub min_reputation: f64,
/// 最大声誉
pub max_reputation: f64,
/// 衰减率(每天)
pub decay_rate: f64,
/// 衰减间隔(秒)
pub decay_interval: u64,
/// 恢复率(每次成功出块)
pub recovery_rate: f64,
/// 事件影响权重
pub event_weights: HashMap<ReputationEvent, f64>,
}
impl Default for ReputationConfig {
fn default() -> Self {
let mut event_weights = HashMap::new();
event_weights.insert(ReputationEvent::BlockProduced, 0.001);
event_weights.insert(ReputationEvent::BlockMissed, -0.01);
event_weights.insert(ReputationEvent::DoubleSign, -0.5);
event_weights.insert(ReputationEvent::Violation, -0.3);
event_weights.insert(ReputationEvent::LongUptime, 0.05);
event_weights.insert(ReputationEvent::ConstitutionTestPassed, 0.02);
event_weights.insert(ReputationEvent::CommunityContribution, 0.03);
Self {
initial_reputation: 0.5,
min_reputation: 0.0,
max_reputation: 1.0,
decay_rate: 0.001, // 每天衰减0.1%
decay_interval: 24 * 3600, // 1天
recovery_rate: 0.002, // 每次成功出块恢复0.2%
event_weights,
}
}
}
/// 声誉管理器
pub struct ReputationManager {
/// 声誉配置
config: ReputationConfig,
/// 声誉变化记录
changes: Vec<ReputationChange>,
/// 上次衰减时间
last_decay: HashMap<Address, u64>,
}
impl ReputationManager {
pub fn new() -> Self {
Self {
config: ReputationConfig::default(),
changes: Vec::new(),
last_decay: HashMap::new(),
}
}
pub fn with_config(config: ReputationConfig) -> Self {
Self {
config,
changes: Vec::new(),
last_decay: HashMap::new(),
}
}
/// 记录声誉事件
pub fn record_event(
&mut self,
node: &mut CbpNode,
event: ReputationEvent,
timestamp: u64,
note: Option<String>,
) -> Result<f64, CbppL1Error> {
let old_reputation = node.reputation;
// 获取事件权重
let weight = self.config.event_weights.get(&event)
.copied()
.unwrap_or(0.0);
// 计算新声誉
let mut new_reputation = old_reputation + weight;
new_reputation = new_reputation.max(self.config.min_reputation);
new_reputation = new_reputation.min(self.config.max_reputation);
// 更新节点声誉
node.reputation = new_reputation;
// 记录变化
let change = ReputationChange {
address: node.address,
event,
old_reputation,
new_reputation,
delta: new_reputation - old_reputation,
timestamp,
note,
};
self.changes.push(change);
Ok(new_reputation)
}
/// 应用声誉衰减
pub fn apply_decay(
&mut self,
node: &mut CbpNode,
timestamp: u64,
) -> Result<f64, CbppL1Error> {
// 获取上次衰减时间
let last_decay = self.last_decay.get(&node.address).copied().unwrap_or(node.registered_at);
// 检查是否需要衰减
if timestamp < last_decay + self.config.decay_interval {
return Ok(node.reputation);
}
// 计算衰减次数
let decay_count = (timestamp - last_decay) / self.config.decay_interval;
let old_reputation = node.reputation;
let mut new_reputation = old_reputation;
// 应用衰减
for _ in 0..decay_count {
new_reputation *= 1.0 - self.config.decay_rate;
new_reputation = new_reputation.max(self.config.min_reputation);
}
// 更新节点声誉
node.reputation = new_reputation;
// 更新上次衰减时间
self.last_decay.insert(node.address, timestamp);
// 记录变化
if (new_reputation - old_reputation).abs() > 0.0001 {
let change = ReputationChange {
address: node.address,
event: ReputationEvent::BlockMissed, // 使用BlockMissed表示衰减
old_reputation,
new_reputation,
delta: new_reputation - old_reputation,
timestamp,
note: Some(format!("Decay applied ({} intervals)", decay_count)),
};
self.changes.push(change);
}
Ok(new_reputation)
}
/// 恢复声誉
pub fn recover_reputation(
&mut self,
node: &mut CbpNode,
amount: f64,
timestamp: u64,
reason: String,
) -> Result<f64, CbppL1Error> {
let old_reputation = node.reputation;
let mut new_reputation = old_reputation + amount;
new_reputation = new_reputation.min(self.config.max_reputation);
// 更新节点声誉
node.reputation = new_reputation;
// 记录变化
let change = ReputationChange {
address: node.address,
event: ReputationEvent::CommunityContribution,
old_reputation,
new_reputation,
delta: new_reputation - old_reputation,
timestamp,
note: Some(reason),
};
self.changes.push(change);
Ok(new_reputation)
}
/// 批量更新声誉(用于出块奖励)
pub fn batch_update_for_blocks(
&mut self,
nodes: &mut [CbpNode],
timestamp: u64,
) -> Result<usize, CbppL1Error> {
let mut updated = 0;
for node in nodes.iter_mut() {
if node.blocks_produced > 0 {
self.record_event(
node,
ReputationEvent::BlockProduced,
timestamp,
Some(format!("Block production reward")),
)?;
updated += 1;
}
}
Ok(updated)
}
/// 查询声誉历史
pub fn get_reputation_history(
&self,
address: &Address,
limit: Option<usize>,
) -> Vec<&ReputationChange> {
let mut history: Vec<&ReputationChange> = self.changes.iter()
.filter(|c| &c.address == address)
.collect();
// 按时间倒序
history.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
if let Some(limit) = limit {
history.truncate(limit);
}
history
}
/// 查询声誉统计
pub fn get_reputation_statistics(&self, address: &Address) -> ReputationStatistics {
let history: Vec<&ReputationChange> = self.changes.iter()
.filter(|c| &c.address == address)
.collect();
if history.is_empty() {
return ReputationStatistics {
total_changes: 0,
positive_changes: 0,
negative_changes: 0,
total_gain: 0.0,
total_loss: 0.0,
current_reputation: self.config.initial_reputation,
};
}
let total_changes = history.len();
let positive_changes = history.iter().filter(|c| c.delta > 0.0).count();
let negative_changes = history.iter().filter(|c| c.delta < 0.0).count();
let total_gain: f64 = history.iter()
.filter(|c| c.delta > 0.0)
.map(|c| c.delta)
.sum();
let total_loss: f64 = history.iter()
.filter(|c| c.delta < 0.0)
.map(|c| c.delta.abs())
.sum();
let current_reputation = history.first().map(|c| c.new_reputation).unwrap_or(self.config.initial_reputation);
ReputationStatistics {
total_changes,
positive_changes,
negative_changes,
total_gain,
total_loss,
current_reputation,
}
}
/// 获取声誉排名
pub fn get_reputation_ranking(&self, nodes: &[CbpNode]) -> Vec<(Address, f64)> {
let mut ranking: Vec<(Address, f64)> = nodes.iter()
.map(|n| (n.address, n.reputation))
.collect();
// 按声誉降序排序
ranking.sort_by(|a, b| b.1.partial_cmp(&a.1).expect("FIX-006: unexpected None/Err"));
ranking
}
/// 更新配置
pub fn update_config(&mut self, config: ReputationConfig) {
self.config = config;
}
/// 获取配置
pub fn get_config(&self) -> &ReputationConfig {
&self.config
}
}
impl Default for ReputationManager {
fn default() -> Self {
Self::new()
}
}
/// 声誉统计
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReputationStatistics {
/// 总变化次数
pub total_changes: usize,
/// 正向变化次数
pub positive_changes: usize,
/// 负向变化次数
pub negative_changes: usize,
/// 总增长
pub total_gain: f64,
/// 总损失
pub total_loss: f64,
/// 当前声誉
pub current_reputation: f64,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CbpStatus;
fn create_test_node() -> CbpNode {
CbpNode {
address: Address::new([1u8; 32]),
did: "did:nac:test".to_string(),
stake_amount: 100_000_000_000,
kyc_level: 2,
hardware_score: 8000,
constitution_score: 80,
status: CbpStatus::Active,
registered_at: 1000,
last_active_at: 2000,
blocks_produced: 100,
reputation: 0.5,
}
}
#[test]
fn test_record_event_positive() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
let result = manager.record_event(
&mut node,
ReputationEvent::BlockProduced,
3000,
None,
);
assert!(result.is_ok());
assert!(node.reputation > 0.5);
}
#[test]
fn test_record_event_negative() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
let result = manager.record_event(
&mut node,
ReputationEvent::BlockMissed,
3000,
None,
);
assert!(result.is_ok());
assert!(node.reputation < 0.5);
}
#[test]
fn test_reputation_bounds() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
node.reputation = 0.99;
// 测试最大值
manager.record_event(&mut node, ReputationEvent::LongUptime, 3000, None).expect("FIX-006: unexpected None/Err");
assert!(node.reputation <= 1.0);
// 测试最小值
node.reputation = 0.01;
manager.record_event(&mut node, ReputationEvent::DoubleSign, 3000, None).expect("FIX-006: unexpected None/Err");
assert!(node.reputation >= 0.0);
}
#[test]
fn test_apply_decay() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
node.reputation = 0.8;
// 1天后衰减
let timestamp = node.registered_at + 24 * 3600;
let result = manager.apply_decay(&mut node, timestamp);
assert!(result.is_ok());
assert!(node.reputation < 0.8);
}
#[test]
fn test_no_decay_before_interval() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
node.reputation = 0.8;
// 不足1天不应该衰减
let timestamp = node.registered_at + 3600;
let result = manager.apply_decay(&mut node, timestamp);
assert!(result.is_ok());
assert_eq!(node.reputation, 0.8);
}
#[test]
fn test_recover_reputation() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
node.reputation = 0.5;
let result = manager.recover_reputation(
&mut node,
0.1,
3000,
"Community contribution".to_string(),
);
assert!(result.is_ok());
assert_eq!(node.reputation, 0.6);
}
#[test]
fn test_batch_update_for_blocks() {
let mut manager = ReputationManager::new();
let mut nodes = vec![
create_test_node(),
create_test_node(),
];
nodes[0].blocks_produced = 10;
nodes[1].blocks_produced = 5;
let result = manager.batch_update_for_blocks(&mut nodes, 3000);
assert!(result.is_ok());
assert_eq!(result.expect("mainnet: handle error"), 2);
}
#[test]
fn test_get_reputation_history() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
manager.record_event(&mut node, ReputationEvent::BlockProduced, 1000, None).expect("FIX-006: unexpected None/Err");
manager.record_event(&mut node, ReputationEvent::BlockProduced, 2000, None).expect("FIX-006: unexpected None/Err");
manager.record_event(&mut node, ReputationEvent::BlockMissed, 3000, None).expect("FIX-006: unexpected None/Err");
let history = manager.get_reputation_history(&node.address, None);
assert_eq!(history.len(), 3);
// 限制数量
let limited = manager.get_reputation_history(&node.address, Some(2));
assert_eq!(limited.len(), 2);
}
#[test]
fn test_get_reputation_statistics() {
let mut manager = ReputationManager::new();
let mut node = create_test_node();
manager.record_event(&mut node, ReputationEvent::BlockProduced, 1000, None).expect("FIX-006: unexpected None/Err");
manager.record_event(&mut node, ReputationEvent::BlockProduced, 2000, None).expect("FIX-006: unexpected None/Err");
manager.record_event(&mut node, ReputationEvent::BlockMissed, 3000, None).expect("FIX-006: unexpected None/Err");
let stats = manager.get_reputation_statistics(&node.address);
assert_eq!(stats.total_changes, 3);
assert_eq!(stats.positive_changes, 2);
assert_eq!(stats.negative_changes, 1);
}
#[test]
fn test_get_reputation_ranking() {
let manager = ReputationManager::new();
let mut nodes = vec![
create_test_node(),
create_test_node(),
create_test_node(),
];
nodes[0].reputation = 0.8;
nodes[1].reputation = 0.6;
nodes[2].reputation = 0.9;
let ranking = manager.get_reputation_ranking(&nodes);
assert_eq!(ranking.len(), 3);
assert_eq!(ranking[0].1, 0.9);
assert_eq!(ranking[1].1, 0.8);
assert_eq!(ranking[2].1, 0.6);
}
#[test]
fn test_update_config() {
let mut manager = ReputationManager::new();
let mut new_config = ReputationConfig::default();
new_config.decay_rate = 0.002;
new_config.recovery_rate = 0.003;
manager.update_config(new_config);
let config = manager.get_config();
assert_eq!(config.decay_rate, 0.002);
assert_eq!(config.recovery_rate, 0.003);
}
}