package state import ( "errors" "fmt" "math/big" "github.com/tutus-one/tutus-chain/pkg/util" "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // EducationAccountStatus represents the status of an education account. type EducationAccountStatus uint8 const ( // EducationAccountActive indicates an active account. EducationAccountActive EducationAccountStatus = 0 // EducationAccountSuspended indicates a temporarily suspended account. EducationAccountSuspended EducationAccountStatus = 1 // EducationAccountClosed indicates a permanently closed account. EducationAccountClosed EducationAccountStatus = 2 ) // CertificationStatus represents the validity status of a certification. type CertificationStatus uint8 const ( // CertificationActive indicates a valid active certification. CertificationActive CertificationStatus = 0 // CertificationExpired indicates an expired certification. CertificationExpired CertificationStatus = 1 // CertificationRevoked indicates a revoked certification. CertificationRevoked CertificationStatus = 2 ) // EnrollmentStatus represents the status of a program enrollment. type EnrollmentStatus uint8 const ( // EnrollmentActive indicates an active enrollment. EnrollmentActive EnrollmentStatus = 0 // EnrollmentCompleted indicates successful completion. EnrollmentCompleted EnrollmentStatus = 1 // EnrollmentWithdrawn indicates voluntary withdrawal. EnrollmentWithdrawn EnrollmentStatus = 2 // EnrollmentTransferred indicates transfer to another institution. EnrollmentTransferred EnrollmentStatus = 3 ) // EducationAccount represents a citizen's lifelong learning account. type EducationAccount struct { VitaID uint64 // Owner's Vita token ID Owner util.Uint160 // Owner's address TotalCredits uint64 // Lifetime credits received UsedCredits uint64 // Credits spent on education AvailableCredits uint64 // Current balance Status EducationAccountStatus // Account status CreatedAt uint32 // Block height when created UpdatedAt uint32 // Block height of last modification } // ToStackItem implements stackitem.Convertible interface. func (a *EducationAccount) 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.TotalCredits))), stackitem.NewBigInteger(big.NewInt(int64(a.UsedCredits))), stackitem.NewBigInteger(big.NewInt(int64(a.AvailableCredits))), 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 *EducationAccount) 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)) } 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) } totalCredits, err := items[2].TryInteger() if err != nil { return fmt.Errorf("invalid totalCredits: %w", err) } a.TotalCredits = totalCredits.Uint64() usedCredits, err := items[3].TryInteger() if err != nil { return fmt.Errorf("invalid usedCredits: %w", err) } a.UsedCredits = usedCredits.Uint64() availableCredits, err := items[4].TryInteger() if err != nil { return fmt.Errorf("invalid availableCredits: %w", err) } a.AvailableCredits = availableCredits.Uint64() status, err := items[5].TryInteger() if err != nil { return fmt.Errorf("invalid status: %w", err) } a.Status = EducationAccountStatus(status.Uint64()) createdAt, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid createdAt: %w", err) } a.CreatedAt = uint32(createdAt.Uint64()) updatedAt, err := items[7].TryInteger() if err != nil { return fmt.Errorf("invalid updatedAt: %w", err) } a.UpdatedAt = uint32(updatedAt.Uint64()) return nil } // Certification represents a verified skill or credential. type Certification struct { ID uint64 // Unique certification ID VitaID uint64 // Owner's Vita ID Owner util.Uint160 // Owner's address CertType string // Type of certification Name string // Certification name Institution util.Uint160 // Issuing institution ContentHash util.Uint256 // Off-chain content hash IssuedAt uint32 // Block height when issued ExpiresAt uint32 // 0 = never expires Status CertificationStatus // Certification status } // ToStackItem implements stackitem.Convertible interface. func (c *Certification) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(c.ID))), stackitem.NewBigInteger(big.NewInt(int64(c.VitaID))), stackitem.NewByteArray(c.Owner.BytesBE()), stackitem.NewByteArray([]byte(c.CertType)), stackitem.NewByteArray([]byte(c.Name)), stackitem.NewByteArray(c.Institution.BytesBE()), stackitem.NewByteArray(c.ContentHash.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(c.IssuedAt))), stackitem.NewBigInteger(big.NewInt(int64(c.ExpiresAt))), stackitem.NewBigInteger(big.NewInt(int64(c.Status))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (c *Certification) 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)) } id, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid id: %w", err) } c.ID = id.Uint64() vitaID, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid vitaID: %w", err) } c.VitaID = vitaID.Uint64() owner, err := items[2].TryBytes() if err != nil { return fmt.Errorf("invalid owner: %w", err) } c.Owner, err = util.Uint160DecodeBytesBE(owner) if err != nil { return fmt.Errorf("invalid owner address: %w", err) } certType, err := items[3].TryBytes() if err != nil { return fmt.Errorf("invalid certType: %w", err) } c.CertType = string(certType) name, err := items[4].TryBytes() if err != nil { return fmt.Errorf("invalid name: %w", err) } c.Name = string(name) institution, err := items[5].TryBytes() if err != nil { return fmt.Errorf("invalid institution: %w", err) } c.Institution, err = util.Uint160DecodeBytesBE(institution) if err != nil { return fmt.Errorf("invalid institution address: %w", err) } contentHash, err := items[6].TryBytes() if err != nil { return fmt.Errorf("invalid contentHash: %w", err) } c.ContentHash, err = util.Uint256DecodeBytesBE(contentHash) if err != nil { return fmt.Errorf("invalid contentHash value: %w", err) } issuedAt, err := items[7].TryInteger() if err != nil { return fmt.Errorf("invalid issuedAt: %w", err) } c.IssuedAt = uint32(issuedAt.Uint64()) expiresAt, err := items[8].TryInteger() if err != nil { return fmt.Errorf("invalid expiresAt: %w", err) } c.ExpiresAt = uint32(expiresAt.Uint64()) status, err := items[9].TryInteger() if err != nil { return fmt.Errorf("invalid status: %w", err) } c.Status = CertificationStatus(status.Uint64()) return nil } // IsExpired checks if the certification has expired. func (c *Certification) IsExpired(currentBlock uint32) bool { return c.ExpiresAt != 0 && c.ExpiresAt <= currentBlock } // IsValid checks if the certification is currently valid. func (c *Certification) IsValid(currentBlock uint32) bool { return c.Status == CertificationActive && !c.IsExpired(currentBlock) } // Enrollment represents a program enrollment record. type Enrollment struct { ID uint64 // Unique enrollment ID VitaID uint64 // Student's Vita ID Student util.Uint160 // Student's address ProgramID string // Program identifier Institution util.Uint160 // Educational institution CreditsAllocated uint64 // Credits committed to program StartedAt uint32 // Block height when started CompletedAt uint32 // Block height when completed (0 = ongoing) Status EnrollmentStatus // Enrollment status } // ToStackItem implements stackitem.Convertible interface. func (e *Enrollment) 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.Student.BytesBE()), stackitem.NewByteArray([]byte(e.ProgramID)), stackitem.NewByteArray(e.Institution.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(e.CreditsAllocated))), stackitem.NewBigInteger(big.NewInt(int64(e.StartedAt))), stackitem.NewBigInteger(big.NewInt(int64(e.CompletedAt))), stackitem.NewBigInteger(big.NewInt(int64(e.Status))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (e *Enrollment) 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) } e.ID = id.Uint64() vitaID, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid vitaID: %w", err) } e.VitaID = vitaID.Uint64() student, err := items[2].TryBytes() if err != nil { return fmt.Errorf("invalid student: %w", err) } e.Student, err = util.Uint160DecodeBytesBE(student) if err != nil { return fmt.Errorf("invalid student address: %w", err) } programID, err := items[3].TryBytes() if err != nil { return fmt.Errorf("invalid programID: %w", err) } e.ProgramID = string(programID) institution, err := items[4].TryBytes() if err != nil { return fmt.Errorf("invalid institution: %w", err) } e.Institution, err = util.Uint160DecodeBytesBE(institution) if err != nil { return fmt.Errorf("invalid institution address: %w", err) } creditsAllocated, err := items[5].TryInteger() if err != nil { return fmt.Errorf("invalid creditsAllocated: %w", err) } e.CreditsAllocated = creditsAllocated.Uint64() startedAt, err := items[6].TryInteger() if err != nil { return fmt.Errorf("invalid startedAt: %w", err) } e.StartedAt = uint32(startedAt.Uint64()) completedAt, err := items[7].TryInteger() if err != nil { return fmt.Errorf("invalid completedAt: %w", err) } e.CompletedAt = uint32(completedAt.Uint64()) status, err := items[8].TryInteger() if err != nil { return fmt.Errorf("invalid status: %w", err) } e.Status = EnrollmentStatus(status.Uint64()) return nil } // ScireConfig represents configurable parameters for the Scire contract. type ScireConfig struct { AnnualCreditAllocation uint64 // Default credits per year MaxCreditsPerProgram uint64 // Maximum credits for single program CertificationFee uint64 // VTS fee for issuing certifications (0 = free) MinEnrollmentDuration uint32 // Minimum blocks for enrollment } // ToStackItem implements stackitem.Convertible interface. func (c *ScireConfig) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(c.AnnualCreditAllocation))), stackitem.NewBigInteger(big.NewInt(int64(c.MaxCreditsPerProgram))), stackitem.NewBigInteger(big.NewInt(int64(c.CertificationFee))), stackitem.NewBigInteger(big.NewInt(int64(c.MinEnrollmentDuration))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (c *ScireConfig) 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)) } annualCredits, err := items[0].TryInteger() if err != nil { return fmt.Errorf("invalid annualCreditAllocation: %w", err) } c.AnnualCreditAllocation = annualCredits.Uint64() maxCredits, err := items[1].TryInteger() if err != nil { return fmt.Errorf("invalid maxCreditsPerProgram: %w", err) } c.MaxCreditsPerProgram = maxCredits.Uint64() certFee, err := items[2].TryInteger() if err != nil { return fmt.Errorf("invalid certificationFee: %w", err) } c.CertificationFee = certFee.Uint64() minDuration, err := items[3].TryInteger() if err != nil { return fmt.Errorf("invalid minEnrollmentDuration: %w", err) } c.MinEnrollmentDuration = uint32(minDuration.Uint64()) return nil }