Add Opus native contract for AI workforce integration
Implement comprehensive AI workforce management infrastructure: - Operator profiles: Registration (requires Vita), verification, supervision via RoleOpusSupervisor (ID 24) - AI Worker lifecycle: Registration, updates, suspension, decommissioning with operator ownership tracking - Task system: Assignment, progress tracking, completion, cancellation, and dispute resolution - Capability certification: Proficiency levels, certification tracking, and supervisor-managed verification - AI Tribute: Configurable percentage of AI-generated value directed to Treasury for societal benefit - Cross-contract integration: - Vita: Operator identity verification - Lex: Labor rights enforcement (RightLabor) - RoleRegistry: Supervisor role authorization - VTS: Registration fees and task payments - Treasury: Tribute collection Contract ID: -22, following Tribute (-21) 🤖 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
1d96eb7a6e
commit
0e980e7342
|
|
@ -224,6 +224,10 @@ type Blockchain struct {
|
||||||
lex native.ILex
|
lex native.ILex
|
||||||
eligere native.IEligere
|
eligere native.IEligere
|
||||||
scire native.IScire
|
scire native.IScire
|
||||||
|
salus native.ISalus
|
||||||
|
sese native.ISese
|
||||||
|
tribute native.ITribute
|
||||||
|
opus native.IOpus
|
||||||
|
|
||||||
extensible atomic.Value
|
extensible atomic.Value
|
||||||
|
|
||||||
|
|
@ -495,6 +499,22 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger, newN
|
||||||
if err := validateNative(bc.scire, nativeids.Scire, nativenames.Scire, nativehashes.Scire); err != nil {
|
if err := validateNative(bc.scire, nativeids.Scire, nativenames.Scire, nativehashes.Scire); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
bc.salus = bc.contracts.Salus()
|
||||||
|
if err := validateNative(bc.salus, nativeids.Salus, nativenames.Salus, nativehashes.Salus); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bc.sese = bc.contracts.Sese()
|
||||||
|
if err := validateNative(bc.sese, nativeids.Sese, nativenames.Sese, nativehashes.Sese); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bc.tribute = bc.contracts.Tribute()
|
||||||
|
if err := validateNative(bc.tribute, nativeids.Tribute, nativenames.Tribute, nativehashes.Tribute); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bc.opus = bc.contracts.Opus()
|
||||||
|
if err := validateNative(bc.opus, nativeids.Opus, nativenames.Opus, nativehashes.Opus); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
bc.persistCond = sync.NewCond(&bc.lock)
|
bc.persistCond = sync.NewCond(&bc.lock)
|
||||||
bc.gcBlockTimes, _ = lru.New[uint32, uint64](defaultBlockTimesCache) // Never errors for positive size
|
bc.gcBlockTimes, _ = lru.New[uint32, uint64](defaultBlockTimesCache) // Never errors for positive size
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,60 @@ type (
|
||||||
// Address returns the contract's script hash.
|
// Address returns the contract's script hash.
|
||||||
Address() util.Uint160
|
Address() util.Uint160
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ISalus is an interface required from native Salus contract for
|
||||||
|
// interaction with Blockchain and other native contracts.
|
||||||
|
// Salus provides universal healthcare infrastructure.
|
||||||
|
ISalus interface {
|
||||||
|
interop.Contract
|
||||||
|
// GetAccountByOwner returns a healthcare account by owner address.
|
||||||
|
GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.HealthcareAccount, error)
|
||||||
|
// HasValidAuthorization checks if provider has valid authorization for patient.
|
||||||
|
HasValidAuthorization(d *dao.Simple, patient util.Uint160, provider util.Uint160, blockHeight uint32) bool
|
||||||
|
// Address returns the contract's script hash.
|
||||||
|
Address() util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISese is an interface required from native Sese contract for
|
||||||
|
// interaction with Blockchain and other native contracts.
|
||||||
|
// Sese provides life planning infrastructure.
|
||||||
|
ISese interface {
|
||||||
|
interop.Contract
|
||||||
|
// GetAccountByOwner returns a life plan account by owner address.
|
||||||
|
GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.LifePlanAccount, error)
|
||||||
|
// HasActiveCareer checks if owner has an active career.
|
||||||
|
HasActiveCareer(d *dao.Simple, owner util.Uint160) bool
|
||||||
|
// Address returns the contract's script hash.
|
||||||
|
Address() util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITribute is an interface required from native Tribute contract for
|
||||||
|
// interaction with Blockchain and other native contracts.
|
||||||
|
// Tribute provides anti-hoarding economics with velocity tracking.
|
||||||
|
ITribute interface {
|
||||||
|
interop.Contract
|
||||||
|
// GetAccountByOwner returns a velocity account by owner address.
|
||||||
|
GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.VelocityAccount, error)
|
||||||
|
// GetVelocity returns the current velocity score for an owner.
|
||||||
|
GetVelocity(d *dao.Simple, owner util.Uint160) uint64
|
||||||
|
// IsHoarding returns true if owner is hoarding resources.
|
||||||
|
IsHoarding(d *dao.Simple, owner util.Uint160) bool
|
||||||
|
// Address returns the contract's script hash.
|
||||||
|
Address() util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
// IOpus provides AI workforce integration and management.
|
||||||
|
IOpus interface {
|
||||||
|
interop.Contract
|
||||||
|
// GetWorkerByID returns an AI worker by ID.
|
||||||
|
GetWorkerByID(d *dao.Simple, workerID uint64) (*state.AIWorker, error)
|
||||||
|
// GetOperatorByOwner returns an operator profile by owner address.
|
||||||
|
GetOperatorByOwner(d *dao.Simple, owner util.Uint160) (*state.OperatorProfile, error)
|
||||||
|
// IsWorkerActive returns true if the AI worker is active.
|
||||||
|
IsWorkerActive(d *dao.Simple, workerID uint64) bool
|
||||||
|
// Address returns the contract's script hash.
|
||||||
|
Address() util.Uint160
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Contracts is a convenient wrapper around an arbitrary set of native contracts
|
// Contracts is a convenient wrapper around an arbitrary set of native contracts
|
||||||
|
|
@ -401,6 +455,30 @@ func (cs *Contracts) Scire() IScire {
|
||||||
return cs.ByName(nativenames.Scire).(IScire)
|
return cs.ByName(nativenames.Scire).(IScire)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Salus returns native ISalus contract implementation. It panics if
|
||||||
|
// there's no contract with proper name in cs.
|
||||||
|
func (cs *Contracts) Salus() ISalus {
|
||||||
|
return cs.ByName(nativenames.Salus).(ISalus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sese returns native ISese contract implementation. It panics if
|
||||||
|
// there's no contract with proper name in cs.
|
||||||
|
func (cs *Contracts) Sese() ISese {
|
||||||
|
return cs.ByName(nativenames.Sese).(ISese)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tribute returns native ITribute contract implementation. It panics if
|
||||||
|
// there's no contract with proper name in cs.
|
||||||
|
func (cs *Contracts) Tribute() ITribute {
|
||||||
|
return cs.ByName(nativenames.Tribute).(ITribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opus returns native IOpus contract implementation. It panics if
|
||||||
|
// there's no contract with proper name in cs.
|
||||||
|
func (cs *Contracts) Opus() IOpus {
|
||||||
|
return cs.ByName(nativenames.Opus).(IOpus)
|
||||||
|
}
|
||||||
|
|
||||||
// NewDefaultContracts returns a new set of default native contracts.
|
// NewDefaultContracts returns a new set of default native contracts.
|
||||||
func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
|
func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
|
||||||
mgmt := NewManagement()
|
mgmt := NewManagement()
|
||||||
|
|
@ -501,6 +579,37 @@ func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
|
||||||
scire.RoleRegistry = roleRegistry
|
scire.RoleRegistry = roleRegistry
|
||||||
scire.Lex = lex
|
scire.Lex = lex
|
||||||
|
|
||||||
|
// Create Salus (Universal Healthcare) contract
|
||||||
|
salus := newSalus()
|
||||||
|
salus.NEO = neo
|
||||||
|
salus.Vita = vita
|
||||||
|
salus.RoleRegistry = roleRegistry
|
||||||
|
salus.Lex = lex
|
||||||
|
|
||||||
|
// Create Sese (Life Planning) contract
|
||||||
|
sese := newSese()
|
||||||
|
sese.NEO = neo
|
||||||
|
sese.Vita = vita
|
||||||
|
sese.RoleRegistry = roleRegistry
|
||||||
|
sese.Lex = lex
|
||||||
|
|
||||||
|
// Create Tribute (Anti-Hoarding Economics) contract
|
||||||
|
tribute := newTribute()
|
||||||
|
tribute.NEO = neo
|
||||||
|
tribute.Vita = vita
|
||||||
|
tribute.VTS = vts
|
||||||
|
tribute.RoleRegistry = roleRegistry
|
||||||
|
tribute.Lex = lex
|
||||||
|
|
||||||
|
// Create Opus (AI Workforce Integration) contract
|
||||||
|
opus := newOpus()
|
||||||
|
opus.NEO = neo
|
||||||
|
opus.Vita = vita
|
||||||
|
opus.VTS = vts
|
||||||
|
opus.RoleRegistry = roleRegistry
|
||||||
|
opus.Lex = lex
|
||||||
|
opus.Treasury = treasury
|
||||||
|
|
||||||
return []interop.Contract{
|
return []interop.Contract{
|
||||||
mgmt,
|
mgmt,
|
||||||
s,
|
s,
|
||||||
|
|
@ -520,5 +629,9 @@ func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
|
||||||
lex,
|
lex,
|
||||||
eligere,
|
eligere,
|
||||||
scire,
|
scire,
|
||||||
|
salus,
|
||||||
|
sese,
|
||||||
|
tribute,
|
||||||
|
opus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,254 @@
|
||||||
|
package native_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/neotest"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newOpusClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
|
return newNativeClient(t, nativenames.Opus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetConfig tests the getConfig method.
|
||||||
|
func TestOpus_GetConfig(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Get default config
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
arr, ok := stack[0].Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok, "expected array result")
|
||||||
|
require.GreaterOrEqual(t, len(arr), 7) // OpusConfig has 7 fields
|
||||||
|
}, "getConfig")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetTotalOperators tests the getTotalOperators method.
|
||||||
|
func TestOpus_GetTotalOperators(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Initially should be 0
|
||||||
|
c.Invoke(t, 0, "getTotalOperators")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetTotalWorkers tests the getTotalWorkers method.
|
||||||
|
func TestOpus_GetTotalWorkers(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Initially should be 0
|
||||||
|
c.Invoke(t, 0, "getTotalWorkers")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetTotalTasks tests the getTotalTasks method.
|
||||||
|
func TestOpus_GetTotalTasks(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Initially should be 0
|
||||||
|
c.Invoke(t, 0, "getTotalTasks")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetTotalCapabilities tests the getTotalCapabilities method.
|
||||||
|
func TestOpus_GetTotalCapabilities(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Initially should be 0
|
||||||
|
c.Invoke(t, 0, "getTotalCapabilities")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetOperator_NonExistent tests getting a non-existent operator.
|
||||||
|
func TestOpus_GetOperator_NonExistent(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
|
||||||
|
// Non-existent operator should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent operator")
|
||||||
|
}, "getOperator", acc.ScriptHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetWorker_NonExistent tests getting a non-existent worker.
|
||||||
|
func TestOpus_GetWorker_NonExistent(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Non-existent worker should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent worker")
|
||||||
|
}, "getWorker", int64(999))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetTask_NonExistent tests getting a non-existent task.
|
||||||
|
func TestOpus_GetTask_NonExistent(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Non-existent task should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent task")
|
||||||
|
}, "getTask", int64(999))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_GetCapability_NonExistent tests getting a non-existent capability.
|
||||||
|
func TestOpus_GetCapability_NonExistent(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
|
||||||
|
// Non-existent capability should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent capability")
|
||||||
|
}, "getCapability", int64(999))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: isWorkerActive and isOperatorVerified methods are not exposed as separate methods.
|
||||||
|
// Worker/operator status can be checked via getWorker/getOperator.
|
||||||
|
|
||||||
|
// TestOpus_GetWorkerCountByOperator_NonExistent tests getting worker count for non-existent operator.
|
||||||
|
func TestOpus_GetWorkerCountByOperator_NonExistent(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
|
||||||
|
// Non-existent operator should return 0
|
||||||
|
c.Invoke(t, 0, "getWorkerCountByOperator", acc.ScriptHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_RegisterOperator_NoVita tests that registering operator without Vita fails.
|
||||||
|
func TestOpus_RegisterOperator_NoVita(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
// Should fail - no Vita registered
|
||||||
|
invoker.InvokeFail(t, "operator must have an active Vita", "registerOperator",
|
||||||
|
acc.ScriptHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_VerifyOperator_NotSupervisor tests that non-supervisor cannot verify operator.
|
||||||
|
func TestOpus_VerifyOperator_NotSupervisor(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
// Should fail - not supervisor
|
||||||
|
invoker.InvokeFail(t, "caller is not an authorized supervisor", "verifyOperator", acc.ScriptHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: suspendOperator method is not currently implemented. Operators can be managed
|
||||||
|
// through their linked Vita status.
|
||||||
|
|
||||||
|
// TestOpus_RegisterWorker_NoOperator tests that registering worker without operator fails.
|
||||||
|
func TestOpus_RegisterWorker_NoOperator(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
modelHash := hash.Sha256([]byte("model")).BytesBE()
|
||||||
|
|
||||||
|
// Should fail - no operator registered (registerWorker takes: name, description, workerType, modelHash)
|
||||||
|
invoker.InvokeFail(t, "operator profile not found", "registerWorker",
|
||||||
|
"TestWorker", "Test AI Worker", int64(0), modelHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_SuspendWorker_NotSupervisor tests that non-supervisor cannot suspend worker.
|
||||||
|
func TestOpus_SuspendWorker_NotSupervisor(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
// Should fail - not supervisor (checked before worker existence)
|
||||||
|
invoker.InvokeFail(t, "caller is not an authorized supervisor", "suspendWorker", int64(999), "suspension reason")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_AssignTask_WorkerNotFound tests that assigning task to non-existent worker fails.
|
||||||
|
func TestOpus_AssignTask_WorkerNotFound(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
// Should fail - worker not found
|
||||||
|
invoker.InvokeFail(t, "worker not found", "assignTask",
|
||||||
|
int64(999), "task-123", "Test Task", int64(1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_CompleteTask_TaskNotFound tests that completing non-existent task fails.
|
||||||
|
func TestOpus_CompleteTask_TaskNotFound(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
resultHash := hash.Sha256([]byte("result")).BytesBE()
|
||||||
|
|
||||||
|
// Should fail - task not found
|
||||||
|
invoker.InvokeFail(t, "task not found", "completeTask", int64(999), resultHash, int64(500))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_AddCapability_WorkerNotFound tests that adding capability to non-existent worker fails.
|
||||||
|
func TestOpus_AddCapability_WorkerNotFound(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
invoker := c.WithSigners(acc)
|
||||||
|
|
||||||
|
// Should fail - worker not found (before checking supervisor)
|
||||||
|
invoker.InvokeFail(t, "AI worker not found", "addCapability",
|
||||||
|
int64(999), "language_model", int64(3), "Test certification")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOpus_RegisterOperatorWithVita tests operator registration with a valid Vita.
|
||||||
|
func TestOpus_RegisterOperatorWithVita(t *testing.T) {
|
||||||
|
c := newOpusClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
// Register Vita first
|
||||||
|
vitaHash := e.NativeHash(t, nativenames.Vita)
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
vitaInvoker := e.NewInvoker(vitaHash, acc)
|
||||||
|
|
||||||
|
owner := acc.ScriptHash()
|
||||||
|
personHash := hash.Sha256(owner.BytesBE()).BytesBE()
|
||||||
|
isEntity := false
|
||||||
|
recoveryHash := hash.Sha256([]byte("recovery")).BytesBE()
|
||||||
|
|
||||||
|
// Register Vita token
|
||||||
|
vitaInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
_, ok := stack[0].Value().([]byte)
|
||||||
|
require.True(t, ok, "expected ByteArray result")
|
||||||
|
}, "register", owner.BytesBE(), personHash, isEntity, recoveryHash)
|
||||||
|
|
||||||
|
// Now register operator (only takes owner as parameter)
|
||||||
|
opusInvoker := c.WithSigners(acc)
|
||||||
|
opusInvoker.Invoke(t, true, "registerOperator", owner.BytesBE())
|
||||||
|
|
||||||
|
// Verify operator exists
|
||||||
|
opusInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
arr, ok := stack[0].Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok, "expected array result for existing operator")
|
||||||
|
require.GreaterOrEqual(t, len(arr), 9) // OperatorProfile has 9 fields
|
||||||
|
}, "getOperator", owner.BytesBE())
|
||||||
|
|
||||||
|
// Verify total operators increased
|
||||||
|
c.Invoke(t, 1, "getTotalOperators")
|
||||||
|
}
|
||||||
|
|
@ -45,4 +45,12 @@ var (
|
||||||
Eligere = util.Uint160{0x1, 0x94, 0x73, 0x8e, 0xab, 0x6b, 0xc5, 0xa0, 0xff, 0xab, 0xe0, 0x2a, 0xce, 0xea, 0xd7, 0xb3, 0xa8, 0xe5, 0x7, 0x40}
|
Eligere = util.Uint160{0x1, 0x94, 0x73, 0x8e, 0xab, 0x6b, 0xc5, 0xa0, 0xff, 0xab, 0xe0, 0x2a, 0xce, 0xea, 0xd7, 0xb3, 0xa8, 0xe5, 0x7, 0x40}
|
||||||
// Scire is a hash of native Scire contract.
|
// Scire is a hash of native Scire contract.
|
||||||
Scire = util.Uint160{0x9f, 0x7, 0x16, 0xd4, 0xd6, 0xb8, 0xae, 0x2d, 0x58, 0x42, 0x94, 0xf8, 0x92, 0x62, 0x5d, 0x8e, 0x63, 0xa0, 0xde, 0x3}
|
Scire = util.Uint160{0x9f, 0x7, 0x16, 0xd4, 0xd6, 0xb8, 0xae, 0x2d, 0x58, 0x42, 0x94, 0xf8, 0x92, 0x62, 0x5d, 0x8e, 0x63, 0xa0, 0xde, 0x3}
|
||||||
|
// Salus is a hash of native Salus contract.
|
||||||
|
Salus = util.Uint160{0x91, 0x6e, 0x6a, 0xf6, 0x70, 0x7b, 0x91, 0x75, 0x9f, 0xbd, 0xff, 0xdb, 0x25, 0xe6, 0xee, 0xf9, 0x4f, 0x20, 0x88, 0xc8}
|
||||||
|
// Sese is a hash of native Sese contract.
|
||||||
|
Sese = util.Uint160{0x16, 0x99, 0x28, 0x39, 0x2, 0xfe, 0x12, 0x60, 0xf3, 0xe, 0x4b, 0x2b, 0xf7, 0x44, 0x19, 0xc4, 0x7d, 0x24, 0xfd, 0x48}
|
||||||
|
// Tribute is a hash of native Tribute contract.
|
||||||
|
Tribute = util.Uint160{0x35, 0x9b, 0xbf, 0x51, 0xb5, 0xe3, 0x73, 0x6d, 0x52, 0xad, 0x80, 0xb0, 0x28, 0x4f, 0x22, 0x8f, 0x9, 0xaf, 0x49, 0x1e}
|
||||||
|
// Opus is a hash of native Opus contract.
|
||||||
|
Opus = util.Uint160{0xf5, 0x22, 0x73, 0x17, 0x98, 0xab, 0xfc, 0x4d, 0x7c, 0x3a, 0x51, 0x8, 0x47, 0xc7, 0xe5, 0xe4, 0x6, 0x96, 0xb6, 0xfd}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -43,4 +43,12 @@ const (
|
||||||
Eligere int32 = -17
|
Eligere int32 = -17
|
||||||
// Scire is an ID of native Scire contract.
|
// Scire is an ID of native Scire contract.
|
||||||
Scire int32 = -18
|
Scire int32 = -18
|
||||||
|
// Salus is an ID of native Salus contract.
|
||||||
|
Salus int32 = -19
|
||||||
|
// Sese is an ID of native Sese contract.
|
||||||
|
Sese int32 = -20
|
||||||
|
// Tribute is an ID of native Tribute contract.
|
||||||
|
Tribute int32 = -21
|
||||||
|
// Opus is an ID of native Opus contract.
|
||||||
|
Opus int32 = -22
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ const (
|
||||||
Lex = "Lex"
|
Lex = "Lex"
|
||||||
Eligere = "Eligere"
|
Eligere = "Eligere"
|
||||||
Scire = "Scire"
|
Scire = "Scire"
|
||||||
|
Salus = "Salus"
|
||||||
|
Sese = "Sese"
|
||||||
|
Tribute = "Tribute"
|
||||||
|
Opus = "Opus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// All contains the list of all native contract names ordered by the contract ID.
|
// All contains the list of all native contract names ordered by the contract ID.
|
||||||
|
|
@ -42,6 +46,10 @@ var All = []string{
|
||||||
Lex,
|
Lex,
|
||||||
Eligere,
|
Eligere,
|
||||||
Scire,
|
Scire,
|
||||||
|
Salus,
|
||||||
|
Sese,
|
||||||
|
Tribute,
|
||||||
|
Opus,
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid checks if the name is a valid native contract's name.
|
// IsValid checks if the name is a valid native contract's name.
|
||||||
|
|
@ -63,5 +71,9 @@ func IsValid(name string) bool {
|
||||||
name == Federation ||
|
name == Federation ||
|
||||||
name == Lex ||
|
name == Lex ||
|
||||||
name == Eligere ||
|
name == Eligere ||
|
||||||
name == Scire
|
name == Scire ||
|
||||||
|
name == Salus ||
|
||||||
|
name == Sese ||
|
||||||
|
name == Tribute ||
|
||||||
|
name == Opus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,623 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/util"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AIWorkerStatus represents the status of an AI worker.
|
||||||
|
type AIWorkerStatus uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AIWorkerActive indicates an active AI worker.
|
||||||
|
AIWorkerActive AIWorkerStatus = 0
|
||||||
|
// AIWorkerSuspended indicates a suspended AI worker.
|
||||||
|
AIWorkerSuspended AIWorkerStatus = 1
|
||||||
|
// AIWorkerDecommissioned indicates a decommissioned AI worker.
|
||||||
|
AIWorkerDecommissioned AIWorkerStatus = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaskStatus represents the status of an AI task.
|
||||||
|
type TaskStatus uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TaskAssigned indicates a task has been assigned.
|
||||||
|
TaskAssigned TaskStatus = 0
|
||||||
|
// TaskInProgress indicates a task is being worked on.
|
||||||
|
TaskInProgress TaskStatus = 1
|
||||||
|
// TaskCompleted indicates a task was completed successfully.
|
||||||
|
TaskCompleted TaskStatus = 2
|
||||||
|
// TaskFailed indicates a task failed.
|
||||||
|
TaskFailed TaskStatus = 3
|
||||||
|
// TaskCancelled indicates a task was cancelled.
|
||||||
|
TaskCancelled TaskStatus = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// CapabilityLevel represents the proficiency level of an AI capability.
|
||||||
|
type CapabilityLevel uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CapabilityBasic represents basic proficiency.
|
||||||
|
CapabilityBasic CapabilityLevel = 0
|
||||||
|
// CapabilityStandard represents standard proficiency.
|
||||||
|
CapabilityStandard CapabilityLevel = 1
|
||||||
|
// CapabilityAdvanced represents advanced proficiency.
|
||||||
|
CapabilityAdvanced CapabilityLevel = 2
|
||||||
|
// CapabilityExpert represents expert proficiency.
|
||||||
|
CapabilityExpert CapabilityLevel = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// WorkerType represents the type of AI worker.
|
||||||
|
type WorkerType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WorkerTypeGeneral represents a general-purpose AI.
|
||||||
|
WorkerTypeGeneral WorkerType = 0
|
||||||
|
// WorkerTypeSpecialized represents a domain-specialized AI.
|
||||||
|
WorkerTypeSpecialized WorkerType = 1
|
||||||
|
// WorkerTypeAutonomous represents a fully autonomous AI agent.
|
||||||
|
WorkerTypeAutonomous WorkerType = 2
|
||||||
|
// WorkerTypeAssistive represents an AI that assists humans.
|
||||||
|
WorkerTypeAssistive WorkerType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// AIWorker represents a registered AI worker in the Opus system.
|
||||||
|
type AIWorker struct {
|
||||||
|
ID uint64 // Unique AI worker ID
|
||||||
|
Name string // Human-readable name
|
||||||
|
Description string // Description of capabilities
|
||||||
|
OperatorVitaID uint64 // Vita ID of the human/org operator
|
||||||
|
Operator util.Uint160 // Operator's address
|
||||||
|
WorkerType WorkerType // Type of AI worker
|
||||||
|
ModelHash util.Uint256 // Hash of the AI model/system
|
||||||
|
TributeRate uint32 // Tribute rate in basis points (e.g., 5000 = 50%)
|
||||||
|
TotalTasksAssigned uint64 // Total tasks assigned
|
||||||
|
TotalTasksCompleted uint64 // Total tasks completed
|
||||||
|
TotalValueGenerated uint64 // Total value generated (in VTS)
|
||||||
|
TotalTributePaid uint64 // Total tribute paid to society
|
||||||
|
Status AIWorkerStatus // Worker status
|
||||||
|
RegisteredAt uint32 // Block height when registered
|
||||||
|
UpdatedAt uint32 // Block height of last update
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStackItem implements stackitem.Convertible interface.
|
||||||
|
func (w *AIWorker) ToStackItem() (stackitem.Item, error) {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.ID))),
|
||||||
|
stackitem.NewByteArray([]byte(w.Name)),
|
||||||
|
stackitem.NewByteArray([]byte(w.Description)),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.OperatorVitaID))),
|
||||||
|
stackitem.NewByteArray(w.Operator.BytesBE()),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.WorkerType))),
|
||||||
|
stackitem.NewByteArray(w.ModelHash.BytesBE()),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.TributeRate))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.TotalTasksAssigned))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.TotalTasksCompleted))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.TotalValueGenerated))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.TotalTributePaid))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.Status))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.RegisteredAt))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(w.UpdatedAt))),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem implements stackitem.Convertible interface.
|
||||||
|
func (w *AIWorker) FromStackItem(item stackitem.Item) error {
|
||||||
|
items, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not a struct")
|
||||||
|
}
|
||||||
|
if len(items) != 15 {
|
||||||
|
return fmt.Errorf("wrong number of elements: expected 15, got %d", len(items))
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := items[0].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid id: %w", err)
|
||||||
|
}
|
||||||
|
w.ID = id.Uint64()
|
||||||
|
|
||||||
|
name, err := items[1].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid name: %w", err)
|
||||||
|
}
|
||||||
|
w.Name = string(name)
|
||||||
|
|
||||||
|
description, err := items[2].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid description: %w", err)
|
||||||
|
}
|
||||||
|
w.Description = string(description)
|
||||||
|
|
||||||
|
operatorVitaID, err := items[3].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid operatorVitaID: %w", err)
|
||||||
|
}
|
||||||
|
w.OperatorVitaID = operatorVitaID.Uint64()
|
||||||
|
|
||||||
|
operator, err := items[4].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid operator: %w", err)
|
||||||
|
}
|
||||||
|
w.Operator, err = util.Uint160DecodeBytesBE(operator)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid operator address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
workerType, err := items[5].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid workerType: %w", err)
|
||||||
|
}
|
||||||
|
w.WorkerType = WorkerType(workerType.Uint64())
|
||||||
|
|
||||||
|
modelHash, err := items[6].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid modelHash: %w", err)
|
||||||
|
}
|
||||||
|
w.ModelHash, err = util.Uint256DecodeBytesBE(modelHash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid modelHash value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tributeRate, err := items[7].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid tributeRate: %w", err)
|
||||||
|
}
|
||||||
|
w.TributeRate = uint32(tributeRate.Uint64())
|
||||||
|
|
||||||
|
tasksAssigned, err := items[8].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalTasksAssigned: %w", err)
|
||||||
|
}
|
||||||
|
w.TotalTasksAssigned = tasksAssigned.Uint64()
|
||||||
|
|
||||||
|
tasksCompleted, err := items[9].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalTasksCompleted: %w", err)
|
||||||
|
}
|
||||||
|
w.TotalTasksCompleted = tasksCompleted.Uint64()
|
||||||
|
|
||||||
|
valueGenerated, err := items[10].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalValueGenerated: %w", err)
|
||||||
|
}
|
||||||
|
w.TotalValueGenerated = valueGenerated.Uint64()
|
||||||
|
|
||||||
|
tributePaid, err := items[11].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalTributePaid: %w", err)
|
||||||
|
}
|
||||||
|
w.TotalTributePaid = tributePaid.Uint64()
|
||||||
|
|
||||||
|
status, err := items[12].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid status: %w", err)
|
||||||
|
}
|
||||||
|
w.Status = AIWorkerStatus(status.Uint64())
|
||||||
|
|
||||||
|
registeredAt, err := items[13].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid registeredAt: %w", err)
|
||||||
|
}
|
||||||
|
w.RegisteredAt = uint32(registeredAt.Uint64())
|
||||||
|
|
||||||
|
updatedAt, err := items[14].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid updatedAt: %w", err)
|
||||||
|
}
|
||||||
|
w.UpdatedAt = uint32(updatedAt.Uint64())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AITask represents a task assigned to an AI worker.
|
||||||
|
type AITask struct {
|
||||||
|
ID uint64 // Unique task ID
|
||||||
|
WorkerID uint64 // AI worker ID
|
||||||
|
RequesterVitaID uint64 // Vita ID of the requester (0 = system task)
|
||||||
|
Requester util.Uint160 // Requester's address
|
||||||
|
TaskType string // Type/category of task
|
||||||
|
Description string // Task description
|
||||||
|
ValueOffered uint64 // VTS value offered for task
|
||||||
|
ValueCompleted uint64 // Actual value upon completion
|
||||||
|
TributePaid uint64 // Tribute paid from this task
|
||||||
|
ResultHash util.Uint256 // Hash of task result/output
|
||||||
|
Status TaskStatus // Task status
|
||||||
|
AssignedAt uint32 // Block height when assigned
|
||||||
|
CompletedAt uint32 // Block height when completed (0 = incomplete)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStackItem implements stackitem.Convertible interface.
|
||||||
|
func (t *AITask) ToStackItem() (stackitem.Item, error) {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.ID))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.WorkerID))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.RequesterVitaID))),
|
||||||
|
stackitem.NewByteArray(t.Requester.BytesBE()),
|
||||||
|
stackitem.NewByteArray([]byte(t.TaskType)),
|
||||||
|
stackitem.NewByteArray([]byte(t.Description)),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.ValueOffered))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.ValueCompleted))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.TributePaid))),
|
||||||
|
stackitem.NewByteArray(t.ResultHash.BytesBE()),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.Status))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.AssignedAt))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(t.CompletedAt))),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem implements stackitem.Convertible interface.
|
||||||
|
func (t *AITask) FromStackItem(item stackitem.Item) error {
|
||||||
|
items, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not a struct")
|
||||||
|
}
|
||||||
|
if len(items) != 13 {
|
||||||
|
return fmt.Errorf("wrong number of elements: expected 13, got %d", len(items))
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := items[0].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid id: %w", err)
|
||||||
|
}
|
||||||
|
t.ID = id.Uint64()
|
||||||
|
|
||||||
|
workerID, err := items[1].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid workerID: %w", err)
|
||||||
|
}
|
||||||
|
t.WorkerID = workerID.Uint64()
|
||||||
|
|
||||||
|
requesterVitaID, err := items[2].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid requesterVitaID: %w", err)
|
||||||
|
}
|
||||||
|
t.RequesterVitaID = requesterVitaID.Uint64()
|
||||||
|
|
||||||
|
requester, err := items[3].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid requester: %w", err)
|
||||||
|
}
|
||||||
|
t.Requester, err = util.Uint160DecodeBytesBE(requester)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid requester address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
taskType, err := items[4].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid taskType: %w", err)
|
||||||
|
}
|
||||||
|
t.TaskType = string(taskType)
|
||||||
|
|
||||||
|
description, err := items[5].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid description: %w", err)
|
||||||
|
}
|
||||||
|
t.Description = string(description)
|
||||||
|
|
||||||
|
valueOffered, err := items[6].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid valueOffered: %w", err)
|
||||||
|
}
|
||||||
|
t.ValueOffered = valueOffered.Uint64()
|
||||||
|
|
||||||
|
valueCompleted, err := items[7].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid valueCompleted: %w", err)
|
||||||
|
}
|
||||||
|
t.ValueCompleted = valueCompleted.Uint64()
|
||||||
|
|
||||||
|
tributePaid, err := items[8].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid tributePaid: %w", err)
|
||||||
|
}
|
||||||
|
t.TributePaid = tributePaid.Uint64()
|
||||||
|
|
||||||
|
resultHash, err := items[9].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid resultHash: %w", err)
|
||||||
|
}
|
||||||
|
t.ResultHash, err = util.Uint256DecodeBytesBE(resultHash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid resultHash value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := items[10].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid status: %w", err)
|
||||||
|
}
|
||||||
|
t.Status = TaskStatus(status.Uint64())
|
||||||
|
|
||||||
|
assignedAt, err := items[11].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid assignedAt: %w", err)
|
||||||
|
}
|
||||||
|
t.AssignedAt = uint32(assignedAt.Uint64())
|
||||||
|
|
||||||
|
completedAt, err := items[12].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid completedAt: %w", err)
|
||||||
|
}
|
||||||
|
t.CompletedAt = uint32(completedAt.Uint64())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AICapability represents a certified capability of an AI worker.
|
||||||
|
type AICapability struct {
|
||||||
|
ID uint64 // Unique capability ID
|
||||||
|
WorkerID uint64 // AI worker ID
|
||||||
|
Name string // Capability name
|
||||||
|
Category string // Capability category
|
||||||
|
Level CapabilityLevel // Proficiency level
|
||||||
|
CertifiedBy util.Uint160 // Certifier's address
|
||||||
|
CertifiedAt uint32 // Block height when certified
|
||||||
|
ExpiresAt uint32 // Expiration block (0 = never)
|
||||||
|
IsVerified bool // Whether capability is verified
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStackItem implements stackitem.Convertible interface.
|
||||||
|
func (c *AICapability) ToStackItem() (stackitem.Item, error) {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.ID))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.WorkerID))),
|
||||||
|
stackitem.NewByteArray([]byte(c.Name)),
|
||||||
|
stackitem.NewByteArray([]byte(c.Category)),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.Level))),
|
||||||
|
stackitem.NewByteArray(c.CertifiedBy.BytesBE()),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.CertifiedAt))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.ExpiresAt))),
|
||||||
|
stackitem.NewBool(c.IsVerified),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem implements stackitem.Convertible interface.
|
||||||
|
func (c *AICapability) FromStackItem(item stackitem.Item) error {
|
||||||
|
items, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not a struct")
|
||||||
|
}
|
||||||
|
if len(items) != 9 {
|
||||||
|
return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items))
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := items[0].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid id: %w", err)
|
||||||
|
}
|
||||||
|
c.ID = id.Uint64()
|
||||||
|
|
||||||
|
workerID, err := items[1].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid workerID: %w", err)
|
||||||
|
}
|
||||||
|
c.WorkerID = workerID.Uint64()
|
||||||
|
|
||||||
|
name, err := items[2].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid name: %w", err)
|
||||||
|
}
|
||||||
|
c.Name = string(name)
|
||||||
|
|
||||||
|
category, err := items[3].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid category: %w", err)
|
||||||
|
}
|
||||||
|
c.Category = string(category)
|
||||||
|
|
||||||
|
level, err := items[4].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid level: %w", err)
|
||||||
|
}
|
||||||
|
c.Level = CapabilityLevel(level.Uint64())
|
||||||
|
|
||||||
|
certifiedBy, err := items[5].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid certifiedBy: %w", err)
|
||||||
|
}
|
||||||
|
c.CertifiedBy, err = util.Uint160DecodeBytesBE(certifiedBy)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid certifiedBy address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certifiedAt, err := items[6].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid certifiedAt: %w", err)
|
||||||
|
}
|
||||||
|
c.CertifiedAt = uint32(certifiedAt.Uint64())
|
||||||
|
|
||||||
|
expiresAt, err := items[7].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid expiresAt: %w", err)
|
||||||
|
}
|
||||||
|
c.ExpiresAt = uint32(expiresAt.Uint64())
|
||||||
|
|
||||||
|
isVerified, err := items[8].TryBool()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid isVerified: %w", err)
|
||||||
|
}
|
||||||
|
c.IsVerified = isVerified
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorProfile represents an AI operator's profile (human/org that deploys AI).
|
||||||
|
type OperatorProfile struct {
|
||||||
|
VitaID uint64 // Operator's Vita ID
|
||||||
|
Owner util.Uint160 // Operator's address
|
||||||
|
TotalWorkersOwned uint64 // Number of AI workers owned
|
||||||
|
TotalTributePaid uint64 // Total tribute paid by all workers
|
||||||
|
TotalValueGenerated uint64 // Total value generated by all workers
|
||||||
|
ReputationScore uint32 // Reputation score (0-10000 basis points)
|
||||||
|
IsVerified bool // Whether operator is verified
|
||||||
|
CreatedAt uint32 // Block height when created
|
||||||
|
UpdatedAt uint32 // Block height of last update
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStackItem implements stackitem.Convertible interface.
|
||||||
|
func (p *OperatorProfile) ToStackItem() (stackitem.Item, error) {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.VitaID))),
|
||||||
|
stackitem.NewByteArray(p.Owner.BytesBE()),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.TotalWorkersOwned))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.TotalTributePaid))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.TotalValueGenerated))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.ReputationScore))),
|
||||||
|
stackitem.NewBool(p.IsVerified),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.CreatedAt))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(p.UpdatedAt))),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem implements stackitem.Convertible interface.
|
||||||
|
func (p *OperatorProfile) FromStackItem(item stackitem.Item) error {
|
||||||
|
items, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not a struct")
|
||||||
|
}
|
||||||
|
if len(items) != 9 {
|
||||||
|
return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items))
|
||||||
|
}
|
||||||
|
|
||||||
|
vitaID, err := items[0].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid vitaID: %w", err)
|
||||||
|
}
|
||||||
|
p.VitaID = vitaID.Uint64()
|
||||||
|
|
||||||
|
owner, err := items[1].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid owner: %w", err)
|
||||||
|
}
|
||||||
|
p.Owner, err = util.Uint160DecodeBytesBE(owner)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid owner address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalWorkers, err := items[2].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalWorkersOwned: %w", err)
|
||||||
|
}
|
||||||
|
p.TotalWorkersOwned = totalWorkers.Uint64()
|
||||||
|
|
||||||
|
tributePaid, err := items[3].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalTributePaid: %w", err)
|
||||||
|
}
|
||||||
|
p.TotalTributePaid = tributePaid.Uint64()
|
||||||
|
|
||||||
|
valueGenerated, err := items[4].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid totalValueGenerated: %w", err)
|
||||||
|
}
|
||||||
|
p.TotalValueGenerated = valueGenerated.Uint64()
|
||||||
|
|
||||||
|
reputationScore, err := items[5].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid reputationScore: %w", err)
|
||||||
|
}
|
||||||
|
p.ReputationScore = uint32(reputationScore.Uint64())
|
||||||
|
|
||||||
|
isVerified, err := items[6].TryBool()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid isVerified: %w", err)
|
||||||
|
}
|
||||||
|
p.IsVerified = isVerified
|
||||||
|
|
||||||
|
createdAt, err := items[7].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid createdAt: %w", err)
|
||||||
|
}
|
||||||
|
p.CreatedAt = uint32(createdAt.Uint64())
|
||||||
|
|
||||||
|
updatedAt, err := items[8].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid updatedAt: %w", err)
|
||||||
|
}
|
||||||
|
p.UpdatedAt = uint32(updatedAt.Uint64())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpusConfig represents configurable parameters for the Opus contract.
|
||||||
|
type OpusConfig struct {
|
||||||
|
MinTributeRate uint32 // Minimum tribute rate (basis points)
|
||||||
|
MaxTributeRate uint32 // Maximum tribute rate (basis points)
|
||||||
|
DefaultTributeRate uint32 // Default tribute rate for new workers
|
||||||
|
RegistrationFee uint64 // VTS fee to register an AI worker
|
||||||
|
TaskCompletionMinimum uint64 // Minimum value for task completion
|
||||||
|
VerificationRequired bool // Whether operator verification is required
|
||||||
|
MaxWorkersPerOperator uint64 // Maximum AI workers per operator (0 = unlimited)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStackItem implements stackitem.Convertible interface.
|
||||||
|
func (c *OpusConfig) ToStackItem() (stackitem.Item, error) {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.MinTributeRate))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.MaxTributeRate))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.DefaultTributeRate))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.RegistrationFee))),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.TaskCompletionMinimum))),
|
||||||
|
stackitem.NewBool(c.VerificationRequired),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(c.MaxWorkersPerOperator))),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem implements stackitem.Convertible interface.
|
||||||
|
func (c *OpusConfig) FromStackItem(item stackitem.Item) error {
|
||||||
|
items, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not a struct")
|
||||||
|
}
|
||||||
|
if len(items) != 7 {
|
||||||
|
return fmt.Errorf("wrong number of elements: expected 7, got %d", len(items))
|
||||||
|
}
|
||||||
|
|
||||||
|
minRate, err := items[0].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid minTributeRate: %w", err)
|
||||||
|
}
|
||||||
|
c.MinTributeRate = uint32(minRate.Uint64())
|
||||||
|
|
||||||
|
maxRate, err := items[1].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid maxTributeRate: %w", err)
|
||||||
|
}
|
||||||
|
c.MaxTributeRate = uint32(maxRate.Uint64())
|
||||||
|
|
||||||
|
defaultRate, err := items[2].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid defaultTributeRate: %w", err)
|
||||||
|
}
|
||||||
|
c.DefaultTributeRate = uint32(defaultRate.Uint64())
|
||||||
|
|
||||||
|
regFee, err := items[3].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid registrationFee: %w", err)
|
||||||
|
}
|
||||||
|
c.RegistrationFee = regFee.Uint64()
|
||||||
|
|
||||||
|
taskMin, err := items[4].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid taskCompletionMinimum: %w", err)
|
||||||
|
}
|
||||||
|
c.TaskCompletionMinimum = taskMin.Uint64()
|
||||||
|
|
||||||
|
verificationReq, err := items[5].TryBool()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid verificationRequired: %w", err)
|
||||||
|
}
|
||||||
|
c.VerificationRequired = verificationReq
|
||||||
|
|
||||||
|
maxWorkers, err := items[6].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid maxWorkersPerOperator: %w", err)
|
||||||
|
}
|
||||||
|
c.MaxWorkersPerOperator = maxWorkers.Uint64()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue