Add Lex native contract for universal law and rights framework

Implement comprehensive legal framework ensuring equality for all citizens:

- Add 14 immutable constitutional rights (life, liberty, property,
  equality, due process, privacy, expression, assembly, movement,
  education, healthcare, labor, vote, asylum)

- Implement hierarchical law registry with categories:
  Constitutional, Federal, Regional, Local, Administrative

- Add configurable enforcement types per law:
  Automatic (blocks violations), Logging (emits events), Advisory

- Implement rights restriction system requiring due process:
  - All restrictions require judicial authority (RoleJudge)
  - All restrictions require a caseID (legal proceeding)
  - All restrictions must have expiration (no indefinite detention)

- Add cross-contract integration methods:
  HasRightInternal, IsRestrictedInternal, CheckPropertyRight,
  CheckMovementRight, CheckLibertyRight

- Update test data for native contract state serialization

Constitutional rights are code, not data - they cannot be amended
or removed, ensuring true immutability and universal equality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Tutus Development 2025-12-20 05:33:26 +00:00
parent 5d410f9ba0
commit 6d834ff9c2
7 changed files with 1396 additions and 9 deletions

View File

@ -191,6 +191,25 @@ type (
// GetDeficit returns the current cumulative deficit.
GetDeficit(d *dao.Simple) *big.Int
}
// ILex is an interface required from native Lex contract for
// interaction with Blockchain and other native contracts.
// Lex provides universal law registry and rights enforcement.
ILex interface {
interop.Contract
// HasRightInternal checks if subject has a specific constitutional right.
HasRightInternal(d *dao.Simple, subject util.Uint160, rightID uint64, blockHeight uint32) bool
// IsRestrictedInternal checks if a subject's right is restricted.
IsRestrictedInternal(d *dao.Simple, subject util.Uint160, rightID uint64, blockHeight uint32) bool
// CheckPropertyRight checks if subject has property rights.
CheckPropertyRight(d *dao.Simple, subject util.Uint160, blockHeight uint32) bool
// CheckMovementRight checks if subject has movement rights.
CheckMovementRight(d *dao.Simple, subject util.Uint160, blockHeight uint32) bool
// CheckLibertyRight checks if subject has liberty rights.
CheckLibertyRight(d *dao.Simple, subject util.Uint160, blockHeight uint32) bool
// Address returns the contract's script hash.
Address() util.Uint160
}
)
// Contracts is a convenient wrapper around an arbitrary set of native contracts
@ -336,6 +355,12 @@ func (cs *Contracts) Treasury() ITreasury {
return cs.ByName(nativenames.Treasury).(ITreasury)
}
// Lex returns native ILex contract implementation. It panics if
// there's no contract with proper name in cs.
func (cs *Contracts) Lex() ILex {
return cs.ByName(nativenames.Lex).(ILex)
}
// NewDefaultContracts returns a new set of default native contracts.
func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
mgmt := NewManagement()
@ -404,6 +429,13 @@ func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
federation := newFederation()
federation.NEO = neo
// Create Lex (Law Registry) contract for universal rights and law enforcement
lex := newLex()
lex.NEO = neo
lex.Vita = vita
lex.RoleRegistry = roleRegistry
lex.Federation = federation
// Wire GAS dependencies for Vita fee exemption
gas.Vita = vita
gas.Federation = federation
@ -425,5 +457,6 @@ func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
roleRegistry,
vts,
federation,
lex,
}
}

935
pkg/core/native/lex.go Normal file
View File

@ -0,0 +1,935 @@
package native
import (
"encoding/binary"
"errors"
"math/big"
"github.com/tutus-one/tutus-chain/pkg/config"
"github.com/tutus-one/tutus-chain/pkg/core/dao"
"github.com/tutus-one/tutus-chain/pkg/core/interop"
"github.com/tutus-one/tutus-chain/pkg/core/native/nativeids"
"github.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
"github.com/tutus-one/tutus-chain/pkg/core/state"
"github.com/tutus-one/tutus-chain/pkg/smartcontract"
"github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag"
"github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest"
"github.com/tutus-one/tutus-chain/pkg/util"
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)
// Lex represents the Lex native contract for universal law and rights enforcement.
// It provides:
// - Immutable constitutional rights (14 fundamental rights)
// - Hierarchical law registry (Federal > Regional > Local)
// - Rights restriction system (judicial orders)
// - Cross-contract compliance checking
type Lex struct {
interop.ContractMD
NEO INEO
Vita IVita
RoleRegistry IRoleRegistry
Federation IFederation
}
// Storage key prefixes for Lex.
const (
lexPrefixLaw byte = 0x01 // lawID -> Law
lexPrefixLawName byte = 0x02 // name -> lawID
lexPrefixLawCategory byte = 0x03 // category + lawID -> exists
lexPrefixLawJurisdiction byte = 0x04 // jurisdiction + lawID -> exists
lexPrefixLawHierarchy byte = 0x05 // parentID + childID -> exists
lexPrefixRestriction byte = 0x20 // subject + rightID -> RightsRestriction
lexPrefixSubjectRights byte = 0x21 // subject -> []restrictedRightIDs
lexPrefixLawCounter byte = 0xF0 // -> nextLawID
)
// Maximum lengths.
const (
maxLawNameLength = 256
maxReasonLength = 1024
maxLawsPerCategory = 10000
)
// Event names for Lex.
const (
LawEnactedEvent = "LawEnacted"
LawAmendedEvent = "LawAmended"
LawRepealedEvent = "LawRepealed"
LawSuspendedEvent = "LawSuspended"
LawReinstatedEvent = "LawReinstated"
RightRestrictedEvent = "RightRestricted"
RestrictionLiftedEvent = "RestrictionLifted"
RightViolationEvent = "RightViolation"
)
// Judicial authority roles (defined in RoleRegistry).
const (
RoleLegislator uint64 = 10 // Can propose and enact laws
RoleJudge uint64 = 11 // Can issue rights restrictions
RoleProsecutor uint64 = 12 // Can initiate cases
RoleDefender uint64 = 13 // Public defender
RoleEnforcer uint64 = 14 // Can execute judicial orders
)
// Various errors.
var (
ErrLawNotFound = errors.New("law not found")
ErrLawNameExists = errors.New("law name already exists")
ErrLawNameTooLong = errors.New("law name too long")
ErrInvalidLawCategory = errors.New("invalid law category")
ErrInvalidParentLaw = errors.New("invalid parent law")
ErrLawNotActive = errors.New("law is not active")
ErrCannotModifyConst = errors.New("cannot modify constitutional rights")
ErrRestrictionNotFound = errors.New("restriction not found")
ErrRestrictionExists = errors.New("restriction already exists")
ErrInvalidRightID = errors.New("invalid right ID")
ErrReasonTooLong = errors.New("reason too long")
ErrNoCaseID = errors.New("case ID required for due process")
ErrNoExpiration = errors.New("expiration required (no indefinite restrictions)")
ErrNotAuthorized = errors.New("not authorized")
ErrRightRestricted = errors.New("right is restricted")
ErrNoActiveVita = errors.New("no active Vita token")
ErrJudicialAuthRequired = errors.New("judicial authority required")
)
var _ interop.Contract = (*Lex)(nil)
// newLex creates a new Lex native contract.
func newLex() *Lex {
l := &Lex{
ContractMD: *interop.NewContractMD(nativenames.Lex, nativeids.Lex),
}
defer l.BuildHFSpecificMD(l.ActiveIn())
// Query methods (safe, read-only)
// getLaw returns a law by ID
desc := NewDescriptor("getLaw", smartcontract.ArrayType,
manifest.NewParameter("lawID", smartcontract.IntegerType))
md := NewMethodAndPrice(l.getLaw, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// getLawByName returns a law by name
desc = NewDescriptor("getLawByName", smartcontract.ArrayType,
manifest.NewParameter("name", smartcontract.StringType))
md = NewMethodAndPrice(l.getLawByName, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// isLawActive checks if a law is currently active
desc = NewDescriptor("isLawActive", smartcontract.BoolType,
manifest.NewParameter("lawID", smartcontract.IntegerType))
md = NewMethodAndPrice(l.isLawActive, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// getConstitutionalRight returns a constitutional right by ID
desc = NewDescriptor("getConstitutionalRight", smartcontract.ArrayType,
manifest.NewParameter("rightID", smartcontract.IntegerType))
md = NewMethodAndPrice(l.getConstitutionalRight, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// getAllConstitutionalRights returns all 14 constitutional rights
desc = NewDescriptor("getAllConstitutionalRights", smartcontract.ArrayType)
md = NewMethodAndPrice(l.getAllConstitutionalRights, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// hasRight checks if a subject has a specific right (not restricted)
desc = NewDescriptor("hasRight", smartcontract.BoolType,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType))
md = NewMethodAndPrice(l.hasRight, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// isRestricted checks if a subject's right is restricted
desc = NewDescriptor("isRestricted", smartcontract.BoolType,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType))
md = NewMethodAndPrice(l.isRestricted, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// getRestriction returns the restriction on a subject's right
desc = NewDescriptor("getRestriction", smartcontract.ArrayType,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType))
md = NewMethodAndPrice(l.getRestriction, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// getSubjectRestrictions returns all restrictions on a subject
desc = NewDescriptor("getSubjectRestrictions", smartcontract.ArrayType,
manifest.NewParameter("subject", smartcontract.Hash160Type))
md = NewMethodAndPrice(l.getSubjectRestrictions, 1<<16, callflag.ReadStates)
l.AddMethod(md, desc)
// getLawCount returns the total number of laws
desc = NewDescriptor("getLawCount", smartcontract.IntegerType)
md = NewMethodAndPrice(l.getLawCount, 1<<15, callflag.ReadStates)
l.AddMethod(md, desc)
// Administrative methods (committee/authority only)
// enactLaw creates a new law
desc = NewDescriptor("enactLaw", smartcontract.IntegerType,
manifest.NewParameter("name", smartcontract.StringType),
manifest.NewParameter("contentHash", smartcontract.Hash256Type),
manifest.NewParameter("category", smartcontract.IntegerType),
manifest.NewParameter("jurisdiction", smartcontract.IntegerType),
manifest.NewParameter("parentID", smartcontract.IntegerType),
manifest.NewParameter("effectiveAt", smartcontract.IntegerType),
manifest.NewParameter("enforcement", smartcontract.IntegerType))
md = NewMethodAndPrice(l.enactLaw, 1<<17, callflag.States|callflag.AllowNotify)
l.AddMethod(md, desc)
// repealLaw permanently removes a law
desc = NewDescriptor("repealLaw", smartcontract.BoolType,
manifest.NewParameter("lawID", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType))
md = NewMethodAndPrice(l.repealLaw, 1<<16, callflag.States|callflag.AllowNotify)
l.AddMethod(md, desc)
// suspendLaw temporarily suspends a law
desc = NewDescriptor("suspendLaw", smartcontract.BoolType,
manifest.NewParameter("lawID", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType),
manifest.NewParameter("duration", smartcontract.IntegerType))
md = NewMethodAndPrice(l.suspendLaw, 1<<16, callflag.States|callflag.AllowNotify)
l.AddMethod(md, desc)
// reinstateLaw reinstates a suspended law
desc = NewDescriptor("reinstateLaw", smartcontract.BoolType,
manifest.NewParameter("lawID", smartcontract.IntegerType))
md = NewMethodAndPrice(l.reinstateLaw, 1<<16, callflag.States|callflag.AllowNotify)
l.AddMethod(md, desc)
// Rights restriction methods (judicial authority only)
// restrictRight restricts a subject's right
desc = NewDescriptor("restrictRight", smartcontract.BoolType,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType),
manifest.NewParameter("restrictionType", smartcontract.IntegerType),
manifest.NewParameter("duration", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType),
manifest.NewParameter("caseID", smartcontract.Hash256Type))
md = NewMethodAndPrice(l.restrictRight, 1<<17, callflag.States|callflag.AllowNotify)
l.AddMethod(md, desc)
// liftRestriction removes a restriction
desc = NewDescriptor("liftRestriction", smartcontract.BoolType,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType))
md = NewMethodAndPrice(l.liftRestriction, 1<<16, callflag.States|callflag.AllowNotify)
l.AddMethod(md, desc)
// Events
eDesc := NewEventDescriptor(LawEnactedEvent,
manifest.NewParameter("lawID", smartcontract.IntegerType),
manifest.NewParameter("name", smartcontract.StringType),
manifest.NewParameter("category", smartcontract.IntegerType),
manifest.NewParameter("enactedBy", smartcontract.Hash160Type))
l.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(LawRepealedEvent,
manifest.NewParameter("lawID", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType))
l.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(LawSuspendedEvent,
manifest.NewParameter("lawID", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType),
manifest.NewParameter("duration", smartcontract.IntegerType))
l.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(LawReinstatedEvent,
manifest.NewParameter("lawID", smartcontract.IntegerType))
l.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(RightRestrictedEvent,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType),
manifest.NewParameter("restrictionType", smartcontract.IntegerType),
manifest.NewParameter("expiresAt", smartcontract.IntegerType),
manifest.NewParameter("caseID", smartcontract.Hash256Type))
l.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(RestrictionLiftedEvent,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType),
manifest.NewParameter("reason", smartcontract.StringType))
l.AddEvent(NewEvent(eDesc))
eDesc = NewEventDescriptor(RightViolationEvent,
manifest.NewParameter("subject", smartcontract.Hash160Type),
manifest.NewParameter("rightID", smartcontract.IntegerType),
manifest.NewParameter("action", smartcontract.StringType))
l.AddEvent(NewEvent(eDesc))
return l
}
// Metadata returns contract metadata.
func (l *Lex) Metadata() *interop.ContractMD {
return &l.ContractMD
}
// Initialize implements the Contract interface.
func (l *Lex) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
if hf != l.ActiveIn() {
return nil
}
// Initialize law counter starting at 1 (0 is reserved for root)
l.putLawCounter(ic.DAO, 1)
return nil
}
// InitializeCache implements the Contract interface.
func (l *Lex) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
return nil
}
// OnPersist implements the Contract interface.
func (l *Lex) OnPersist(ic *interop.Context) error {
return nil
}
// PostPersist implements the Contract interface.
func (l *Lex) PostPersist(ic *interop.Context) error {
return nil
}
// ActiveIn implements the Contract interface.
func (l *Lex) ActiveIn() *config.Hardfork {
return nil
}
// Address returns the contract's script hash.
func (l *Lex) Address() util.Uint160 {
return l.Hash
}
// ==================== Storage Key Helpers ====================
func (l *Lex) makeLawKey(lawID uint64) []byte {
key := make([]byte, 9)
key[0] = lexPrefixLaw
binary.BigEndian.PutUint64(key[1:], lawID)
return key
}
func (l *Lex) makeLawNameKey(name string) []byte {
key := make([]byte, 1+len(name))
key[0] = lexPrefixLawName
copy(key[1:], name)
return key
}
func (l *Lex) makeRestrictionKey(subject util.Uint160, rightID uint64) []byte {
key := make([]byte, 29)
key[0] = lexPrefixRestriction
copy(key[1:21], subject[:])
binary.BigEndian.PutUint64(key[21:], rightID)
return key
}
func (l *Lex) makeSubjectRightsKey(subject util.Uint160) []byte {
key := make([]byte, 21)
key[0] = lexPrefixSubjectRights
copy(key[1:], subject[:])
return key
}
// ==================== Law Counter ====================
func (l *Lex) getLawCounter(d *dao.Simple) uint64 {
si := d.GetStorageItem(l.ID, []byte{lexPrefixLawCounter})
if si == nil {
return 1
}
return binary.BigEndian.Uint64(si)
}
func (l *Lex) putLawCounter(d *dao.Simple, count uint64) {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, count)
d.PutStorageItem(l.ID, []byte{lexPrefixLawCounter}, buf)
}
// ==================== Internal Storage Methods ====================
func (l *Lex) getLawInternal(d *dao.Simple, lawID uint64) *state.Law {
si := d.GetStorageItem(l.ID, l.makeLawKey(lawID))
if si == nil {
return nil
}
law, err := state.LawFromBytes(si)
if err != nil {
return nil
}
return law
}
func (l *Lex) putLaw(d *dao.Simple, law *state.Law) {
d.PutStorageItem(l.ID, l.makeLawKey(law.ID), law.Bytes())
}
func (l *Lex) getLawIDByName(d *dao.Simple, name string) (uint64, bool) {
si := d.GetStorageItem(l.ID, l.makeLawNameKey(name))
if si == nil {
return 0, false
}
return binary.BigEndian.Uint64(si), true
}
func (l *Lex) putLawNameIndex(d *dao.Simple, name string, lawID uint64) {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, lawID)
d.PutStorageItem(l.ID, l.makeLawNameKey(name), buf)
}
func (l *Lex) getRestrictionInternal(d *dao.Simple, subject util.Uint160, rightID uint64) *state.RightsRestriction {
si := d.GetStorageItem(l.ID, l.makeRestrictionKey(subject, rightID))
if si == nil {
return nil
}
r, err := state.RightsRestrictionFromBytes(si)
if err != nil {
return nil
}
return r
}
func (l *Lex) putRestriction(d *dao.Simple, r *state.RightsRestriction) {
d.PutStorageItem(l.ID, l.makeRestrictionKey(r.Subject, r.RightID), r.Bytes())
}
func (l *Lex) deleteRestriction(d *dao.Simple, subject util.Uint160, rightID uint64) {
d.DeleteStorageItem(l.ID, l.makeRestrictionKey(subject, rightID))
}
func (l *Lex) getSubjectRestrictedRights(d *dao.Simple, subject util.Uint160) []uint64 {
si := d.GetStorageItem(l.ID, l.makeSubjectRightsKey(subject))
if si == nil {
return nil
}
count := len(si) / 8
rights := make([]uint64, count)
for i := 0; i < count; i++ {
rights[i] = binary.BigEndian.Uint64(si[i*8:])
}
return rights
}
func (l *Lex) putSubjectRestrictedRights(d *dao.Simple, subject util.Uint160, rights []uint64) {
if len(rights) == 0 {
d.DeleteStorageItem(l.ID, l.makeSubjectRightsKey(subject))
return
}
buf := make([]byte, len(rights)*8)
for i, r := range rights {
binary.BigEndian.PutUint64(buf[i*8:], r)
}
d.PutStorageItem(l.ID, l.makeSubjectRightsKey(subject), buf)
}
func (l *Lex) addSubjectRestrictedRight(d *dao.Simple, subject util.Uint160, rightID uint64) {
rights := l.getSubjectRestrictedRights(d, subject)
for _, r := range rights {
if r == rightID {
return // Already exists
}
}
rights = append(rights, rightID)
l.putSubjectRestrictedRights(d, subject, rights)
}
func (l *Lex) removeSubjectRestrictedRight(d *dao.Simple, subject util.Uint160, rightID uint64) {
rights := l.getSubjectRestrictedRights(d, subject)
for i, r := range rights {
if r == rightID {
rights = append(rights[:i], rights[i+1:]...)
break
}
}
l.putSubjectRestrictedRights(d, subject, rights)
}
// ==================== Authorization Helpers ====================
func (l *Lex) checkCommittee(ic *interop.Context) error {
if l.RoleRegistry != nil && l.RoleRegistry.CheckCommittee(ic) {
return nil
}
if l.NEO != nil && l.NEO.CheckCommittee(ic) {
return nil
}
return ErrNotAuthorized
}
func (l *Lex) checkJudicialAuthority(ic *interop.Context) error {
if l.RoleRegistry == nil {
// Fall back to committee check if no RoleRegistry
return l.checkCommittee(ic)
}
caller := ic.VM.GetCallingScriptHash()
if l.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleJudge, ic.Block.Index) {
return nil
}
// Also allow committee
if l.RoleRegistry.CheckCommittee(ic) {
return nil
}
return ErrJudicialAuthRequired
}
// ==================== Query Methods ====================
func (l *Lex) getLaw(ic *interop.Context, args []stackitem.Item) stackitem.Item {
lawID := toUint64(args[0])
law := l.getLawInternal(ic.DAO, lawID)
if law == nil {
return stackitem.Null{}
}
item, _ := law.ToStackItem()
return item
}
func (l *Lex) getLawByName(ic *interop.Context, args []stackitem.Item) stackitem.Item {
name := toString(args[0])
lawID, found := l.getLawIDByName(ic.DAO, name)
if !found {
return stackitem.Null{}
}
law := l.getLawInternal(ic.DAO, lawID)
if law == nil {
return stackitem.Null{}
}
item, _ := law.ToStackItem()
return item
}
func (l *Lex) isLawActive(ic *interop.Context, args []stackitem.Item) stackitem.Item {
lawID := toUint64(args[0])
law := l.getLawInternal(ic.DAO, lawID)
if law == nil {
return stackitem.NewBool(false)
}
// Check status and effective dates
if law.Status != state.LawStatusActive {
return stackitem.NewBool(false)
}
if law.EffectiveAt > ic.Block.Index {
return stackitem.NewBool(false)
}
if law.ExpiresAt != 0 && law.ExpiresAt <= ic.Block.Index {
return stackitem.NewBool(false)
}
return stackitem.NewBool(true)
}
func (l *Lex) getConstitutionalRight(ic *interop.Context, args []stackitem.Item) stackitem.Item {
rightID := toUint64(args[0])
right := state.GetConstitutionalRight(rightID)
if right == nil {
return stackitem.Null{}
}
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(right.ID))),
stackitem.NewByteArray([]byte(right.Name)),
stackitem.NewByteArray([]byte(right.Description)),
stackitem.NewBigInteger(big.NewInt(int64(right.Enforcement))),
})
}
func (l *Lex) getAllConstitutionalRights(ic *interop.Context, args []stackitem.Item) stackitem.Item {
rights := state.GetConstitutionalRights()
items := make([]stackitem.Item, len(rights))
for i, r := range rights {
items[i] = 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.Enforcement))),
})
}
return stackitem.NewArray(items)
}
func (l *Lex) hasRight(ic *interop.Context, args []stackitem.Item) stackitem.Item {
subject := toUint160(args[0])
rightID := toUint64(args[1])
return stackitem.NewBool(l.HasRightInternal(ic.DAO, subject, rightID, ic.Block.Index))
}
func (l *Lex) isRestricted(ic *interop.Context, args []stackitem.Item) stackitem.Item {
subject := toUint160(args[0])
rightID := toUint64(args[1])
return stackitem.NewBool(l.IsRestrictedInternal(ic.DAO, subject, rightID, ic.Block.Index))
}
func (l *Lex) getRestriction(ic *interop.Context, args []stackitem.Item) stackitem.Item {
subject := toUint160(args[0])
rightID := toUint64(args[1])
r := l.getRestrictionInternal(ic.DAO, subject, rightID)
if r == nil {
return stackitem.Null{}
}
item, _ := r.ToStackItem()
return item
}
func (l *Lex) getSubjectRestrictions(ic *interop.Context, args []stackitem.Item) stackitem.Item {
subject := toUint160(args[0])
rightIDs := l.getSubjectRestrictedRights(ic.DAO, subject)
items := make([]stackitem.Item, 0)
for _, rightID := range rightIDs {
r := l.getRestrictionInternal(ic.DAO, subject, rightID)
if r != nil && !r.IsExpired(ic.Block.Index) {
item, _ := r.ToStackItem()
items = append(items, item)
}
}
return stackitem.NewArray(items)
}
func (l *Lex) getLawCount(ic *interop.Context, args []stackitem.Item) stackitem.Item {
count := l.getLawCounter(ic.DAO)
return stackitem.NewBigInteger(big.NewInt(int64(count - 1))) // Counter starts at 1
}
// ==================== Law Management Methods ====================
func (l *Lex) enactLaw(ic *interop.Context, args []stackitem.Item) stackitem.Item {
if err := l.checkCommittee(ic); err != nil {
panic(err)
}
name := toString(args[0])
if len(name) > maxLawNameLength {
panic(ErrLawNameTooLong)
}
contentHashBytes, err := args[1].TryBytes()
if err != nil {
panic("invalid content hash")
}
var contentHash util.Uint256
if len(contentHashBytes) == 32 {
copy(contentHash[:], contentHashBytes)
}
category := state.LawCategory(toUint64(args[2]))
if category < state.LawCategoryFederal || category > state.LawCategoryAdministrative {
panic(ErrInvalidLawCategory)
}
// Cannot create constitutional laws via enactLaw - they are immutable
if category == state.LawCategoryConstitutional {
panic(ErrCannotModifyConst)
}
jurisdiction := uint32(toUint64(args[3]))
parentID := toUint64(args[4])
effectiveAt := uint32(toUint64(args[5]))
enforcement := state.EnforcementType(toUint64(args[6]))
// Check if name exists
if _, exists := l.getLawIDByName(ic.DAO, name); exists {
panic(ErrLawNameExists)
}
// Validate parent law if specified
if parentID != 0 {
parent := l.getLawInternal(ic.DAO, parentID)
if parent == nil {
panic(ErrInvalidParentLaw)
}
if parent.Status != state.LawStatusActive {
panic(ErrLawNotActive)
}
// Child category must be lower or equal precedence than parent
if category < parent.Category {
panic(ErrInvalidLawCategory)
}
}
// Create law
lawID := l.getLawCounter(ic.DAO)
law := &state.Law{
ID: lawID,
Name: name,
ContentHash: contentHash,
Category: category,
Jurisdiction: jurisdiction,
ParentID: parentID,
EffectiveAt: effectiveAt,
ExpiresAt: 0, // Perpetual by default
EnactedAt: ic.Block.Index,
EnactedBy: ic.VM.GetCallingScriptHash(),
Status: state.LawStatusActive,
SupersededBy: 0,
RequiresVita: true,
Enforcement: enforcement,
}
l.putLaw(ic.DAO, law)
l.putLawNameIndex(ic.DAO, name, lawID)
l.putLawCounter(ic.DAO, lawID+1)
ic.AddNotification(l.Hash, LawEnactedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(lawID))),
stackitem.NewByteArray([]byte(name)),
stackitem.NewBigInteger(big.NewInt(int64(category))),
stackitem.NewByteArray(law.EnactedBy.BytesBE()),
}))
return stackitem.NewBigInteger(big.NewInt(int64(lawID)))
}
func (l *Lex) repealLaw(ic *interop.Context, args []stackitem.Item) stackitem.Item {
if err := l.checkCommittee(ic); err != nil {
panic(err)
}
lawID := toUint64(args[0])
reason := toString(args[1])
if len(reason) > maxReasonLength {
panic(ErrReasonTooLong)
}
law := l.getLawInternal(ic.DAO, lawID)
if law == nil {
panic(ErrLawNotFound)
}
if law.Category == state.LawCategoryConstitutional {
panic(ErrCannotModifyConst)
}
if law.Status == state.LawStatusRepealed {
return stackitem.NewBool(false)
}
law.Status = state.LawStatusRepealed
l.putLaw(ic.DAO, law)
ic.AddNotification(l.Hash, LawRepealedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(lawID))),
stackitem.NewByteArray([]byte(reason)),
}))
return stackitem.NewBool(true)
}
func (l *Lex) suspendLaw(ic *interop.Context, args []stackitem.Item) stackitem.Item {
if err := l.checkCommittee(ic); err != nil {
panic(err)
}
lawID := toUint64(args[0])
reason := toString(args[1])
duration := uint32(toUint64(args[2]))
if len(reason) > maxReasonLength {
panic(ErrReasonTooLong)
}
law := l.getLawInternal(ic.DAO, lawID)
if law == nil {
panic(ErrLawNotFound)
}
if law.Category == state.LawCategoryConstitutional {
panic(ErrCannotModifyConst)
}
if law.Status != state.LawStatusActive {
panic(ErrLawNotActive)
}
law.Status = state.LawStatusSuspended
l.putLaw(ic.DAO, law)
ic.AddNotification(l.Hash, LawSuspendedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(lawID))),
stackitem.NewByteArray([]byte(reason)),
stackitem.NewBigInteger(big.NewInt(int64(duration))),
}))
return stackitem.NewBool(true)
}
func (l *Lex) reinstateLaw(ic *interop.Context, args []stackitem.Item) stackitem.Item {
if err := l.checkCommittee(ic); err != nil {
panic(err)
}
lawID := toUint64(args[0])
law := l.getLawInternal(ic.DAO, lawID)
if law == nil {
panic(ErrLawNotFound)
}
if law.Status != state.LawStatusSuspended {
return stackitem.NewBool(false)
}
law.Status = state.LawStatusActive
l.putLaw(ic.DAO, law)
ic.AddNotification(l.Hash, LawReinstatedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(lawID))),
}))
return stackitem.NewBool(true)
}
// ==================== Rights Restriction Methods ====================
func (l *Lex) restrictRight(ic *interop.Context, args []stackitem.Item) stackitem.Item {
if err := l.checkJudicialAuthority(ic); err != nil {
panic(err)
}
subject := toUint160(args[0])
rightID := toUint64(args[1])
restrictionType := state.RestrictionType(toUint64(args[2]))
duration := uint32(toUint64(args[3]))
reason := toString(args[4])
caseIDBytes, err := args[5].TryBytes()
if err != nil {
panic("invalid case ID")
}
var caseID util.Uint256
if len(caseIDBytes) == 32 {
copy(caseID[:], caseIDBytes)
}
// Validate inputs
if !state.IsConstitutionalRight(rightID) {
panic(ErrInvalidRightID)
}
if len(reason) > maxReasonLength {
panic(ErrReasonTooLong)
}
// Due process requires a case ID
if caseID == (util.Uint256{}) {
panic(ErrNoCaseID)
}
// No indefinite restrictions allowed
if duration == 0 {
panic(ErrNoExpiration)
}
// Check if restriction already exists
existing := l.getRestrictionInternal(ic.DAO, subject, rightID)
if existing != nil && !existing.IsExpired(ic.Block.Index) {
panic(ErrRestrictionExists)
}
r := &state.RightsRestriction{
Subject: subject,
RightID: rightID,
RestrictionType: restrictionType,
IssuedBy: ic.VM.GetCallingScriptHash(),
IssuedAt: ic.Block.Index,
ExpiresAt: ic.Block.Index + duration,
Reason: reason,
CaseID: caseID,
}
l.putRestriction(ic.DAO, r)
l.addSubjectRestrictedRight(ic.DAO, subject, rightID)
ic.AddNotification(l.Hash, RightRestrictedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(subject.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(rightID))),
stackitem.NewBigInteger(big.NewInt(int64(restrictionType))),
stackitem.NewBigInteger(big.NewInt(int64(r.ExpiresAt))),
stackitem.NewByteArray(caseID[:]),
}))
return stackitem.NewBool(true)
}
func (l *Lex) liftRestriction(ic *interop.Context, args []stackitem.Item) stackitem.Item {
if err := l.checkJudicialAuthority(ic); err != nil {
panic(err)
}
subject := toUint160(args[0])
rightID := toUint64(args[1])
reason := toString(args[2])
if len(reason) > maxReasonLength {
panic(ErrReasonTooLong)
}
r := l.getRestrictionInternal(ic.DAO, subject, rightID)
if r == nil {
panic(ErrRestrictionNotFound)
}
l.deleteRestriction(ic.DAO, subject, rightID)
l.removeSubjectRestrictedRight(ic.DAO, subject, rightID)
ic.AddNotification(l.Hash, RestrictionLiftedEvent, stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(subject.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(rightID))),
stackitem.NewByteArray([]byte(reason)),
}))
return stackitem.NewBool(true)
}
// ==================== Cross-Contract Access Methods ====================
// HasRightInternal checks if a subject has a specific right (for cross-native access).
// Returns true if the subject has an active Vita and the right is not restricted.
func (l *Lex) HasRightInternal(d *dao.Simple, subject util.Uint160, rightID uint64, blockHeight uint32) bool {
// Check if it's a constitutional right
if !state.IsConstitutionalRight(rightID) {
return false
}
// Must have active Vita to have rights
if l.Vita != nil {
token, err := l.Vita.GetTokenByOwner(d, subject)
if err != nil || token == nil || token.Status != state.TokenStatusActive {
// Check asylum (humanitarian override)
if l.Federation != nil && l.Federation.HasAsylum(d, subject) {
// Asylum seekers retain rights even without local active Vita
} else {
return false
}
}
}
// Check if right is restricted
return !l.IsRestrictedInternal(d, subject, rightID, blockHeight)
}
// IsRestrictedInternal checks if a subject's right is restricted (for cross-native access).
func (l *Lex) IsRestrictedInternal(d *dao.Simple, subject util.Uint160, rightID uint64, blockHeight uint32) bool {
r := l.getRestrictionInternal(d, subject, rightID)
if r == nil {
return false
}
// Check if expired
return !r.IsExpired(blockHeight)
}
// GetConstitutionalRightInternal returns a constitutional right (for cross-native access).
func (l *Lex) GetConstitutionalRightInternal(rightID uint64) *state.ConstitutionalRight {
return state.GetConstitutionalRight(rightID)
}
// CheckPropertyRight checks if subject has property rights (for VTS integration).
func (l *Lex) CheckPropertyRight(d *dao.Simple, subject util.Uint160, blockHeight uint32) bool {
return l.HasRightInternal(d, subject, state.RightProperty, blockHeight)
}
// CheckMovementRight checks if subject has movement rights (for Federation integration).
func (l *Lex) CheckMovementRight(d *dao.Simple, subject util.Uint160, blockHeight uint32) bool {
return l.HasRightInternal(d, subject, state.RightMovement, blockHeight)
}
// CheckLibertyRight checks if subject has liberty rights.
func (l *Lex) CheckLibertyRight(d *dao.Simple, subject util.Uint160, blockHeight uint32) bool {
return l.HasRightInternal(d, subject, state.RightLiberty, blockHeight)
}

File diff suppressed because one or more lines are too long

View File

@ -39,4 +39,6 @@ var (
VTS = util.Uint160{0x68, 0x34, 0x5e, 0x82, 0x6d, 0x8a, 0xff, 0x41, 0x48, 0x23, 0x60, 0xd9, 0x83, 0xa3, 0xd0, 0xf9, 0xb7, 0x59, 0x36, 0x89}
// Federation is a hash of native Federation contract.
Federation = util.Uint160{0xfd, 0x78, 0x70, 0xb, 0xa9, 0x19, 0xed, 0xb0, 0x19, 0x40, 0xdd, 0xc7, 0x97, 0x48, 0xf4, 0x25, 0x1d, 0xb0, 0x5, 0x1f}
// Lex is a hash of native Lex contract.
Lex = util.Uint160{0x2e, 0x3f, 0xb7, 0x5, 0x8, 0x17, 0xef, 0xb1, 0xc2, 0xbe, 0x68, 0xc4, 0xd4, 0xde, 0xc6, 0xf6, 0x2d, 0x92, 0x96, 0xe6}
)

View File

@ -37,4 +37,6 @@ const (
VTS int32 = -14
// Federation is an ID of native Federation contract.
Federation int32 = -15
// Lex is an ID of native Lex contract.
Lex int32 = -16
)

View File

@ -17,6 +17,7 @@ const (
RoleRegistry = "RoleRegistry"
VTS = "VTS"
Federation = "Federation"
Lex = "Lex"
)
// All contains the list of all native contract names ordered by the contract ID.
@ -36,6 +37,7 @@ var All = []string{
RoleRegistry,
VTS,
Federation,
Lex,
}
// IsValid checks if the name is a valid native contract's name.
@ -54,5 +56,6 @@ func IsValid(name string) bool {
name == Vita ||
name == RoleRegistry ||
name == VTS ||
name == Federation
name == Federation ||
name == Lex
}

410
pkg/core/state/lex.go Normal file
View File

@ -0,0 +1,410 @@
package state
import (
"errors"
"fmt"
"math/big"
"github.com/tutus-one/tutus-chain/pkg/util"
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)
// LawCategory represents the hierarchical category of a law.
type LawCategory uint8
const (
// LawCategoryConstitutional represents immutable constitutional rights.
LawCategoryConstitutional LawCategory = 1
// LawCategoryFederal represents national/federal laws.
LawCategoryFederal LawCategory = 2
// LawCategoryRegional represents state/province laws.
LawCategoryRegional LawCategory = 3
// LawCategoryLocal represents municipal/local laws.
LawCategoryLocal LawCategory = 4
// LawCategoryAdministrative represents regulations and administrative rules.
LawCategoryAdministrative LawCategory = 5
)
// LawStatus represents the current status of a law.
type LawStatus uint8
const (
// LawStatusDraft indicates a proposed law not yet voted.
LawStatusDraft LawStatus = 0
// LawStatusActive indicates a law currently in force.
LawStatusActive LawStatus = 1
// LawStatusSuspended indicates a temporarily suspended law.
LawStatusSuspended LawStatus = 2
// LawStatusRepealed indicates a permanently removed law.
LawStatusRepealed LawStatus = 3
// LawStatusSuperseded indicates a law replaced by a newer one.
LawStatusSuperseded LawStatus = 4
)
// EnforcementType specifies how a law is enforced.
type EnforcementType uint8
const (
// EnforcementAutomatic means smart contracts block violations.
EnforcementAutomatic EnforcementType = 1
// EnforcementLogging means violations emit events but allow transaction.
EnforcementLogging EnforcementType = 2
// EnforcementAdvisory means non-binding guidance only.
EnforcementAdvisory EnforcementType = 3
)
// RestrictionType represents the type of rights restriction.
type RestrictionType uint8
const (
// RestrictionSuspend temporarily suspends a right.
RestrictionSuspend RestrictionType = 1
// RestrictionLimit limits a right (e.g., movement within area).
RestrictionLimit RestrictionType = 2
// RestrictionCondition makes a right conditional (e.g., probation).
RestrictionCondition RestrictionType = 3
)
// ConstitutionalRight IDs - these are immutable and built into the protocol.
const (
RightLife uint64 = 1 // Right to life
RightLiberty uint64 = 2 // Freedom from arbitrary detention
RightProperty uint64 = 3 // Right to own and transfer assets
RightEquality uint64 = 4 // Equal treatment under law
RightDueProcess uint64 = 5 // Fair legal proceedings
RightPrivacy uint64 = 6 // Privacy of personal data
RightExpression uint64 = 7 // Freedom of expression
RightAssembly uint64 = 8 // Freedom of assembly
RightMovement uint64 = 9 // Freedom of movement
RightEducation uint64 = 10 // Right to education
RightHealthcare uint64 = 11 // Right to healthcare
RightLabor uint64 = 12 // Right to fair labor
RightVote uint64 = 13 // Right to vote
RightAsylum uint64 = 14 // Right to seek asylum
// MaxConstitutionalRight is the highest constitutional right ID.
MaxConstitutionalRight uint64 = 14
)
// Law represents a law in the registry.
type Law struct {
ID uint64 // Unique law identifier
Name string // Human-readable name
ContentHash util.Uint256 // Hash of full law text (stored off-chain)
Category LawCategory // Constitutional, Federal, Regional, Local
Jurisdiction uint32 // Jurisdiction ID (0 = global)
ParentID uint64 // Parent law in hierarchy (0 = root)
EffectiveAt uint32 // Block height when law becomes active
ExpiresAt uint32 // Block height when law expires (0 = perpetual)
EnactedAt uint32 // Block height of enactment
EnactedBy util.Uint160 // Authority that enacted
Status LawStatus // Active, Suspended, Repealed, Superseded
SupersededBy uint64 // ID of law that replaced this one
RequiresVita bool // Whether law applies only to Vita holders
Enforcement EnforcementType // How law is enforced
}
// ToStackItem implements stackitem.Convertible interface.
func (l *Law) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(l.ID))),
stackitem.NewByteArray([]byte(l.Name)),
stackitem.NewByteArray(l.ContentHash[:]),
stackitem.NewBigInteger(big.NewInt(int64(l.Category))),
stackitem.NewBigInteger(big.NewInt(int64(l.Jurisdiction))),
stackitem.NewBigInteger(big.NewInt(int64(l.ParentID))),
stackitem.NewBigInteger(big.NewInt(int64(l.EffectiveAt))),
stackitem.NewBigInteger(big.NewInt(int64(l.ExpiresAt))),
stackitem.NewBigInteger(big.NewInt(int64(l.EnactedAt))),
stackitem.NewByteArray(l.EnactedBy.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(l.Status))),
stackitem.NewBigInteger(big.NewInt(int64(l.SupersededBy))),
stackitem.NewBool(l.RequiresVita),
stackitem.NewBigInteger(big.NewInt(int64(l.Enforcement))),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (l *Law) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 14 {
return fmt.Errorf("wrong number of elements: expected 14, got %d", len(items))
}
id, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid ID: %w", err)
}
l.ID = id.Uint64()
name, err := items[1].TryBytes()
if err != nil {
return fmt.Errorf("invalid Name: %w", err)
}
l.Name = string(name)
contentHash, err := items[2].TryBytes()
if err != nil {
return fmt.Errorf("invalid ContentHash: %w", err)
}
if len(contentHash) == 32 {
copy(l.ContentHash[:], contentHash)
}
category, err := items[3].TryInteger()
if err != nil {
return fmt.Errorf("invalid Category: %w", err)
}
l.Category = LawCategory(category.Uint64())
jurisdiction, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid Jurisdiction: %w", err)
}
l.Jurisdiction = uint32(jurisdiction.Uint64())
parentID, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid ParentID: %w", err)
}
l.ParentID = parentID.Uint64()
effectiveAt, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid EffectiveAt: %w", err)
}
l.EffectiveAt = uint32(effectiveAt.Uint64())
expiresAt, err := items[7].TryInteger()
if err != nil {
return fmt.Errorf("invalid ExpiresAt: %w", err)
}
l.ExpiresAt = uint32(expiresAt.Uint64())
enactedAt, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid EnactedAt: %w", err)
}
l.EnactedAt = uint32(enactedAt.Uint64())
enactedBy, err := items[9].TryBytes()
if err != nil {
return fmt.Errorf("invalid EnactedBy: %w", err)
}
l.EnactedBy, err = util.Uint160DecodeBytesBE(enactedBy)
if err != nil {
return fmt.Errorf("invalid EnactedBy address: %w", err)
}
status, err := items[10].TryInteger()
if err != nil {
return fmt.Errorf("invalid Status: %w", err)
}
l.Status = LawStatus(status.Uint64())
supersededBy, err := items[11].TryInteger()
if err != nil {
return fmt.Errorf("invalid SupersededBy: %w", err)
}
l.SupersededBy = supersededBy.Uint64()
requiresVita, err := items[12].TryBool()
if err != nil {
return fmt.Errorf("invalid RequiresVita: %w", err)
}
l.RequiresVita = requiresVita
enforcement, err := items[13].TryInteger()
if err != nil {
return fmt.Errorf("invalid Enforcement: %w", err)
}
l.Enforcement = EnforcementType(enforcement.Uint64())
return nil
}
// Bytes serializes the Law to bytes.
func (l *Law) Bytes() []byte {
item, _ := l.ToStackItem()
data, _ := stackitem.Serialize(item)
return data
}
// LawFromBytes deserializes a Law from bytes.
func LawFromBytes(data []byte) (*Law, error) {
item, err := stackitem.Deserialize(data)
if err != nil {
return nil, err
}
l := &Law{}
if err := l.FromStackItem(item); err != nil {
return nil, err
}
return l, nil
}
// RightsRestriction represents a restriction on a constitutional right.
type RightsRestriction struct {
Subject util.Uint160 // Who is restricted
RightID uint64 // Which right is restricted
RestrictionType RestrictionType // Type of restriction
IssuedBy util.Uint160 // Authority that issued
IssuedAt uint32 // Block height when issued
ExpiresAt uint32 // When restriction ends (must be set)
Reason string // Legal basis
CaseID util.Uint256 // Associated legal case (required for due process)
}
// ToStackItem implements stackitem.Convertible interface.
func (r *RightsRestriction) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(r.Subject.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(r.RightID))),
stackitem.NewBigInteger(big.NewInt(int64(r.RestrictionType))),
stackitem.NewByteArray(r.IssuedBy.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(r.IssuedAt))),
stackitem.NewBigInteger(big.NewInt(int64(r.ExpiresAt))),
stackitem.NewByteArray([]byte(r.Reason)),
stackitem.NewByteArray(r.CaseID[:]),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (r *RightsRestriction) 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))
}
subject, err := items[0].TryBytes()
if err != nil {
return fmt.Errorf("invalid Subject: %w", err)
}
r.Subject, err = util.Uint160DecodeBytesBE(subject)
if err != nil {
return fmt.Errorf("invalid Subject address: %w", err)
}
rightID, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid RightID: %w", err)
}
r.RightID = rightID.Uint64()
restrictionType, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid RestrictionType: %w", err)
}
r.RestrictionType = RestrictionType(restrictionType.Uint64())
issuedBy, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid IssuedBy: %w", err)
}
r.IssuedBy, err = util.Uint160DecodeBytesBE(issuedBy)
if err != nil {
return fmt.Errorf("invalid IssuedBy address: %w", err)
}
issuedAt, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid IssuedAt: %w", err)
}
r.IssuedAt = uint32(issuedAt.Uint64())
expiresAt, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid ExpiresAt: %w", err)
}
r.ExpiresAt = uint32(expiresAt.Uint64())
reason, err := items[6].TryBytes()
if err != nil {
return fmt.Errorf("invalid Reason: %w", err)
}
r.Reason = string(reason)
caseID, err := items[7].TryBytes()
if err != nil {
return fmt.Errorf("invalid CaseID: %w", err)
}
if len(caseID) == 32 {
copy(r.CaseID[:], caseID)
}
return nil
}
// Bytes serializes the RightsRestriction to bytes.
func (r *RightsRestriction) Bytes() []byte {
item, _ := r.ToStackItem()
data, _ := stackitem.Serialize(item)
return data
}
// RightsRestrictionFromBytes deserializes a RightsRestriction from bytes.
func RightsRestrictionFromBytes(data []byte) (*RightsRestriction, error) {
item, err := stackitem.Deserialize(data)
if err != nil {
return nil, err
}
r := &RightsRestriction{}
if err := r.FromStackItem(item); err != nil {
return nil, err
}
return r, nil
}
// IsExpired returns true if the restriction has expired.
func (r *RightsRestriction) IsExpired(currentBlock uint32) bool {
return r.ExpiresAt != 0 && r.ExpiresAt <= currentBlock
}
// ConstitutionalRight represents an immutable constitutional right.
type ConstitutionalRight struct {
ID uint64 // Right ID (1-14)
Name string // Human-readable name
Description string // Description of the right
Enforcement EnforcementType // How right is enforced
}
// GetConstitutionalRights returns all 14 constitutional rights.
// These are hardcoded and immutable.
func GetConstitutionalRights() []ConstitutionalRight {
return []ConstitutionalRight{
{RightLife, "Life", "Protection from arbitrary death", EnforcementAutomatic},
{RightLiberty, "Liberty", "Freedom from arbitrary detention", EnforcementAutomatic},
{RightProperty, "Property", "Right to own and transfer assets", EnforcementAutomatic},
{RightEquality, "Equality", "Equal treatment under law", EnforcementAutomatic},
{RightDueProcess, "DueProcess", "Fair legal proceedings before restriction", EnforcementAutomatic},
{RightPrivacy, "Privacy", "Privacy of personal data", EnforcementAutomatic},
{RightExpression, "Expression", "Freedom of expression", EnforcementLogging},
{RightAssembly, "Assembly", "Freedom of assembly", EnforcementLogging},
{RightMovement, "Movement", "Freedom of movement", EnforcementAutomatic},
{RightEducation, "Education", "Right to education", EnforcementLogging},
{RightHealthcare, "Healthcare", "Right to healthcare", EnforcementLogging},
{RightLabor, "Labor", "Right to fair labor conditions", EnforcementLogging},
{RightVote, "Vote", "Right to participate in democracy", EnforcementAutomatic},
{RightAsylum, "Asylum", "Right to seek protection from persecution", EnforcementAutomatic},
}
}
// GetConstitutionalRight returns a specific constitutional right by ID.
func GetConstitutionalRight(id uint64) *ConstitutionalRight {
if id < 1 || id > MaxConstitutionalRight {
return nil
}
rights := GetConstitutionalRights()
return &rights[id-1]
}
// IsConstitutionalRight returns true if the right ID is a constitutional right.
func IsConstitutionalRight(id uint64) bool {
return id >= 1 && id <= MaxConstitutionalRight
}