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:
parent
5d410f9ba0
commit
6d834ff9c2
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue