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

417 lines
13 KiB
Go

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
}