tutus-chain/pkg/tutustest/government.go

238 lines
8.2 KiB
Go
Executable File

package tutustest
import (
"math/big"
"testing"
"time"
"github.com/stretchr/testify/require"
"git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
"git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction"
"git.marketally.com/tutus-one/tutus-chain/pkg/util"
"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)
// GovernmentHelper provides utilities for testing Tutus government contracts.
// It wraps common operations like registering citizens, granting roles, and
// setting up cross-contract test scenarios.
type GovernmentHelper struct {
*Executor
t testing.TB
// Contract invokers for common government contracts
Vita *ContractInvoker
Lex *ContractInvoker
Eligere *ContractInvoker
Scire *ContractInvoker
Salus *ContractInvoker
VTS *ContractInvoker
Annos *ContractInvoker
Tribute *ContractInvoker
RoleReg *ContractInvoker
Treasury *ContractInvoker
}
// NewGovernmentHelper creates a helper for testing government contracts.
// It initializes invokers for all major government contracts.
func NewGovernmentHelper(t testing.TB, e *Executor) *GovernmentHelper {
g := &GovernmentHelper{
Executor: e,
t: t,
}
// Initialize contract invokers with committee authority
g.Vita = e.CommitteeInvoker(e.NativeHash(t, nativenames.Vita))
g.Lex = e.CommitteeInvoker(e.NativeHash(t, nativenames.Lex))
g.Eligere = e.CommitteeInvoker(e.NativeHash(t, nativenames.Eligere))
g.Scire = e.CommitteeInvoker(e.NativeHash(t, nativenames.Scire))
g.Salus = e.CommitteeInvoker(e.NativeHash(t, nativenames.Salus))
g.VTS = e.CommitteeInvoker(e.NativeHash(t, nativenames.VTS))
g.Annos = e.CommitteeInvoker(e.NativeHash(t, nativenames.Annos))
g.Tribute = e.CommitteeInvoker(e.NativeHash(t, nativenames.Tribute))
g.RoleReg = e.CommitteeInvoker(e.NativeHash(t, nativenames.RoleRegistry))
g.Treasury = e.CommitteeInvoker(e.NativeHash(t, nativenames.Treasury))
return g
}
// Citizen represents a registered Vita holder for testing.
type Citizen struct {
Account SingleSigner
VitaID uint64
BirthTime uint64
Registered bool
}
// RegisterCitizen registers a new Vita token for the given account.
// birthTimestamp is the citizen's birth date (Unix timestamp in milliseconds).
// Returns the Citizen with VitaID populated.
func (g *GovernmentHelper) RegisterCitizen(account SingleSigner, birthTimestamp uint64) *Citizen {
citizen := &Citizen{
Account: account,
BirthTime: birthTimestamp,
}
// Get current token count to predict the new VitaID
g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) {
count, err := stack[0].TryInteger()
require.NoError(t, err)
citizen.VitaID = count.Uint64()
}, "totalSupply")
// Register the Vita token
g.Vita.Invoke(g.t, true, "register", account.ScriptHash(), birthTimestamp)
citizen.Registered = true
return citizen
}
// RegisterAdultCitizen registers a citizen who is 25 years old (adult).
// Useful for tests requiring voting age or adult status.
func (g *GovernmentHelper) RegisterAdultCitizen(account SingleSigner) *Citizen {
// 25 years ago in milliseconds
birthTime := uint64(time.Now().AddDate(-25, 0, 0).UnixMilli())
return g.RegisterCitizen(account, birthTime)
}
// RegisterChildCitizen registers a citizen who is 10 years old (child).
// Useful for tests verifying age restrictions.
func (g *GovernmentHelper) RegisterChildCitizen(account SingleSigner) *Citizen {
// 10 years ago in milliseconds
birthTime := uint64(time.Now().AddDate(-10, 0, 0).UnixMilli())
return g.RegisterCitizen(account, birthTime)
}
// RegisterElderCitizen registers a citizen who is 70 years old (elder).
// Useful for tests involving retirement age.
func (g *GovernmentHelper) RegisterElderCitizen(account SingleSigner) *Citizen {
// 70 years ago in milliseconds
birthTime := uint64(time.Now().AddDate(-70, 0, 0).UnixMilli())
return g.RegisterCitizen(account, birthTime)
}
// NewCitizenAccount creates a new account and registers it as a citizen.
// Returns both the citizen and their account for signing transactions.
func (g *GovernmentHelper) NewCitizenAccount() *Citizen {
acc := g.Vita.NewAccount(g.t).(SingleSigner)
return g.RegisterAdultCitizen(acc)
}
// VerifyVitaOwnership checks that the given account owns a Vita token.
func (g *GovernmentHelper) VerifyVitaOwnership(account util.Uint160) bool {
var hasVita bool
g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) {
balance, err := stack[0].TryInteger()
require.NoError(t, err)
hasVita = balance.Cmp(big.NewInt(0)) > 0
}, "balanceOf", account)
return hasVita
}
// GetVitaID returns the Vita token ID for the given owner, or -1 if not found.
func (g *GovernmentHelper) GetVitaID(owner util.Uint160) int64 {
var vitaID int64 = -1
g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) {
if stack[0].Type() != stackitem.AnyT {
tokens := stack[0].Value().([]stackitem.Item)
if len(tokens) > 0 {
id, err := tokens[0].TryInteger()
require.NoError(t, err)
vitaID = id.Int64()
}
}
}, "tokensOf", owner)
return vitaID
}
// SuspendVita suspends a citizen's Vita token (requires committee + Lex restriction).
func (g *GovernmentHelper) SuspendVita(vitaID uint64, reason string) {
// First, create a Lex liberty restriction (required for due process)
// This would normally require a judicial order
g.Vita.Invoke(g.t, true, "suspend", vitaID, reason)
}
// TransferVTS transfers VTS tokens between accounts.
func (g *GovernmentHelper) TransferVTS(from, to SingleSigner, amount int64) util.Uint256 {
vtsInvoker := g.VTS.WithSigners(from)
return vtsInvoker.Invoke(g.t, true, "transfer", from.ScriptHash(), to.ScriptHash(), amount, nil)
}
// GetVTSBalance returns the VTS balance for an account.
func (g *GovernmentHelper) GetVTSBalance(account util.Uint160) *big.Int {
var balance *big.Int
g.VTS.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) {
var err error
balance, err = stack[0].TryInteger()
require.NoError(t, err)
}, "balanceOf", account)
return balance
}
// MintVTS mints VTS tokens to an account (committee only).
func (g *GovernmentHelper) MintVTS(to util.Uint160, amount int64) {
g.VTS.Invoke(g.t, true, "mint", to, amount)
}
// CreateProposal creates a new Eligere proposal.
// Returns the proposal ID.
func (g *GovernmentHelper) CreateProposal(proposer *Citizen, title, description string, category int64) uint64 {
eligereInvoker := g.Eligere.WithSigners(proposer.Account)
var proposalID uint64
eligereInvoker.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) {
id, err := stack[0].TryInteger()
require.NoError(t, err)
proposalID = id.Uint64()
}, "createProposal", title, description, category)
return proposalID
}
// Vote casts a vote on a proposal.
func (g *GovernmentHelper) Vote(voter *Citizen, proposalID uint64, vote int64) {
eligereInvoker := g.Eligere.WithSigners(voter.Account)
eligereInvoker.Invoke(g.t, true, "vote", proposalID, vote)
}
// PrepareVitaRegistration prepares a Vita registration transaction without executing.
// Useful for batching or testing transaction ordering.
func (g *GovernmentHelper) PrepareVitaRegistration(account SingleSigner, birthTimestamp uint64) *transaction.Transaction {
return g.Vita.PrepareInvoke(g.t, "register", account.ScriptHash(), birthTimestamp)
}
// BatchRegisterCitizens registers multiple citizens in a single block.
func (g *GovernmentHelper) BatchRegisterCitizens(accounts []SingleSigner) []*Citizen {
citizens := make([]*Citizen, len(accounts))
txs := make([]*transaction.Transaction, len(accounts))
birthTime := uint64(time.Now().AddDate(-25, 0, 0).UnixMilli())
// Get starting VitaID
var startID uint64
g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) {
count, err := stack[0].TryInteger()
require.NoError(t, err)
startID = count.Uint64()
}, "totalSupply")
// Prepare all transactions
for i, acc := range accounts {
citizens[i] = &Citizen{
Account: acc,
BirthTime: birthTime,
VitaID: startID + uint64(i),
}
txs[i] = g.Vita.PrepareInvoke(g.t, "register", acc.ScriptHash(), birthTime)
}
// Execute all in one block
g.Vita.AddNewBlock(g.t, txs...)
// Verify all succeeded
for i, tx := range txs {
g.Vita.CheckHalt(g.t, tx.Hash(), stackitem.Make(true))
citizens[i].Registered = true
}
return citizens
}