424 lines
12 KiB
Go
424 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
|
|
// 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
|
|
}
|
|
}
|