Rust Holochain数据完整性类型库holochain_integrity_types的使用,支持分布式应用数据模型验证与类型安全

holochain_integrity_types

Project Forum Chat License: Apache-2.0

这个crate提供了Holochain应用开发者在完整性zome代码中需要的类型,除此之外没有其他内容。

这个crate被有意保持得尽可能最小化,因为它通常作为依赖项包含在Holochain Zomes中,而Zomes是以Wasm块的形式分布的。

贡献

Holochain是一个开源项目。我们欢迎各种形式的参与,并正在积极扩大接受参与的范围。请参阅我们的贡献指南,了解我们关于参与社区的一般实践和协议,以及关于代码格式化、测试实践、持续集成等方面的具体期望。

许可证

License: Apache-2.0

版权所有 © 2019 - 2024, Holochain Foundation

这个程序是免费软件:你可以重新分发和/或修改它,遵循LICENSE文件(Apache 2.0)中提供的许可证条款。这个程序分发时希望它有用,但没有任何保证;甚至没有适销性或特定用途适用性的隐含保证。

安装

在你的项目目录中运行以下Cargo命令:

cargo add holochain_integrity_types

或者在你的Cargo.toml中添加以下行:

holochain_integrity_types = "0.5.4"

完整示例代码

下面是一个使用holochain_integrity_types的完整示例,展示了如何定义数据模型并进行验证:

use holochain_integrity_types::*;
use hdi::prelude::*;

// 定义一个简单的博客文章Entry类型
#[entry_def]
#[derive(Clone)]
pub struct BlogPost {
    pub title: String,
    pub content: String,
    pub author: AgentPubKey,
    pub timestamp: Timestamp,
}

// 实现EntryDefRegistration trait来注册Entry类型
impl EntryDefRegistration for BlogPost {
    fn entry_def() -> EntryDef {
        EntryDef {
            id: "blog_post".into(),
            visibility: EntryVisibility::Public,
            required_validations: 3.into(),
            required_validation_package: RequiredValidationType::Entry,
        }
    }
}

// 定义链接类型
#[link_type]
pub enum BlogLinkType {
    AuthorToPosts,
}

// 定义验证回调
#[hdk_extern]
pub fn validate(op: Op) -> ExternResult<ValidateCallbackResult> {
    match op {
        Op::StoreEntry { entry, .. } => {
            match entry {
                Entry::App(app_entry) => {
                    if app_entry.entry_def_id == BlogPost::entry_def().id {
                        // 验证博客文章数据
                        let post: BlogPost = app_entry.try_into()?;
                        if post.title.is_empty() {
                            return Ok(ValidateCallbackResult::Invalid(
                                "Title cannot be empty".to_string(),
                            ));
                        }
                        if post.content.len() < 10 {
                            return Ok(ValidateCallbackResult::Invalid(
                                "Content too short".to_string(),
                            ));
                        }
                    }
                }
                _ => (),
            }
        }
        Op::RegisterCreateLink { .. } => {
            // 验证链接创建
        }
        Op::RegisterDeleteLink { .. } => {
            // 验证链接删除
        }
        _ => (),
    }
    Ok(ValidateCallbackResult::Valid)
}

#[hdk_extern]
pub fn init(_: ()) -> ExternResult<InitCallbackResult> {
    Ok(InitCallbackResult::Pass)
}

这个示例展示了:

  1. 定义一个BlogPost Entry类型
  2. 实现EntryDefRegistration trait来注册Entry类型
  3. 定义链接类型
  4. 实现验证回调来确保数据完整性
  5. 基本的初始化函数

使用holochain_integrity_types可以帮助你在分布式应用中确保数据模型的类型安全和完整性验证。

扩展示例代码

以下是一个更完整的示例,展示了如何使用holochain_integrity_types实现一个简单的社交应用:

use holochain_integrity_types::*;
use hdi::prelude::*;

// 定义用户个人资料Entry类型
#[entry_def]
#[derive(Clone)]
pub struct UserProfile {
    pub username: String,
    pub bio: String,
    pub avatar_hash: Option<EntryHash>,
}

// 实现EntryDefRegistration
impl EntryDefRegistration for UserProfile {
    fn entry_def() -> EntryDef {
        EntryDef {
            id: "user_profile".into(),
            visibility: EntryVisibility::Public,
            required_validations: 3.into(),
            required_validation_package: RequiredValidationType::Entry,
        }
    }
}

// 定义帖子Entry类型
#[entry_def]
#[derive(Clone)]
pub struct SocialPost {
    pub content: String,
    pub creator: AgentPubKey,
    pub created_at: Timestamp,
    pub tags: Vec<String>,
}

// 实现EntryDefRegistration
impl EntryDefRegistration for SocialPost {
    fn entry_def() -> EntryDef {
        EntryDef {
            id: "social_post".into(),
            visibility: EntryVisibility::Public,
            required_validations: 3.into(),
            required_validation_package: RequiredValidationType::Entry,
        }
    }
}

// 定义链接类型
#[link_type]
pub enum SocialLinkType {
    CreatorToPosts,
    TagToPosts,
    AgentToProfile,
}

// 实现验证回调
#[hdk_extern]
pub fn validate(op: Op) -> ExternResult<ValidateCallbackResult> {
    match op {
        Op::StoreEntry { entry, .. } => {
            match entry {
                Entry::App(app_entry) => {
                    if app_entry.entry_def_id == UserProfile::entry_def().id {
                        let profile: UserProfile = app_entry.try_into()?;
                        if profile.username.is_empty() {
                            return Ok(ValidateCallbackResult::Invalid(
                                "Username cannot be empty".to_string(),
                            ));
                        }
                        if profile.bio.len() > 500 {
                            return Ok(ValidateCallbackResult::Invalid(
                                "Bio too long".to_string(),
                            ));
                        }
                    } else if app_entry.entry_def_id == SocialPost::entry_def().id {
                        let post: SocialPost = app_entry.try_into()?;
                        if post.content.is_empty() {
                            return Ok(ValidateCallbackResult::Invalid(
                                "Post content cannot be empty".to_string(),
                            ));
                        }
                        if post.content.len() > 1000 {
                            return Ok(ValidateCallbackResult::Invalid(
                                "Post too long".to_string(),
                            ));
                        }
                    }
                }
                _ => (),
            }
        }
        Op::RegisterCreateLink {
            base_address,
            target_address,
            link_type,
            tag,
            ..
        } => {
            // 验证链接创建逻辑
        }
        Op::RegisterDeleteLink { .. } => {
            // 验证链接删除逻辑
        }
        _ => (),
    }
    Ok(ValidateCallbackResult::Valid)
}

// 初始化函数
#[hdk_extern]
pub fn init(_: ()) -> ExternResult<InitCallbackResult> {
    Ok(InitCallbackResult::Pass)
}

// 创建用户个人资料的函数
#[hdk_extern]
pub fn create_profile(username: String, bio: String) -> ExternResult<EntryHash> {
    let profile = UserProfile {
        username,
        bio,
        avatar_hash: None,
    };
    create_entry(profile)
}

// 创建帖子的函数
#[hdk_extern]
pub fn create_post(content: String, tags: Vec<String>) -> ExternResult<EntryHash> {
    let post = SocialPost {
        content,
        creator: agent_info()?.agent_latest_pubkey,
        created_at: sys_time()?,
        tags,
    };
    create_entry(post)
}

这个扩展示例展示了:

  1. 定义用户个人资料和社交帖子两种Entry类型
  2. 实现对应的EntryDefRegistration
  3. 定义更丰富的链接类型
  4. 更详细的验证逻辑
  5. 提供了创建个人资料和帖子的实用函数
  6. 展示了如何获取代理信息和系统时间等常用操作

1 回复

基于提供的内容,我将整理出完整的holochain_integrity_types使用示例。首先展示内容中已有的示例,然后给出一个完整的博客应用demo。

内容中提供的示例

定义记录类型

use holochain_integrity_types::EntryDef;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlogPost {
    pub title: String,
    pub content: String,
    pub author: String,
    pub timestamp: u64,
}

impl EntryDef for BlogPost {
    fn entry_def() -> EntryDef {
        EntryDef {
            id: "blog_post".into(),
            visibility: EntryVisibility::Public,
            required_validations: 5.into(),
            required_validation_type: RequiredValidationType::Full,
        }
    }
}

定义链接类型

use holochain_integrity_types::LinkType;

pub enum BlogLinkTypes {
    AuthorToPosts,
    PostToComments,
}

impl LinkType for BlogLinkTypes {
    fn try_from_scope_string(scope: &str) -> Result<Self, WasmError> {
        match scope {
            "author_to_posts" => Ok(Self::AuthorToPosts),
            "post_to_comments" => Ok(Self::PostToComments),
            _ => Err(WasmError::Guest(format!("Unknown link type: {}", scope))),
        }
    }
    
    fn as_scope_string(&self) -> String {
        match self {
            Self::AuthorToPosts => "author_to_posts".to_string(),
            Self::PostToComments => "post_to_comments".to_string(),
        }
    }
}

完整博客应用示例

//! 完整的Holochain博客应用示例
use holochain_integrity_types::*;
use serde::{Serialize, Deserialize};

// ========== 数据结构定义 ==========

/// 博客文章结构体
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlogPost {
    pub title: String,
    pub content: String,
    pub author: String,
    pub timestamp: u64,
    pub tags: Vec<String>,
}

/// 博客评论结构体
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlogComment {
    pub content: String,
    pub author: String,
    pub timestamp: u64,
    pub post_hash: String,
}

// ========== EntryDef实现 ==========

impl EntryDef for BlogPost {
    fn entry_def() -> EntryDef {
        EntryDef {
            id: "blog_post".into(),
            visibility: EntryVisibility::Public,
            required_validations: 3.into(),
            required_validation_type: RequiredValidationType::Full,
        }
    }
}

impl EntryDef for BlogComment {
    fn entry_def() -> EntryDef {
        EntryDef {
            id: "blog_comment".into(),
            visibility: EntryVisibility::Public,
            required_validations: 3.into(),
            required_validation_type: RequiredValidationType::Subset,
        }
    }
}

// ========== 数据验证实现 ==========

impl ValidateData<BlogPost> for BlogPost {
    fn validate(&self) -> Result<(), String> {
        if self.title.is_empty() {
            return Err("标题不能为空".to_string());
        }
        if self.content.len() < 20 {
            return Err("内容至少需要20个字符".to_string());
        }
        if self.author.is_empty() {
            return Err("作者不能为空".to_string());
        }
        if self.timestamp == 0 {
            return Err("无效的时间戳".to_string());
        }
        Ok(())
    }
}

impl ValidateData<BlogComment> for BlogComment {
    fn validate(&self) -> Result<(), String> {
        if self.content.is_empty() {
            return Err("评论内容不能为空".to_string());
        }
        if self.author.is_empty() {
            return Err("评论作者不能为空".to_string());
        }
        if self.post_hash.is_empty() {
            return Err("必须关联到一篇文章".to_string());
        }
        Ok(())
    }
}

// ========== 链接类型定义 ==========

pub enum BlogLinkTypes {
    AuthorToPosts,    // 作者到文章的链接
    PostToComments,   // 文章到评论的链接
    PostToTags,       // 文章到标签的链接
    TagToPosts,       // 标签到文章的链接
}

impl LinkType for BlogLinkTypes {
    fn try_from_scope_string(scope: &str) -> Result<Self, WasmError> {
        match scope {
            "author_to_posts" => Ok(Self::AuthorToPosts),
            "post_to_comments" => Ok(Self::PostToComments),
            "post_to_tags" => Ok(Self::PostToTags),
            "tag_to_posts" => Ok(Self::TagToPosts),
            _ => Err(WasmError::Guest(format!("未知的链接类型: {}", scope))),
        }
    }
    
    fn as_scope_string(&self) -> String {
        match self {
            Self::AuthorToPosts => "author_to_posts".to_string(),
            Self::PostToComments => "post_to_comments".to_string(),
            Self::PostToTags => "post_to_tags".to_string(),
            Self::TagToPosts => "tag_to_posts".to_string(),
        }
    }
}

// ========== Zome配置 ==========

pub fn blog_zome_info() -> ZomeInfo {
    ZomeInfo {
        name: ZomeName::new("blog_zome").unwrap(),
        id: 0,
        properties: Default::default(),
        entry_defs: vec![
            BlogPost::entry_def(),
            BlogComment::entry_def(),
        ],
        link_types: vec![],
    }
}

// ========== 测试用例 ==========

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_blog_post_validation() {
        let valid_post = BlogPost {
            title: "Rust Holochain指南".to_string(),
            content: "这是一篇关于Holochain的详细指南...".to_string(),
            author: "rustacean".to_string(),
            timestamp: 1234567890,
            tags: vec!["rust".to_string(), "holochain".to_string()],
        };
        
        assert!(valid_post.validate().is_ok());
        
        let invalid_post = BlogPost {
            title: "".to_string(),  // 无效: 空标题
            content: "短".to_string(),  // 无效: 内容太短
            author: "".to_string(),  // 无效: 空作者
            timestamp: 0,  // 无效: 零时间戳
            tags: vec![],
        };
        
        assert!(invalid_post.validate().is_err());
    }
    
    #[test]
    fn test_link_type_conversion() {
        let link = BlogLinkTypes::try_from_scope_string("post_to_comments").unwrap();
        assert_eq!(link.as_scope_string(), "post_to_comments");
    }
}

使用说明

  1. 这个完整示例展示了如何在Holochain中构建一个博客应用
  2. 包含了两种数据类型(BlogPost和BlogComment)的定义
  3. 实现了数据验证逻辑确保数据完整性
  4. 定义了多种链接类型来建立数据关系
  5. 包含了基本的测试用例

注意事项

  1. 确保在实际应用中使用最新的holochain_integrity_types版本
  2. 根据业务需求调整验证规则
  3. 在分布式环境中,所有节点必须使用相同的验证规则
  4. 考虑数据隐私和权限设置
回到顶部