tutus-chain/pkg/core/state/state_anchors.go

422 lines
12 KiB
Go

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
// DataTypeCustom is for government-defined extensions.
DataTypeCustom DataType = 4
)
// 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
}
}