package model import ( "context" "errors" "fmt" "time" "go.yandata.net/iod/iod/go-trustlog/api/logger" "go.yandata.net/iod/iod/go-trustlog/internal/helpers" ) // Record 表示一条记录。 // 用于记录系统中的操作行为,包含记录标识、节点前缀、操作者信息等。 type Record struct { ID string `json:"id" validate:"required,max=128"` DoPrefix string `json:"doPrefix" validate:"max=512"` ProducerID string `json:"producerId" validate:"required,max=512"` Timestamp time.Time `json:"timestamp"` Operator string `json:"operator" validate:"max=64"` Extra []byte `json:"extra" validate:"max=512"` RCType string `json:"type" validate:"max=64"` binary []byte } // // ===== 构造函数 ===== // // NewFullRecord 创建包含所有字段的完整 Record。 // 自动完成字段校验,确保创建的 Record 是完整且有效的。 func NewFullRecord( doPrefix string, producerID string, timestamp time.Time, operator string, extra []byte, rcType string, ) (*Record, error) { log := logger.GetGlobalLogger() log.Debug("Creating new full record", "doPrefix", doPrefix, "producerID", producerID, "operator", operator, "rcType", rcType, "extraLength", len(extra), ) record := &Record{ DoPrefix: doPrefix, ProducerID: producerID, Timestamp: timestamp, Operator: operator, Extra: extra, RCType: rcType, } log.Debug("Checking and initializing record") if err := record.CheckAndInit(); err != nil { log.Error("Failed to check and init record", "error", err, ) return nil, err } log.Debug("Full record created successfully", "recordID", record.ID, ) return record, nil } // // ===== 接口实现 ===== // func (r *Record) Key() string { return r.ID } // RecordHashData 实现 HashData 接口,用于存储 Record 的哈希计算结果。 type RecordHashData struct { key string hash string } func (r RecordHashData) Key() string { return r.key } func (r RecordHashData) Hash() string { return r.hash } func (r RecordHashData) Type() HashType { return Sha256Simd } // DoHash 计算 Record 的整体哈希值,用于数据完整性验证。 // 哈希基于序列化后的二进制数据计算,确保记录数据的不可篡改性。 func (r *Record) DoHash(_ context.Context) (HashData, error) { log := logger.GetGlobalLogger() log.Debug("Computing hash for record", "recordID", r.ID, ) hashTool := GetHashTool(Sha256Simd) binary, err := r.MarshalBinary() if err != nil { log.Error("Failed to marshal record for hash", "error", err, "recordID", r.ID, ) return nil, fmt.Errorf("failed to marshal record: %w", err) } log.Debug("Computing hash bytes", "recordID", r.ID, "binaryLength", len(binary), ) hash, err := hashTool.HashBytes(binary) if err != nil { log.Error("Failed to compute hash", "error", err, "recordID", r.ID, ) return nil, fmt.Errorf("failed to compute hash: %w", err) } log.Debug("Hash computed successfully", "recordID", r.ID, "hash", hash, ) return RecordHashData{ key: r.ID, hash: hash, }, nil } // // ===== CBOR 序列化相关 ===== // // recordData 用于 CBOR 序列化/反序列化的中间结构。 // 排除缓存字段,仅包含可序列化的数据字段。 type recordData struct { ID *string `cbor:"id"` DoPrefix *string `cbor:"doPrefix"` ProducerID *string `cbor:"producerId"` Timestamp *time.Time `cbor:"timestamp"` Operator *string `cbor:"operator"` Extra []byte `cbor:"extra"` RCType *string `cbor:"type"` } // toRecordData 将 Record 转换为 recordData,用于序列化。 func (r *Record) toRecordData() *recordData { return &recordData{ ID: &r.ID, DoPrefix: &r.DoPrefix, ProducerID: &r.ProducerID, Timestamp: &r.Timestamp, Operator: &r.Operator, Extra: r.Extra, RCType: &r.RCType, } } // fromRecordData 从 recordData 填充 Record,用于反序列化。 func (r *Record) fromRecordData(recData *recordData) { if recData == nil { return } if recData.ID != nil { r.ID = *recData.ID } if recData.DoPrefix != nil { r.DoPrefix = *recData.DoPrefix } if recData.ProducerID != nil { r.ProducerID = *recData.ProducerID } if recData.Timestamp != nil { r.Timestamp = *recData.Timestamp } if recData.Operator != nil { r.Operator = *recData.Operator } if recData.Extra != nil { r.Extra = recData.Extra } if recData.RCType != nil { r.RCType = *recData.RCType } } // MarshalBinary 将 Record 序列化为 CBOR 格式的二进制数据。 // 实现 encoding.BinaryMarshaler 接口。 // 使用 Canonical CBOR 编码确保序列化结果的一致性,使用缓存机制避免重复序列化。 func (r *Record) MarshalBinary() ([]byte, error) { log := logger.GetGlobalLogger() log.Debug("Marshaling record to CBOR binary", "recordID", r.ID, ) if r.binary != nil { log.Debug("Using cached binary data", "recordID", r.ID, ) return r.binary, nil } recData := r.toRecordData() log.Debug("Marshaling record data to canonical CBOR", "recordID", r.ID, ) binary, err := helpers.MarshalCanonical(recData) if err != nil { log.Error("Failed to marshal record to CBOR", "error", err, "recordID", r.ID, ) return nil, fmt.Errorf("failed to marshal record to CBOR: %w", err) } r.binary = binary log.Debug("Record marshaled successfully", "recordID", r.ID, "binaryLength", len(binary), ) return binary, nil } // UnmarshalBinary 从 CBOR 格式的二进制数据反序列化为 Record。 // 实现 encoding.BinaryUnmarshaler 接口。 func (r *Record) UnmarshalBinary(data []byte) error { log := logger.GetGlobalLogger() log.Debug("Unmarshaling record from CBOR binary", "dataLength", len(data), ) if len(data) == 0 { log.Error("Data is empty") return errors.New("data is empty") } recData := &recordData{} log.Debug("Unmarshaling record data from CBOR") if err := helpers.Unmarshal(data, recData); err != nil { log.Error("Failed to unmarshal record from CBOR", "error", err, ) return fmt.Errorf("failed to unmarshal record from CBOR: %w", err) } r.fromRecordData(recData) r.binary = data log.Debug("Record unmarshaled successfully", "recordID", r.ID, ) return nil } // GetDoPrefix 实现 DoPrefixExtractor 接口,返回节点前缀。 func (r *Record) GetDoPrefix() string { return r.DoPrefix } // GetProducerID 返回 ProducerID,实现 Trustlog 接口。 func (r *Record) GetProducerID() string { return r.ProducerID } // // ===== 初始化与验证 ===== // // CheckAndInit 校验并初始化 Record。 // 自动填充缺失字段(ID),字段非空验证由 validate 标签处理。 func (r *Record) CheckAndInit() error { log := logger.GetGlobalLogger() log.Debug("Checking and initializing record", "producerID", r.ProducerID, "doPrefix", r.DoPrefix, ) if r.ID == "" { r.ID = helpers.NewUUIDv7() log.Debug("Generated new record ID", "recordID", r.ID, ) } if r.Timestamp.IsZero() { r.Timestamp = time.Now() log.Debug("Set default timestamp", "timestamp", r.Timestamp, ) } log.Debug("Validating record struct") if err := helpers.GetValidator().Struct(r); err != nil { log.Error("Record validation failed", "error", err, "recordID", r.ID, ) return err } log.Debug("Record checked and initialized successfully", "recordID", r.ID, ) return nil } // // ===== 链式调用支持 ===== // // WithDoPrefix 设置 DoPrefix 并返回自身,支持链式调用。 func (r *Record) WithDoPrefix(doPrefix string) *Record { r.DoPrefix = doPrefix return r } // WithTimestamp 设置 Timestamp 并返回自身,支持链式调用。 func (r *Record) WithTimestamp(timestamp time.Time) *Record { r.Timestamp = timestamp return r } // WithOperator 设置 Operator 并返回自身,支持链式调用。 func (r *Record) WithOperator(operator string) *Record { r.Operator = operator return r } // WithExtra 设置 Extra 并返回自身,支持链式调用。 func (r *Record) WithExtra(extra []byte) *Record { r.Extra = extra return r } // WithRCType 设置 RCType 并返回自身,支持链式调用。 func (r *Record) WithRCType(rcType string) *Record { r.RCType = rcType return r }