使用 Trae 编写 可以携带资产、可以交易的DOBlock - 未编译 未测试 未部署

场景

  1. 游戏账号(EVM系这个场景说烂了)
  2. 多签地址的签名地址。卖出DOB,即是卖出治理权。人变了,但是不需要更改多签地址的成员。
  3. 社区身份绑定在DOB上。卖出DOB,即是卖出社区身份(资历、荣誉等等)。

设计

  1. 基于DOB的CKB地址,其公钥可以由cluster id + spore id构成。
  2. 由DOB的所有者进行签名授权。

// src\dob_lock_args.mol
table DOBLockArgs {
    // The code hash of the Spore type script.
    spore_type_code_hash: Byte32,
    // The hash type of the Spore type script (0: data, 1: type, 2: data1, 4: data2).
    spore_type_hash_type: Byte,
    // The ID of the Cluster that the required Spore must belong to.
    cluster_id: Byte32,
    // The ID of the Spore that is required to unlock this cell.
    spore_id: Byte32,
}
//
#![no_std]
#![no_main]
#![feature(lang_items)]
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]

// Import CKB syscalls and high-level APIs
use ckb_std::{
    ckb_constants::Source,
    ckb_types::core::ScriptHashType,
    ckb_types::{bytes::Bytes, prelude::*},
    high_level::*,
};

// Import the generated molecule code
mod dob_lock_args;
use dob_lock_args::DOBLockArgs;

// Import Spore/Cluster related constants or functions if needed
// For example, if Spore ID is stored in a specific part of the data
// or if Cluster ID needs specific parsing from Spore data.
// const SPORE_TYPE_SCRIPT_ARGS_CLUSTER_ID_OFFSET: usize = 32; // Example offset for Cluster ID in Spore type args
// const SPORE_TYPE_SCRIPT_ARGS_CLUSTER_ID_LEN: usize = 32;    // Example length for Cluster ID

// Define error codes
#[repr(i8)]
enum Error {
    IndexOutOfBound = 1,
    ItemMissing,
    LengthNotEnough,
    Encoding,
    // Add more error codes as needed
    ArgsInvalid, // 5
    SporeCellNotFound, // 6
    InvalidHashType, // 7
    ClusterIdMismatch, // 8
    SporeIdMismatch, // 9
}

impl From<molecule::error::VerificationError> for Error {
    fn from(_: molecule::error::VerificationError) -> Self {
        Error::Encoding
    }
}


fn verify_spore_in_inputs(
    spore_type_code_hash: &[u8; 32],
    spore_type_hash_type: ScriptHashType,
    cluster_id: &[u8; 32],
    spore_id: &[u8; 32],
) -> Result<(), Error> {
    // Search for the specific Spore cell in the transaction inputs.
    let spore_found = QueryIter::new(load_cell_type, Source::Input)
        .enumerate()
        .any(|(i, script_opt)| {
            if let Some(script) = script_opt {
                // 1. Check if the type script matches the Spore's type script.
                if script.code_hash().raw_data().as_ref() == spore_type_code_hash
                    && script.hash_type().into() == spore_type_hash_type
                {
                    // 2. Verify Cluster ID from the Spore's type script arguments.
                    // Assumes the first 32 bytes of args is the Cluster ID.
                    let args: Bytes = script.args().unpack();
                    if args.len() < 32 || &args[..32] != cluster_id {
                        return false; // Cluster ID mismatch
                    }

                    // 3. Verify Spore ID from the Spore's cell data.
                    // Assumes the first 32 bytes of data is the Spore ID.
                    if let Ok(data) = load_cell_data(i, Source::Input) {
                        if data.len() < 32 || &data[..32] != spore_id {
                            return false; // Spore ID mismatch
                        }
                    } else {
                        return false; // Failed to load data
                    }

                    // If all checks pass, we've found the required Spore cell.
                    return true;
                }
            }
            false
        });

    if spore_found {
        Ok(())
    } else {
        Err(Error::SporeCellNotFound)
    }
}


pub fn main() -> Result<(), Error> {
    // Load the arguments of the current DOBLock script.
    let script = load_script()?;
    let args: Bytes = script.args().unpack();
    let dob_lock_args = DOBLockArgs::from_slice(&args).map_err(|_| Error::ArgsInvalid)?;

    // Extract the required Spore identification details from the arguments.
    let spore_type_code_hash: [u8; 32] = dob_lock_args.spore_type_code_hash().into();
    let spore_type_hash_type_byte: u8 = dob_lock_args.spore_type_hash_type().into();
    let cluster_id: [u8; 32] = dob_lock_args.cluster_id().into();
    let spore_id: [u8; 32] = dob_lock_args.spore_id().into();

    let spore_type_hash_type = match spore_type_hash_type_byte {
        0 => ScriptHashType::Data,
        1 => ScriptHashType::Type,
        2 => ScriptHashType::Data1,
        4 => ScriptHashType::Data2, // CKB2023
        _ => return Err(Error::InvalidHashType),
    };

    // The core logic: verify that the specific Spore cell, which acts as a 'key',
    // is present in the transaction's inputs. This is sufficient to unlock the cell
    // protected by this DOBLock script.
    verify_spore_in_inputs(
        &spore_type_code_hash,
        spore_type_hash_type,
        &cluster_id,
        &spore_id,
    )?;

    Ok(())
}