package state import ( "errors" "math/big" "github.com/tutus-one/tutus-chain/pkg/io" "github.com/tutus-one/tutus-chain/pkg/util" "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var errAncoraInvalidStackItem = errors.New("invalid stack item") // DataType represents the type of off-chain data being anchored. type DataType uint8 const ( // DataTypeMedical is for Salus healthcare records (HIPAA protected). DataTypeMedical DataType = 0 // DataTypeEducation is for Scire education records (certifications, transcripts). DataTypeEducation DataType = 1 // DataTypeInvestment is for Collocatio investment records (portfolios, transactions). DataTypeInvestment DataType = 2 // DataTypeDocuments is for personal documents (IPFS CIDs). DataTypeDocuments DataType = 3 // DataTypePresence is for VPP presence attestations (real-time humanity verification). DataTypePresence DataType = 4 // DataTypeCustom is for government-defined extensions. DataTypeCustom DataType = 5 ) // TreeAlgorithm represents the Merkle tree hash algorithm. type TreeAlgorithm uint8 const ( // TreeAlgorithmSHA256 is the default SHA256 algorithm. TreeAlgorithmSHA256 TreeAlgorithm = 0 // TreeAlgorithmKeccak256 is Keccak256 for EVM compatibility. TreeAlgorithmKeccak256 TreeAlgorithm = 1 // TreeAlgorithmPoseidon is Poseidon for ZK-proof friendliness. TreeAlgorithmPoseidon TreeAlgorithm = 2 ) // ErasureStatus represents the status of a GDPR erasure request. type ErasureStatus uint8 const ( // ErasurePending means the request is awaiting off-chain deletion. ErasurePending ErasureStatus = 0 // ErasureConfirmed means off-chain deletion has been confirmed. ErasureConfirmed ErasureStatus = 1 // ErasureDenied means the erasure was denied (legal hold, etc.). ErasureDenied ErasureStatus = 2 ) // RootInfo contains metadata about a Merkle root anchored on-chain. type RootInfo struct { // Root is the 32-byte Merkle root. Root []byte // LeafCount is the number of leaves in the tree. LeafCount uint64 // UpdatedAt is the block height of the last update. UpdatedAt uint32 // UpdatedBy is the provider script hash that updated this root. UpdatedBy util.Uint160 // Version is the incrementing version number. Version uint64 // TreeAlgorithm is the hash algorithm used (0=SHA256, 1=Keccak256, 2=Poseidon). TreeAlgorithm TreeAlgorithm // SchemaVersion is the data schema version (e.g., "1.0.0"). SchemaVersion string // ContentHash is an optional hash of the serialized tree. ContentHash []byte } // EncodeBinary implements io.Serializable. func (r *RootInfo) EncodeBinary(w *io.BinWriter) { w.WriteVarBytes(r.Root) w.WriteU64LE(r.LeafCount) w.WriteU32LE(r.UpdatedAt) r.UpdatedBy.EncodeBinary(w) w.WriteU64LE(r.Version) w.WriteB(byte(r.TreeAlgorithm)) w.WriteString(r.SchemaVersion) w.WriteVarBytes(r.ContentHash) } // DecodeBinary implements io.Serializable. func (r *RootInfo) DecodeBinary(br *io.BinReader) { r.Root = br.ReadVarBytes() r.LeafCount = br.ReadU64LE() r.UpdatedAt = br.ReadU32LE() r.UpdatedBy.DecodeBinary(br) r.Version = br.ReadU64LE() r.TreeAlgorithm = TreeAlgorithm(br.ReadB()) r.SchemaVersion = br.ReadString() r.ContentHash = br.ReadVarBytes() } // ToStackItem converts RootInfo to a stack item for VM. func (r *RootInfo) ToStackItem() (stackitem.Item, error) { return stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(r.Root), stackitem.NewBigInteger(big.NewInt(int64(r.LeafCount))), stackitem.NewBigInteger(big.NewInt(int64(r.UpdatedAt))), stackitem.NewByteArray(r.UpdatedBy.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(r.Version))), stackitem.NewBigInteger(big.NewInt(int64(r.TreeAlgorithm))), stackitem.NewByteArray([]byte(r.SchemaVersion)), stackitem.NewByteArray(r.ContentHash), }), nil } // FromStackItem populates RootInfo from a stack item. func (r *RootInfo) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok || len(arr) < 8 { return errAncoraInvalidStackItem } root, err := arr[0].TryBytes() if err != nil { return err } r.Root = root leafCount, err := arr[1].TryInteger() if err != nil { return err } r.LeafCount = leafCount.Uint64() updatedAt, err := arr[2].TryInteger() if err != nil { return err } r.UpdatedAt = uint32(updatedAt.Uint64()) updatedBy, err := arr[3].TryBytes() if err != nil { return err } r.UpdatedBy, err = util.Uint160DecodeBytesBE(updatedBy) if err != nil { return err } version, err := arr[4].TryInteger() if err != nil { return err } r.Version = version.Uint64() algo, err := arr[5].TryInteger() if err != nil { return err } r.TreeAlgorithm = TreeAlgorithm(algo.Uint64()) schema, err := arr[6].TryBytes() if err != nil { return err } r.SchemaVersion = string(schema) contentHash, err := arr[7].TryBytes() if err != nil { return err } r.ContentHash = contentHash return nil } // ErasureInfo contains metadata about a GDPR erasure request. type ErasureInfo struct { // RequestedAt is the block height when erasure was requested. RequestedAt uint32 // RequestedBy is the script hash of the requester. RequestedBy util.Uint160 // Reason is the GDPR reason code. Reason string // Status is the current erasure status. Status ErasureStatus // ProcessedAt is when off-chain deletion was confirmed. ProcessedAt uint32 // ConfirmedBy is the provider that confirmed deletion. ConfirmedBy util.Uint160 // DeniedReason is the reason for denial (if denied). DeniedReason string } // EncodeBinary implements io.Serializable. func (e *ErasureInfo) EncodeBinary(w *io.BinWriter) { w.WriteU32LE(e.RequestedAt) e.RequestedBy.EncodeBinary(w) w.WriteString(e.Reason) w.WriteB(byte(e.Status)) w.WriteU32LE(e.ProcessedAt) e.ConfirmedBy.EncodeBinary(w) w.WriteString(e.DeniedReason) } // DecodeBinary implements io.Serializable. func (e *ErasureInfo) DecodeBinary(br *io.BinReader) { e.RequestedAt = br.ReadU32LE() e.RequestedBy.DecodeBinary(br) e.Reason = br.ReadString() e.Status = ErasureStatus(br.ReadB()) e.ProcessedAt = br.ReadU32LE() e.ConfirmedBy.DecodeBinary(br) e.DeniedReason = br.ReadString() } // ToStackItem converts ErasureInfo to a stack item for VM. func (e *ErasureInfo) ToStackItem() (stackitem.Item, error) { return stackitem.NewArray([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(e.RequestedAt))), stackitem.NewByteArray(e.RequestedBy.BytesBE()), stackitem.NewByteArray([]byte(e.Reason)), stackitem.NewBigInteger(big.NewInt(int64(e.Status))), stackitem.NewBigInteger(big.NewInt(int64(e.ProcessedAt))), stackitem.NewByteArray(e.ConfirmedBy.BytesBE()), stackitem.NewByteArray([]byte(e.DeniedReason)), }), nil } // FromStackItem populates ErasureInfo from a stack item. func (e *ErasureInfo) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok || len(arr) < 7 { return errAncoraInvalidStackItem } requestedAt, err := arr[0].TryInteger() if err != nil { return err } e.RequestedAt = uint32(requestedAt.Uint64()) requestedBy, err := arr[1].TryBytes() if err != nil { return err } e.RequestedBy, err = util.Uint160DecodeBytesBE(requestedBy) if err != nil { return err } reason, err := arr[2].TryBytes() if err != nil { return err } e.Reason = string(reason) status, err := arr[3].TryInteger() if err != nil { return err } e.Status = ErasureStatus(status.Uint64()) processedAt, err := arr[4].TryInteger() if err != nil { return err } e.ProcessedAt = uint32(processedAt.Uint64()) confirmedBy, err := arr[5].TryBytes() if err != nil { return err } e.ConfirmedBy, err = util.Uint160DecodeBytesBE(confirmedBy) if err != nil { return err } deniedReason, err := arr[6].TryBytes() if err != nil { return err } e.DeniedReason = string(deniedReason) return nil } // ProviderConfig contains configuration for an authorized data provider. type ProviderConfig struct { // DataType is the data type this provider is authorized for. DataType DataType // Provider is the script hash of the authorized provider. Provider util.Uint160 // Description is a human-readable description. Description string // RegisteredAt is the block height when registered. RegisteredAt uint32 // Active indicates if the provider is currently active. Active bool // MaxUpdatesPerBlock is the anti-spam rate limit. MaxUpdatesPerBlock uint32 // UpdateCooldown is the blocks between updates per VitaID. UpdateCooldown uint32 } // EncodeBinary implements io.Serializable. func (p *ProviderConfig) EncodeBinary(w *io.BinWriter) { w.WriteB(byte(p.DataType)) p.Provider.EncodeBinary(w) w.WriteString(p.Description) w.WriteU32LE(p.RegisteredAt) w.WriteBool(p.Active) w.WriteU32LE(p.MaxUpdatesPerBlock) w.WriteU32LE(p.UpdateCooldown) } // DecodeBinary implements io.Serializable. func (p *ProviderConfig) DecodeBinary(br *io.BinReader) { p.DataType = DataType(br.ReadB()) p.Provider.DecodeBinary(br) p.Description = br.ReadString() p.RegisteredAt = br.ReadU32LE() p.Active = br.ReadBool() p.MaxUpdatesPerBlock = br.ReadU32LE() p.UpdateCooldown = br.ReadU32LE() } // ToStackItem converts ProviderConfig to a stack item for VM. func (p *ProviderConfig) ToStackItem() (stackitem.Item, error) { return stackitem.NewArray([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(p.DataType))), stackitem.NewByteArray(p.Provider.BytesBE()), stackitem.NewByteArray([]byte(p.Description)), stackitem.NewBigInteger(big.NewInt(int64(p.RegisteredAt))), stackitem.NewBool(p.Active), stackitem.NewBigInteger(big.NewInt(int64(p.MaxUpdatesPerBlock))), stackitem.NewBigInteger(big.NewInt(int64(p.UpdateCooldown))), }), nil } // FromStackItem populates ProviderConfig from a stack item. func (p *ProviderConfig) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok || len(arr) < 7 { return errAncoraInvalidStackItem } dataType, err := arr[0].TryInteger() if err != nil { return err } p.DataType = DataType(dataType.Uint64()) provider, err := arr[1].TryBytes() if err != nil { return err } p.Provider, err = util.Uint160DecodeBytesBE(provider) if err != nil { return err } description, err := arr[2].TryBytes() if err != nil { return err } p.Description = string(description) registeredAt, err := arr[3].TryInteger() if err != nil { return err } p.RegisteredAt = uint32(registeredAt.Uint64()) active, err := arr[4].TryBool() if err != nil { return err } p.Active = active maxUpdates, err := arr[5].TryInteger() if err != nil { return err } p.MaxUpdatesPerBlock = uint32(maxUpdates.Uint64()) cooldown, err := arr[6].TryInteger() if err != nil { return err } p.UpdateCooldown = uint32(cooldown.Uint64()) return nil } // StateAnchorsConfig contains configuration for the StateAnchors contract. type StateAnchorsConfig struct { // DefaultTreeAlgorithm is the default hash algorithm (0=SHA256). DefaultTreeAlgorithm TreeAlgorithm // MaxProofDepth is the maximum depth of Merkle proofs (default: 32). MaxProofDepth uint32 // DefaultMaxUpdatesPerBlock is the default rate limit. DefaultMaxUpdatesPerBlock uint32 // DefaultUpdateCooldown is the default cooldown in blocks. DefaultUpdateCooldown uint32 // MaxHistoryVersions is the max history to retain per vitaID+dataType. MaxHistoryVersions uint32 // ErasureGracePeriod is blocks before erasure can be denied. ErasureGracePeriod uint32 // AttestationValidBlocks is how long attestations are valid. AttestationValidBlocks uint32 } // DefaultStateAnchorsConfig returns the default configuration. func DefaultStateAnchorsConfig() StateAnchorsConfig { return StateAnchorsConfig{ DefaultTreeAlgorithm: TreeAlgorithmSHA256, MaxProofDepth: 32, DefaultMaxUpdatesPerBlock: 10, DefaultUpdateCooldown: 1, MaxHistoryVersions: 100, ErasureGracePeriod: 1000, // ~16 minutes at 1s blocks AttestationValidBlocks: 86400, // ~24 hours } }