package state import ( "errors" "fmt" "math/big" "git.marketally.com/tutus-one/tutus-chain/pkg/util" "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // HealthcareAccountStatus represents the status of a healthcare account. type HealthcareAccountStatus uint8 const ( // HealthcareAccountActive indicates an active account. HealthcareAccountActive HealthcareAccountStatus = 0 // HealthcareAccountSuspended indicates a temporarily suspended account. HealthcareAccountSuspended HealthcareAccountStatus = 1 // HealthcareAccountClosed indicates a permanently closed account. HealthcareAccountClosed HealthcareAccountStatus = 2 ) // MedicalRecordType represents the type of medical record. type MedicalRecordType uint8 const ( // RecordTypeCheckup indicates a routine checkup. RecordTypeCheckup MedicalRecordType = 0 // RecordTypeTreatment indicates a treatment. RecordTypeTreatment MedicalRecordType = 1 // RecordTypeEmergency indicates an emergency visit. RecordTypeEmergency MedicalRecordType = 2 // RecordTypePrescription indicates a prescription. RecordTypePrescription MedicalRecordType = 3 // RecordTypeLabResult indicates lab results. RecordTypeLabResult MedicalRecordType = 4 // RecordTypeVaccination indicates a vaccination. RecordTypeVaccination MedicalRecordType = 5 // RecordTypeMentalHealth indicates mental health services. RecordTypeMentalHealth MedicalRecordType = 6 // RecordTypePreventive indicates preventive care. RecordTypePreventive MedicalRecordType = 7 ) // AccessLevel represents the level of access granted to a provider. type AccessLevel uint8 const ( // AccessLevelNone indicates no access. AccessLevelNone AccessLevel = 0 // AccessLevelEmergency indicates emergency-only access. AccessLevelEmergency AccessLevel = 1 // AccessLevelLimited indicates limited access (specific record types). AccessLevelLimited AccessLevel = 2 // AccessLevelFull indicates full access to all records. AccessLevelFull AccessLevel = 3 ) // ProviderStatus represents the status of a healthcare provider. type ProviderStatus uint8 const ( // ProviderStatusActive indicates an active provider. ProviderStatusActive ProviderStatus = 0 // ProviderStatusSuspended indicates a suspended provider. ProviderStatusSuspended ProviderStatus = 1 // ProviderStatusRevoked indicates a revoked provider. ProviderStatusRevoked ProviderStatus = 2 ) // HealthcareAccount represents a citizen's healthcare account. type HealthcareAccount struct { VitaID uint64 // Owner's Vita token ID Owner util.Uint160 // Owner's address AnnualAllocation uint64 // Annual healthcare credits CreditsUsed uint64 // Credits used this year CreditsAvailable uint64 // Available healthcare credits BiologicalAge uint32 // Biological age (Salus-adjusted) LastCheckup uint32 // Block height of last checkup Status HealthcareAccountStatus // Account status CreatedAt uint32 // Block height when created UpdatedAt uint32 // Block height of last update } // ToStackItem implements stackitem.Convertible interface. func (a *HealthcareAccount) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(a.VitaID))), stackitem.NewByteArray(a.Owner.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(a.AnnualAllocation))), stackitem.NewBigInteger(big.NewInt(int64(a.CreditsUsed))), stackitem.NewBigInteger(big.NewInt(int64(a.CreditsAvailable))), stackitem.NewBigInteger(big.NewInt(int64(a.BiologicalAge))), stackitem.NewBigInteger(big.NewInt(int64(a.LastCheckup))), stackitem.NewBigInteger(big.NewInt(int64(a.Status))), stackitem.NewBigInteger(big.NewInt(int64(a.CreatedAt))), stackitem.NewBigInteger(big.NewInt(int64(a.UpdatedAt))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (a *HealthcareAccount) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(items) != 10 { return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items)) } vitaID, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid vitaID: %w", err) } a.VitaID = vitaID.Uint64() owner, err := items[1].TryBytes() if err != nil { return fmt.Errorf("invalid owner: %w", err) } a.Owner, err = util.Uint160DecodeBytesBE(owner) if err != nil { return fmt.Errorf("invalid owner address: %w", err) } annualAllocation, err := items[2].TryInteger() if err != nil { return fmt.Errorf("invalid annualAllocation: %w", err) } a.AnnualAllocation = annualAllocation.Uint64() creditsUsed, err := items[3].TryInteger() if err != nil { return fmt.Errorf("invalid creditsUsed: %w", err) } a.CreditsUsed = creditsUsed.Uint64() creditsAvailable, err := items[4].TryInteger() if err != nil { return fmt.Errorf("invalid creditsAvailable: %w", err) } a.CreditsAvailable = creditsAvailable.Uint64() biologicalAge, err := items[5].TryInteger() if err != nil { return fmt.Errorf("invalid biologicalAge: %w", err) } a.BiologicalAge = uint32(biologicalAge.Uint64()) lastCheckup, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid lastCheckup: %w", err) } a.LastCheckup = uint32(lastCheckup.Uint64()) status, err := items[7].TryInteger() if err != nil { return fmt.Errorf("invalid status: %w", err) } a.Status = HealthcareAccountStatus(status.Uint64()) createdAt, err := items[8].TryInteger() if err != nil { return fmt.Errorf("invalid createdAt: %w", err) } a.CreatedAt = uint32(createdAt.Uint64()) updatedAt, err := items[9].TryInteger() if err != nil { return fmt.Errorf("invalid updatedAt: %w", err) } a.UpdatedAt = uint32(updatedAt.Uint64()) return nil } // MedicalRecord represents a medical record reference (data stored off-chain). type MedicalRecord struct { ID uint64 // Unique record ID VitaID uint64 // Patient's Vita ID Patient util.Uint160 // Patient's address Provider util.Uint160 // Healthcare provider's address RecordType MedicalRecordType // Type of medical record ContentHash util.Uint256 // Hash of encrypted off-chain data CreditsUsed uint64 // Healthcare credits used CreatedAt uint32 // Block height when created IsActive bool // Whether record is valid } // ToStackItem implements stackitem.Convertible interface. func (r *MedicalRecord) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(r.ID))), stackitem.NewBigInteger(big.NewInt(int64(r.VitaID))), stackitem.NewByteArray(r.Patient.BytesBE()), stackitem.NewByteArray(r.Provider.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(r.RecordType))), stackitem.NewByteArray(r.ContentHash.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(r.CreditsUsed))), stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), stackitem.NewBool(r.IsActive), }), nil } // FromStackItem implements stackitem.Convertible interface. func (r *MedicalRecord) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(items) != 9 { return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) } id, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid id: %w", err) } r.ID = id.Uint64() vitaID, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid vitaID: %w", err) } r.VitaID = vitaID.Uint64() patient, err := items[2].TryBytes() if err != nil { return fmt.Errorf("invalid patient: %w", err) } r.Patient, err = util.Uint160DecodeBytesBE(patient) if err != nil { return fmt.Errorf("invalid patient address: %w", err) } provider, err := items[3].TryBytes() if err != nil { return fmt.Errorf("invalid provider: %w", err) } r.Provider, err = util.Uint160DecodeBytesBE(provider) if err != nil { return fmt.Errorf("invalid provider address: %w", err) } recordType, err := items[4].TryInteger() if err != nil { return fmt.Errorf("invalid recordType: %w", err) } r.RecordType = MedicalRecordType(recordType.Uint64()) contentHash, err := items[5].TryBytes() if err != nil { return fmt.Errorf("invalid contentHash: %w", err) } r.ContentHash, err = util.Uint256DecodeBytesBE(contentHash) if err != nil { return fmt.Errorf("invalid contentHash value: %w", err) } creditsUsed, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid creditsUsed: %w", err) } r.CreditsUsed = creditsUsed.Uint64() createdAt, err := items[7].TryInteger() if err != nil { return fmt.Errorf("invalid createdAt: %w", err) } r.CreatedAt = uint32(createdAt.Uint64()) isActive, err := items[8].TryBool() if err != nil { return fmt.Errorf("invalid isActive: %w", err) } r.IsActive = isActive return nil } // ProviderAuthorization represents a healthcare provider's access authorization. type ProviderAuthorization struct { ID uint64 // Authorization ID VitaID uint64 // Patient's Vita ID Patient util.Uint160 // Patient's address Provider util.Uint160 // Healthcare provider's address AccessLevel AccessLevel // Level of access granted StartsAt uint32 // Block height when access starts ExpiresAt uint32 // Block height when access expires (0 = no expiry) IsActive bool // Whether authorization is currently active GrantedAt uint32 // Block height when granted } // ToStackItem implements stackitem.Convertible interface. func (p *ProviderAuthorization) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(p.ID))), stackitem.NewBigInteger(big.NewInt(int64(p.VitaID))), stackitem.NewByteArray(p.Patient.BytesBE()), stackitem.NewByteArray(p.Provider.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(p.AccessLevel))), stackitem.NewBigInteger(big.NewInt(int64(p.StartsAt))), stackitem.NewBigInteger(big.NewInt(int64(p.ExpiresAt))), stackitem.NewBool(p.IsActive), stackitem.NewBigInteger(big.NewInt(int64(p.GrantedAt))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (p *ProviderAuthorization) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(items) != 9 { return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) } id, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid id: %w", err) } p.ID = id.Uint64() vitaID, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid vitaID: %w", err) } p.VitaID = vitaID.Uint64() patient, err := items[2].TryBytes() if err != nil { return fmt.Errorf("invalid patient: %w", err) } p.Patient, err = util.Uint160DecodeBytesBE(patient) if err != nil { return fmt.Errorf("invalid patient address: %w", err) } provider, err := items[3].TryBytes() if err != nil { return fmt.Errorf("invalid provider: %w", err) } p.Provider, err = util.Uint160DecodeBytesBE(provider) if err != nil { return fmt.Errorf("invalid provider address: %w", err) } accessLevel, err := items[4].TryInteger() if err != nil { return fmt.Errorf("invalid accessLevel: %w", err) } p.AccessLevel = AccessLevel(accessLevel.Uint64()) startsAt, err := items[5].TryInteger() if err != nil { return fmt.Errorf("invalid startsAt: %w", err) } p.StartsAt = uint32(startsAt.Uint64()) expiresAt, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid expiresAt: %w", err) } p.ExpiresAt = uint32(expiresAt.Uint64()) isActive, err := items[7].TryBool() if err != nil { return fmt.Errorf("invalid isActive: %w", err) } p.IsActive = isActive grantedAt, err := items[8].TryInteger() if err != nil { return fmt.Errorf("invalid grantedAt: %w", err) } p.GrantedAt = uint32(grantedAt.Uint64()) return nil } // IsExpired checks if the authorization has expired. func (p *ProviderAuthorization) IsExpired(currentBlock uint32) bool { return p.ExpiresAt != 0 && p.ExpiresAt <= currentBlock } // IsValid checks if the authorization is currently valid. func (p *ProviderAuthorization) IsValid(currentBlock uint32) bool { return p.IsActive && currentBlock >= p.StartsAt && !p.IsExpired(currentBlock) } // HealthcareProvider represents a registered healthcare provider. type HealthcareProvider struct { Address util.Uint160 // Provider's address Name string // Provider name ProviderID uint64 // Unique provider ID Specialty string // Medical specialty LicenseHash util.Uint256 // Hash of license documentation Status ProviderStatus // Provider status RegisteredAt uint32 // Block height when registered UpdatedAt uint32 // Block height of last update } // ToStackItem implements stackitem.Convertible interface. func (p *HealthcareProvider) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray(p.Address.BytesBE()), stackitem.NewByteArray([]byte(p.Name)), stackitem.NewBigInteger(big.NewInt(int64(p.ProviderID))), stackitem.NewByteArray([]byte(p.Specialty)), stackitem.NewByteArray(p.LicenseHash.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(p.Status))), stackitem.NewBigInteger(big.NewInt(int64(p.RegisteredAt))), stackitem.NewBigInteger(big.NewInt(int64(p.UpdatedAt))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (p *HealthcareProvider) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(items) != 8 { return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) } address, err := items[0].TryBytes() if err != nil { return fmt.Errorf("invalid address: %w", err) } p.Address, err = util.Uint160DecodeBytesBE(address) if err != nil { return fmt.Errorf("invalid provider address: %w", err) } name, err := items[1].TryBytes() if err != nil { return fmt.Errorf("invalid name: %w", err) } p.Name = string(name) providerID, err := items[2].TryInteger() if err != nil { return fmt.Errorf("invalid providerID: %w", err) } p.ProviderID = providerID.Uint64() specialty, err := items[3].TryBytes() if err != nil { return fmt.Errorf("invalid specialty: %w", err) } p.Specialty = string(specialty) licenseHash, err := items[4].TryBytes() if err != nil { return fmt.Errorf("invalid licenseHash: %w", err) } p.LicenseHash, err = util.Uint256DecodeBytesBE(licenseHash) if err != nil { return fmt.Errorf("invalid licenseHash value: %w", err) } status, err := items[5].TryInteger() if err != nil { return fmt.Errorf("invalid status: %w", err) } p.Status = ProviderStatus(status.Uint64()) registeredAt, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid registeredAt: %w", err) } p.RegisteredAt = uint32(registeredAt.Uint64()) updatedAt, err := items[7].TryInteger() if err != nil { return fmt.Errorf("invalid updatedAt: %w", err) } p.UpdatedAt = uint32(updatedAt.Uint64()) return nil } // EmergencyAccess represents an emergency access grant. type EmergencyAccess struct { ID uint64 // Emergency access ID VitaID uint64 // Patient's Vita ID Patient util.Uint160 // Patient's address Provider util.Uint160 // Provider who accessed Reason string // Emergency reason GrantedAt uint32 // Block height when granted ExpiresAt uint32 // Block height when expires WasReviewed bool // Whether access was reviewed } // ToStackItem implements stackitem.Convertible interface. func (e *EmergencyAccess) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(e.ID))), stackitem.NewBigInteger(big.NewInt(int64(e.VitaID))), stackitem.NewByteArray(e.Patient.BytesBE()), stackitem.NewByteArray(e.Provider.BytesBE()), stackitem.NewByteArray([]byte(e.Reason)), stackitem.NewBigInteger(big.NewInt(int64(e.GrantedAt))), stackitem.NewBigInteger(big.NewInt(int64(e.ExpiresAt))), stackitem.NewBool(e.WasReviewed), }), nil } // FromStackItem implements stackitem.Convertible interface. func (e *EmergencyAccess) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(items) != 8 { return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) } id, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid id: %w", err) } e.ID = id.Uint64() vitaID, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid vitaID: %w", err) } e.VitaID = vitaID.Uint64() patient, err := items[2].TryBytes() if err != nil { return fmt.Errorf("invalid patient: %w", err) } e.Patient, err = util.Uint160DecodeBytesBE(patient) if err != nil { return fmt.Errorf("invalid patient address: %w", err) } provider, err := items[3].TryBytes() if err != nil { return fmt.Errorf("invalid provider: %w", err) } e.Provider, err = util.Uint160DecodeBytesBE(provider) if err != nil { return fmt.Errorf("invalid provider address: %w", err) } reason, err := items[4].TryBytes() if err != nil { return fmt.Errorf("invalid reason: %w", err) } e.Reason = string(reason) grantedAt, err := items[5].TryInteger() if err != nil { return fmt.Errorf("invalid grantedAt: %w", err) } e.GrantedAt = uint32(grantedAt.Uint64()) expiresAt, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid expiresAt: %w", err) } e.ExpiresAt = uint32(expiresAt.Uint64()) wasReviewed, err := items[7].TryBool() if err != nil { return fmt.Errorf("invalid wasReviewed: %w", err) } e.WasReviewed = wasReviewed return nil } // SalusConfig represents configurable parameters for the Salus contract. type SalusConfig struct { DefaultAnnualCredits uint64 // Default annual healthcare credits EmergencyAccessDuration uint32 // Blocks for emergency access (default ~24 hours) PreventiveCareBonus uint64 // Bonus credits for preventive care MaxAuthorizationDuration uint32 // Maximum authorization duration in blocks } // ToStackItem implements stackitem.Convertible interface. func (c *SalusConfig) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(c.DefaultAnnualCredits))), stackitem.NewBigInteger(big.NewInt(int64(c.EmergencyAccessDuration))), stackitem.NewBigInteger(big.NewInt(int64(c.PreventiveCareBonus))), stackitem.NewBigInteger(big.NewInt(int64(c.MaxAuthorizationDuration))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (c *SalusConfig) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(items) != 4 { return fmt.Errorf("wrong number of elements: expected 4, got %d", len(items)) } defaultCredits, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid defaultAnnualCredits: %w", err) } c.DefaultAnnualCredits = defaultCredits.Uint64() emergencyDuration, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid emergencyAccessDuration: %w", err) } c.EmergencyAccessDuration = uint32(emergencyDuration.Uint64()) preventiveBonus, err := items[2].TryInteger() if err != nil { return fmt.Errorf("invalid preventiveCareBonus: %w", err) } c.PreventiveCareBonus = preventiveBonus.Uint64() maxAuthDuration, err := items[3].TryInteger() if err != nil { return fmt.Errorf("invalid maxAuthorizationDuration: %w", err) } c.MaxAuthorizationDuration = uint32(maxAuthDuration.Uint64()) return nil }