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

328 lines
9.7 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"
)
// Scope represents permission scope level.
type Scope uint8
const (
// ScopeGlobal applies the permission globally.
ScopeGlobal Scope = 0
// ScopePersonal applies only to the owner's own resources.
ScopePersonal Scope = 1
// ScopeDelegated is delegated by another token holder.
ScopeDelegated Scope = 2
)
// Role represents a custom role definition in the RoleRegistry.
type Role struct {
ID uint64 // Unique sequential identifier
Name string // Human-readable name (max 64 chars)
Description string // Description (max 256 chars)
ParentID uint64 // Parent role ID (0 = no parent, enables hierarchy)
CreatedAt uint32 // Block height when created
CreatedBy util.Uint160 // Creator's script hash
Active bool // Whether role is active
}
// ToStackItem implements stackitem.Convertible interface.
func (r *Role) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(r.ID))),
stackitem.NewByteArray([]byte(r.Name)),
stackitem.NewByteArray([]byte(r.Description)),
stackitem.NewBigInteger(big.NewInt(int64(r.ParentID))),
stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))),
stackitem.NewByteArray(r.CreatedBy.BytesBE()),
stackitem.NewBool(r.Active),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (r *Role) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 7 {
return fmt.Errorf("wrong number of elements: expected 7, got %d", len(items))
}
id, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid id: %w", err)
}
r.ID = id.Uint64()
nameBytes, err := items[1].TryBytes()
if err != nil {
return fmt.Errorf("invalid name: %w", err)
}
r.Name = string(nameBytes)
descBytes, err := items[2].TryBytes()
if err != nil {
return fmt.Errorf("invalid description: %w", err)
}
r.Description = string(descBytes)
parentID, err := items[3].TryInteger()
if err != nil {
return fmt.Errorf("invalid parentID: %w", err)
}
r.ParentID = parentID.Uint64()
createdAt, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid createdAt: %w", err)
}
r.CreatedAt = uint32(createdAt.Int64())
createdByBytes, err := items[5].TryBytes()
if err != nil {
return fmt.Errorf("invalid createdBy: %w", err)
}
r.CreatedBy, err = util.Uint160DecodeBytesBE(createdByBytes)
if err != nil {
return fmt.Errorf("invalid createdBy hash: %w", err)
}
r.Active, err = items[6].TryBool()
if err != nil {
return fmt.Errorf("invalid active: %w", err)
}
return nil
}
// RoleAssignment represents a role granted to a Vita holder.
type RoleAssignment struct {
TokenID uint64 // Vita ID (or script hash as uint64 for address-based)
RoleID uint64 // Role ID
GrantedAt uint32 // Block height when granted
GrantedBy util.Uint160 // Granter's script hash
ExpiresAt uint32 // Expiration block (0 = never)
Active bool // Whether assignment is active
}
// ToStackItem implements stackitem.Convertible interface.
func (a *RoleAssignment) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(a.TokenID))),
stackitem.NewBigInteger(big.NewInt(int64(a.RoleID))),
stackitem.NewBigInteger(big.NewInt(int64(a.GrantedAt))),
stackitem.NewByteArray(a.GrantedBy.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))),
stackitem.NewBool(a.Active),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (a *RoleAssignment) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 6 {
return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items))
}
tokenID, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid tokenID: %w", err)
}
a.TokenID = tokenID.Uint64()
roleID, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid roleID: %w", err)
}
a.RoleID = roleID.Uint64()
grantedAt, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid grantedAt: %w", err)
}
a.GrantedAt = uint32(grantedAt.Int64())
grantedByBytes, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid grantedBy: %w", err)
}
a.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes)
if err != nil {
return fmt.Errorf("invalid grantedBy hash: %w", err)
}
expiresAt, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid expiresAt: %w", err)
}
a.ExpiresAt = uint32(expiresAt.Int64())
a.Active, err = items[5].TryBool()
if err != nil {
return fmt.Errorf("invalid active: %w", err)
}
return nil
}
// PermissionGrant represents a permission assigned to a role.
type PermissionGrant struct {
RoleID uint64 // Role ID
Resource string // Resource identifier (e.g., "documents")
Action string // Action identifier (e.g., "read", "write")
Scope Scope // Scope level
GrantedAt uint32 // Block height when granted
GrantedBy util.Uint160 // Granter's script hash
}
// ToStackItem implements stackitem.Convertible interface.
func (p *PermissionGrant) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(p.RoleID))),
stackitem.NewByteArray([]byte(p.Resource)),
stackitem.NewByteArray([]byte(p.Action)),
stackitem.NewBigInteger(big.NewInt(int64(p.Scope))),
stackitem.NewBigInteger(big.NewInt(int64(p.GrantedAt))),
stackitem.NewByteArray(p.GrantedBy.BytesBE()),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (p *PermissionGrant) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 6 {
return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items))
}
roleID, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid roleID: %w", err)
}
p.RoleID = roleID.Uint64()
resourceBytes, err := items[1].TryBytes()
if err != nil {
return fmt.Errorf("invalid resource: %w", err)
}
p.Resource = string(resourceBytes)
actionBytes, err := items[2].TryBytes()
if err != nil {
return fmt.Errorf("invalid action: %w", err)
}
p.Action = string(actionBytes)
scope, err := items[3].TryInteger()
if err != nil {
return fmt.Errorf("invalid scope: %w", err)
}
p.Scope = Scope(scope.Int64())
grantedAt, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid grantedAt: %w", err)
}
p.GrantedAt = uint32(grantedAt.Int64())
grantedByBytes, err := items[5].TryBytes()
if err != nil {
return fmt.Errorf("invalid grantedBy: %w", err)
}
p.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes)
if err != nil {
return fmt.Errorf("invalid grantedBy hash: %w", err)
}
return nil
}
// AddressRoleAssignment represents a role granted directly to an address (script hash).
// This is used for bootstrapping when Vitas may not exist yet.
type AddressRoleAssignment struct {
Address util.Uint160 // Script hash of the address
RoleID uint64 // Role ID
GrantedAt uint32 // Block height when granted
GrantedBy util.Uint160 // Granter's script hash
ExpiresAt uint32 // Expiration block (0 = never)
Active bool // Whether assignment is active
}
// ToStackItem implements stackitem.Convertible interface.
func (a *AddressRoleAssignment) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(a.Address.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(a.RoleID))),
stackitem.NewBigInteger(big.NewInt(int64(a.GrantedAt))),
stackitem.NewByteArray(a.GrantedBy.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))),
stackitem.NewBool(a.Active),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (a *AddressRoleAssignment) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 6 {
return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items))
}
addressBytes, err := items[0].TryBytes()
if err != nil {
return fmt.Errorf("invalid address: %w", err)
}
a.Address, err = util.Uint160DecodeBytesBE(addressBytes)
if err != nil {
return fmt.Errorf("invalid address hash: %w", err)
}
roleID, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid roleID: %w", err)
}
a.RoleID = roleID.Uint64()
grantedAt, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid grantedAt: %w", err)
}
a.GrantedAt = uint32(grantedAt.Int64())
grantedByBytes, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid grantedBy: %w", err)
}
a.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes)
if err != nil {
return fmt.Errorf("invalid grantedBy hash: %w", err)
}
expiresAt, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid expiresAt: %w", err)
}
a.ExpiresAt = uint32(expiresAt.Int64())
a.Active, err = items[5].TryBool()
if err != nil {
return fmt.Errorf("invalid active: %w", err)
}
return nil
}