485 lines
15 KiB
Rust
485 lines
15 KiB
Rust
// NVM-L0 DAG数据结构(预留用于未来兼容)
|
||
//
|
||
// ⚠️ 重要说明:
|
||
// 1. NAC使用CBPP共识机制,不是DAG共识
|
||
// 2. 此文件中的DAG是作为数据结构使用,不是共识机制
|
||
// 3. 当前版本暂不使用,预留用于未来与其他DAG链的兼容
|
||
// 4. 如需使用,必须明确区分"DAG数据结构"和"DAG共识机制"
|
||
use crate::types::Hash;
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::{HashMap, HashSet, VecDeque};
|
||
use std::time::{SystemTime, UNIX_EPOCH};
|
||
|
||
/// DAG节点
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct DagNode {
|
||
pub hash: Hash,
|
||
pub parents: Vec<Hash>,
|
||
pub data: Vec<u8>,
|
||
pub timestamp: u64,
|
||
pub weight: u64,
|
||
pub cumulative_weight: u64,
|
||
}
|
||
|
||
/// DAG图结构
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct DagGraph {
|
||
nodes: HashMap<Hash, DagNode>,
|
||
tips: HashSet<Hash>,
|
||
genesis_hash: Hash,
|
||
}
|
||
|
||
impl DagGraph {
|
||
/// 创建新的DAG图
|
||
pub fn new(genesis_hash: Hash) -> Self {
|
||
let mut tips = HashSet::new();
|
||
tips.insert(genesis_hash);
|
||
Self {
|
||
nodes: HashMap::new(),
|
||
tips,
|
||
genesis_hash,
|
||
}
|
||
}
|
||
|
||
/// 添加节点到DAG
|
||
pub fn add_node(&mut self, mut node: DagNode) -> Result<(), DagError> {
|
||
// 验证父节点存在
|
||
for parent in &node.parents {
|
||
if !self.nodes.contains_key(parent) && *parent != self.genesis_hash {
|
||
return Err(DagError::ParentNotFound(*parent));
|
||
}
|
||
}
|
||
|
||
// 计算累积权重
|
||
node.cumulative_weight = self.calculate_cumulative_weight(&node)?;
|
||
|
||
// 更新tips
|
||
for parent in &node.parents {
|
||
self.tips.remove(parent);
|
||
}
|
||
self.tips.insert(node.hash);
|
||
|
||
// 添加节点
|
||
self.nodes.insert(node.hash, node);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 获取节点
|
||
pub fn get_node(&self, hash: &Hash) -> Option<&DagNode> {
|
||
self.nodes.get(hash)
|
||
}
|
||
|
||
/// 获取所有tips
|
||
pub fn get_tips(&self) -> Vec<Hash> {
|
||
self.tips.iter().copied().collect()
|
||
}
|
||
|
||
/// 选择父节点(用于新交易)
|
||
/// 使用加权随机算法选择2个父节点
|
||
pub fn select_parents(&self) -> Vec<Hash> {
|
||
let tips: Vec<_> = self.tips.iter().copied().collect();
|
||
|
||
if tips.is_empty() {
|
||
return vec![self.genesis_hash];
|
||
}
|
||
|
||
if tips.len() == 1 {
|
||
return tips;
|
||
}
|
||
|
||
// 选择权重最高的2个tips
|
||
let mut sorted_tips: Vec<_> = tips.iter()
|
||
.filter_map(|tip| {
|
||
self.nodes.get(tip).map(|node| (*tip, node.cumulative_weight))
|
||
})
|
||
.collect();
|
||
|
||
sorted_tips.sort_by(|a, b| b.1.cmp(&a.1));
|
||
|
||
sorted_tips.iter()
|
||
.take(2)
|
||
.map(|(hash, _)| *hash)
|
||
.collect()
|
||
}
|
||
|
||
/// 计算累积权重
|
||
fn calculate_cumulative_weight(&self, node: &DagNode) -> Result<u64, DagError> {
|
||
let mut max_parent_weight = 0u64;
|
||
|
||
for parent in &node.parents {
|
||
if let Some(parent_node) = self.nodes.get(parent) {
|
||
max_parent_weight = max_parent_weight.max(parent_node.cumulative_weight);
|
||
} else if *parent == self.genesis_hash {
|
||
max_parent_weight = 0;
|
||
} else {
|
||
return Err(DagError::ParentNotFound(*parent));
|
||
}
|
||
}
|
||
|
||
Ok(max_parent_weight + node.weight)
|
||
}
|
||
|
||
/// 拓扑排序(用于交易排序)
|
||
pub fn topological_sort(&self) -> Vec<Hash> {
|
||
let mut sorted = Vec::new();
|
||
let mut visited = HashSet::new();
|
||
let mut in_degree: HashMap<Hash, usize> = HashMap::new();
|
||
|
||
// 计算入度
|
||
for (hash, node) in &self.nodes {
|
||
in_degree.entry(*hash).or_insert(0);
|
||
for parent in &node.parents {
|
||
if self.nodes.contains_key(parent) {
|
||
*in_degree.entry(*parent).or_insert(0) += 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 找到所有入度为0的节点
|
||
let mut queue: VecDeque<Hash> = in_degree.iter()
|
||
.filter(|(_, °ree)| degree == 0)
|
||
.map(|(hash, _)| *hash)
|
||
.collect();
|
||
|
||
// BFS遍历
|
||
while let Some(hash) = queue.pop_front() {
|
||
if visited.contains(&hash) {
|
||
continue;
|
||
}
|
||
visited.insert(hash);
|
||
sorted.push(hash);
|
||
|
||
if let Some(node) = self.nodes.get(&hash) {
|
||
for parent in &node.parents {
|
||
if let Some(degree) = in_degree.get_mut(parent) {
|
||
*degree -= 1;
|
||
if *degree == 0 {
|
||
queue.push_back(*parent);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
sorted
|
||
}
|
||
|
||
/// 验证DAG一致性
|
||
pub fn validate(&self) -> Result<(), DagError> {
|
||
for (hash, node) in &self.nodes {
|
||
// 验证哈希
|
||
if *hash != node.hash {
|
||
return Err(DagError::HashMismatch(*hash, node.hash));
|
||
}
|
||
|
||
// 验证父节点存在
|
||
for parent in &node.parents {
|
||
if !self.nodes.contains_key(parent) && *parent != self.genesis_hash {
|
||
return Err(DagError::ParentNotFound(*parent));
|
||
}
|
||
}
|
||
|
||
// 验证时间戳单调性
|
||
for parent in &node.parents {
|
||
if let Some(parent_node) = self.nodes.get(parent) {
|
||
if node.timestamp < parent_node.timestamp {
|
||
return Err(DagError::TimestampNotMonotonic(*hash));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 检查最终性(基于累积权重)
|
||
/// 如果一个节点的累积权重超过阈值,则认为已达到最终性
|
||
pub fn check_finality(&self, hash: &Hash, threshold: u64) -> bool {
|
||
if let Some(node) = self.nodes.get(hash) {
|
||
node.cumulative_weight >= threshold
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
|
||
/// 获取确认的交易(已达到最终性的交易)
|
||
pub fn get_confirmed_transactions(&self, threshold: u64) -> Vec<Hash> {
|
||
self.nodes.iter()
|
||
.filter(|(_, node)| node.cumulative_weight >= threshold)
|
||
.map(|(hash, _)| *hash)
|
||
.collect()
|
||
}
|
||
|
||
/// 并发验证多个节点
|
||
pub fn concurrent_validate(&self, nodes: &[DagNode]) -> Vec<Result<(), DagError>> {
|
||
nodes.iter()
|
||
.map(|node| self.validate_node(node))
|
||
.collect()
|
||
}
|
||
|
||
/// 验证单个节点
|
||
fn validate_node(&self, node: &DagNode) -> Result<(), DagError> {
|
||
// 验证父节点存在
|
||
for parent in &node.parents {
|
||
if !self.nodes.contains_key(parent) && *parent != self.genesis_hash {
|
||
return Err(DagError::ParentNotFound(*parent));
|
||
}
|
||
}
|
||
|
||
// 验证时间戳
|
||
let current_time = SystemTime::now()
|
||
.duration_since(UNIX_EPOCH)
|
||
.expect("mainnet: handle error")
|
||
.as_secs();
|
||
|
||
if node.timestamp > current_time + 60 {
|
||
return Err(DagError::FutureTimestamp(node.hash));
|
||
}
|
||
|
||
// 验证父节点时间戳单调性
|
||
for parent in &node.parents {
|
||
if let Some(parent_node) = self.nodes.get(parent) {
|
||
if node.timestamp < parent_node.timestamp {
|
||
return Err(DagError::TimestampNotMonotonic(node.hash));
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 获取节点数量
|
||
pub fn node_count(&self) -> usize {
|
||
self.nodes.len()
|
||
}
|
||
|
||
/// 获取tips数量
|
||
pub fn tips_count(&self) -> usize {
|
||
self.tips.len()
|
||
}
|
||
|
||
/// 获取节点的所有祖先
|
||
pub fn get_ancestors(&self, hash: &Hash) -> HashSet<Hash> {
|
||
let mut ancestors = HashSet::new();
|
||
let mut queue = VecDeque::new();
|
||
queue.push_back(*hash);
|
||
|
||
while let Some(current) = queue.pop_front() {
|
||
if ancestors.contains(¤t) {
|
||
continue;
|
||
}
|
||
ancestors.insert(current);
|
||
|
||
if let Some(node) = self.nodes.get(¤t) {
|
||
for parent in &node.parents {
|
||
queue.push_back(*parent);
|
||
}
|
||
}
|
||
}
|
||
|
||
ancestors
|
||
}
|
||
|
||
/// 检查两个节点是否有冲突(双花检测)
|
||
pub fn has_conflict(&self, hash1: &Hash, hash2: &Hash) -> bool {
|
||
let ancestors1 = self.get_ancestors(hash1);
|
||
let ancestors2 = self.get_ancestors(hash2);
|
||
|
||
// 如果两个节点的祖先集合不相交,则可能存在冲突
|
||
ancestors1.is_disjoint(&ancestors2)
|
||
}
|
||
}
|
||
|
||
impl Default for DagGraph {
|
||
fn default() -> Self {
|
||
Self::new(Hash::sha3_384(b"genesis"))
|
||
}
|
||
}
|
||
|
||
/// DAG错误类型
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||
pub enum DagError {
|
||
ParentNotFound(Hash),
|
||
HashMismatch(Hash, Hash),
|
||
TimestampNotMonotonic(Hash),
|
||
FutureTimestamp(Hash),
|
||
}
|
||
|
||
impl std::fmt::Display for DagError {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
match self {
|
||
DagError::ParentNotFound(hash) => write!(f, "Parent not found: {:?}", hash),
|
||
DagError::HashMismatch(expected, actual) => {
|
||
write!(f, "Hash mismatch: expected {:?}, got {:?}", expected, actual)
|
||
}
|
||
DagError::TimestampNotMonotonic(hash) => {
|
||
write!(f, "Timestamp not monotonic for node: {:?}", hash)
|
||
}
|
||
DagError::FutureTimestamp(hash) => {
|
||
write!(f, "Future timestamp for node: {:?}", hash)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
impl std::error::Error for DagError {}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn create_test_node(data: &[u8], parents: Vec<Hash>, weight: u64) -> DagNode {
|
||
DagNode {
|
||
hash: Hash::sha3_384(data),
|
||
parents,
|
||
data: data.to_vec(),
|
||
timestamp: SystemTime::now()
|
||
.duration_since(UNIX_EPOCH)
|
||
.expect("mainnet: handle error")
|
||
.as_secs(),
|
||
weight,
|
||
cumulative_weight: 0,
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_dag_creation() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let dag = DagGraph::new(genesis_hash);
|
||
assert_eq!(dag.node_count(), 0);
|
||
assert_eq!(dag.tips_count(), 1);
|
||
}
|
||
|
||
#[test]
|
||
fn test_add_node() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 1);
|
||
assert!(dag.add_node(node1.clone()).is_ok());
|
||
assert_eq!(dag.node_count(), 1);
|
||
assert!(dag.get_node(&node1.hash).is_some());
|
||
}
|
||
|
||
#[test]
|
||
fn test_select_parents() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 1);
|
||
dag.add_node(node1.clone()).expect("mainnet: handle error");
|
||
|
||
let parents = dag.select_parents();
|
||
assert!(!parents.is_empty());
|
||
assert!(parents.contains(&node1.hash));
|
||
}
|
||
|
||
#[test]
|
||
fn test_topological_sort() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 1);
|
||
let node1_hash = node1.hash;
|
||
dag.add_node(node1).expect("mainnet: handle error");
|
||
|
||
let node2 = create_test_node(b"test2", vec![node1_hash], 1);
|
||
dag.add_node(node2).expect("mainnet: handle error");
|
||
|
||
let sorted = dag.topological_sort();
|
||
assert_eq!(sorted.len(), 2);
|
||
}
|
||
|
||
#[test]
|
||
fn test_validate() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 1);
|
||
dag.add_node(node1).expect("mainnet: handle error");
|
||
|
||
assert!(dag.validate().is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_finality() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 100);
|
||
let node1_hash = node1.hash;
|
||
dag.add_node(node1).expect("mainnet: handle error");
|
||
|
||
assert!(dag.check_finality(&node1_hash, 50));
|
||
assert!(!dag.check_finality(&node1_hash, 200));
|
||
}
|
||
|
||
#[test]
|
||
fn test_concurrent_validate() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let dag = DagGraph::new(genesis_hash);
|
||
|
||
let nodes = vec![
|
||
create_test_node(b"test1", vec![genesis_hash], 1),
|
||
create_test_node(b"test2", vec![genesis_hash], 1),
|
||
];
|
||
|
||
let results = dag.concurrent_validate(&nodes);
|
||
assert_eq!(results.len(), 2);
|
||
assert!(results.iter().all(|r| r.is_ok()));
|
||
}
|
||
|
||
#[test]
|
||
fn test_get_ancestors() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 1);
|
||
let node1_hash = node1.hash;
|
||
dag.add_node(node1).expect("mainnet: handle error");
|
||
|
||
let node2 = create_test_node(b"test2", vec![node1_hash], 1);
|
||
let node2_hash = node2.hash;
|
||
dag.add_node(node2).expect("mainnet: handle error");
|
||
|
||
let ancestors = dag.get_ancestors(&node2_hash);
|
||
assert!(ancestors.contains(&node2_hash));
|
||
assert!(ancestors.contains(&node1_hash));
|
||
}
|
||
|
||
#[test]
|
||
fn test_has_conflict() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 1);
|
||
let node1_hash = node1.hash;
|
||
dag.add_node(node1).expect("mainnet: handle error");
|
||
|
||
let node2 = create_test_node(b"test2", vec![genesis_hash], 1);
|
||
let node2_hash = node2.hash;
|
||
dag.add_node(node2).expect("mainnet: handle error");
|
||
|
||
// 两个节点都引用genesis,应该没有冲突
|
||
assert!(!dag.has_conflict(&node1_hash, &node2_hash));
|
||
}
|
||
|
||
#[test]
|
||
fn test_cumulative_weight() {
|
||
let genesis_hash = Hash::sha3_384(b"genesis");
|
||
let mut dag = DagGraph::new(genesis_hash);
|
||
|
||
let node1 = create_test_node(b"test1", vec![genesis_hash], 10);
|
||
let node1_hash = node1.hash;
|
||
dag.add_node(node1).expect("mainnet: handle error");
|
||
|
||
let node2 = create_test_node(b"test2", vec![node1_hash], 5);
|
||
let node2_hash = node2.hash;
|
||
dag.add_node(node2).expect("mainnet: handle error");
|
||
|
||
let node2_data = dag.get_node(&node2_hash).expect("mainnet: handle error");
|
||
assert_eq!(node2_data.cumulative_weight, 15); // 10 + 5
|
||
}
|
||
}
|