package state import ( "errors" "math/big" "git.marketally.com/tutus-one/tutus-chain/pkg/util" "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // ProposalCategory represents the type of proposal. type ProposalCategory uint8 // Proposal categories. const ( ProposalCategoryLawAmendment ProposalCategory = 1 // Lex integration ProposalCategoryInvestment ProposalCategory = 2 // PIO/EIO/CIO (future) ProposalCategoryGovernanceAction ProposalCategory = 3 // Committee/policy changes ProposalCategoryConstitutional ProposalCategory = 4 // Supermajority required ProposalCategoryReferendum ProposalCategory = 5 // Citizen-initiated ) // ProposalStatus represents the lifecycle status of a proposal. type ProposalStatus uint8 // Proposal statuses. const ( ProposalStatusDraft ProposalStatus = 0 // Not yet open for voting ProposalStatusActive ProposalStatus = 1 // Open for voting ProposalStatusPassed ProposalStatus = 2 // Met threshold, awaiting execution ProposalStatusRejected ProposalStatus = 3 // Failed to meet threshold ProposalStatusExecuted ProposalStatus = 4 // Successfully executed ProposalStatusCancelled ProposalStatus = 5 // Cancelled by proposer/committee ProposalStatusExpired ProposalStatus = 6 // Voting period ended without quorum ) // VoteChoice represents a voting decision. type VoteChoice uint8 // Vote choices. const ( VoteChoiceAbstain VoteChoice = 0 VoteChoiceYes VoteChoice = 1 VoteChoiceNo VoteChoice = 2 ) // Proposal represents a democratic proposal in the Eligere voting system. type Proposal struct { ID uint64 // Unique proposal ID Title string // Short title (max 128 chars) ContentHash util.Uint256 // Hash of full proposal content (stored off-chain) Category ProposalCategory // Type of proposal Proposer util.Uint160 // Who created the proposal ProposerVitaID uint64 // Vita ID of proposer // Timing (block heights) CreatedAt uint32 // When created VotingStartsAt uint32 // When voting opens VotingEndsAt uint32 // elegireDeadline - when voting closes ExecutionDelay uint32 // Blocks after passing before execution allowed // Thresholds QuorumPercent uint8 // Minimum participation percentage (e.g., 10%) ThresholdPercent uint8 // Required yes votes percentage (50% or 67%) // Results Status ProposalStatus TotalVotes uint64 // Total votes cast YesVotes uint64 // Votes in favor NoVotes uint64 // Votes against AbstainVotes uint64 // Abstentions (count toward quorum) // Execution target (for contract integrations like Lex) TargetContract util.Uint160 // Contract to call on execution TargetMethod string // Method to call TargetParams []byte // Serialized parameters // Execution tracking ExecutedAt uint32 // When executed (0 if not) ExecutedBy util.Uint160 // Who executed } // ToStackItem implements stackitem.Convertible interface. func (p *Proposal) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(p.ID))), stackitem.NewByteArray([]byte(p.Title)), stackitem.NewByteArray(p.ContentHash[:]), stackitem.NewBigInteger(big.NewInt(int64(p.Category))), stackitem.NewByteArray(p.Proposer.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(p.ProposerVitaID))), stackitem.NewBigInteger(big.NewInt(int64(p.CreatedAt))), stackitem.NewBigInteger(big.NewInt(int64(p.VotingStartsAt))), stackitem.NewBigInteger(big.NewInt(int64(p.VotingEndsAt))), stackitem.NewBigInteger(big.NewInt(int64(p.ExecutionDelay))), stackitem.NewBigInteger(big.NewInt(int64(p.QuorumPercent))), stackitem.NewBigInteger(big.NewInt(int64(p.ThresholdPercent))), stackitem.NewBigInteger(big.NewInt(int64(p.Status))), stackitem.NewBigInteger(big.NewInt(int64(p.TotalVotes))), stackitem.NewBigInteger(big.NewInt(int64(p.YesVotes))), stackitem.NewBigInteger(big.NewInt(int64(p.NoVotes))), stackitem.NewBigInteger(big.NewInt(int64(p.AbstainVotes))), stackitem.NewByteArray(p.TargetContract.BytesBE()), stackitem.NewByteArray([]byte(p.TargetMethod)), stackitem.NewByteArray(p.TargetParams), stackitem.NewBigInteger(big.NewInt(int64(p.ExecutedAt))), stackitem.NewByteArray(p.ExecutedBy.BytesBE()), }), nil } // FromStackItem implements stackitem.Convertible interface. func (p *Proposal) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(arr) < 22 { return errors.New("invalid proposal struct length") } id, err := arr[0].TryInteger() if err != nil { return err } p.ID = id.Uint64() titleBytes, err := arr[1].TryBytes() if err != nil { return err } p.Title = string(titleBytes) contentHashBytes, err := arr[2].TryBytes() if err != nil { return err } if len(contentHashBytes) == 32 { copy(p.ContentHash[:], contentHashBytes) } category, err := arr[3].TryInteger() if err != nil { return err } p.Category = ProposalCategory(category.Uint64()) proposerBytes, err := arr[4].TryBytes() if err != nil { return err } p.Proposer, _ = util.Uint160DecodeBytesBE(proposerBytes) proposerVitaID, err := arr[5].TryInteger() if err != nil { return err } p.ProposerVitaID = proposerVitaID.Uint64() createdAt, err := arr[6].TryInteger() if err != nil { return err } p.CreatedAt = uint32(createdAt.Uint64()) votingStartsAt, err := arr[7].TryInteger() if err != nil { return err } p.VotingStartsAt = uint32(votingStartsAt.Uint64()) votingEndsAt, err := arr[8].TryInteger() if err != nil { return err } p.VotingEndsAt = uint32(votingEndsAt.Uint64()) executionDelay, err := arr[9].TryInteger() if err != nil { return err } p.ExecutionDelay = uint32(executionDelay.Uint64()) quorumPercent, err := arr[10].TryInteger() if err != nil { return err } p.QuorumPercent = uint8(quorumPercent.Uint64()) thresholdPercent, err := arr[11].TryInteger() if err != nil { return err } p.ThresholdPercent = uint8(thresholdPercent.Uint64()) status, err := arr[12].TryInteger() if err != nil { return err } p.Status = ProposalStatus(status.Uint64()) totalVotes, err := arr[13].TryInteger() if err != nil { return err } p.TotalVotes = totalVotes.Uint64() yesVotes, err := arr[14].TryInteger() if err != nil { return err } p.YesVotes = yesVotes.Uint64() noVotes, err := arr[15].TryInteger() if err != nil { return err } p.NoVotes = noVotes.Uint64() abstainVotes, err := arr[16].TryInteger() if err != nil { return err } p.AbstainVotes = abstainVotes.Uint64() targetContractBytes, err := arr[17].TryBytes() if err != nil { return err } p.TargetContract, _ = util.Uint160DecodeBytesBE(targetContractBytes) targetMethodBytes, err := arr[18].TryBytes() if err != nil { return err } p.TargetMethod = string(targetMethodBytes) targetParams, err := arr[19].TryBytes() if err != nil { return err } p.TargetParams = targetParams executedAt, err := arr[20].TryInteger() if err != nil { return err } p.ExecutedAt = uint32(executedAt.Uint64()) executedByBytes, err := arr[21].TryBytes() if err != nil { return err } p.ExecutedBy, _ = util.Uint160DecodeBytesBE(executedByBytes) return nil } // Vote represents a single vote record in the Eligere voting system. type Vote struct { ProposalID uint64 // Which proposal VoterVitaID uint64 // Vita ID of voter (ensures one-person-one-vote) Voter util.Uint160 // Voter's address Choice VoteChoice // How they voted VotedAt uint32 // Block height when voted Weight uint64 // Vote weight (1 for equal voting) } // ToStackItem implements stackitem.Convertible interface. func (v *Vote) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(v.ProposalID))), stackitem.NewBigInteger(big.NewInt(int64(v.VoterVitaID))), stackitem.NewByteArray(v.Voter.BytesBE()), stackitem.NewBigInteger(big.NewInt(int64(v.Choice))), stackitem.NewBigInteger(big.NewInt(int64(v.VotedAt))), stackitem.NewBigInteger(big.NewInt(int64(v.Weight))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (v *Vote) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(arr) < 6 { return errors.New("invalid vote struct length") } proposalID, err := arr[0].TryInteger() if err != nil { return err } v.ProposalID = proposalID.Uint64() voterVitaID, err := arr[1].TryInteger() if err != nil { return err } v.VoterVitaID = voterVitaID.Uint64() voterBytes, err := arr[2].TryBytes() if err != nil { return err } v.Voter, _ = util.Uint160DecodeBytesBE(voterBytes) choice, err := arr[3].TryInteger() if err != nil { return err } v.Choice = VoteChoice(choice.Uint64()) votedAt, err := arr[4].TryInteger() if err != nil { return err } v.VotedAt = uint32(votedAt.Uint64()) weight, err := arr[5].TryInteger() if err != nil { return err } v.Weight = weight.Uint64() return nil } // EligereConfig represents configurable parameters for the Eligere voting system. type EligereConfig struct { DefaultVotingPeriod uint32 // Default blocks for voting (~7 days at 1s blocks = 604800) MinVotingPeriod uint32 // Minimum blocks (~1 day = 86400) MaxVotingPeriod uint32 // Maximum blocks (~30 days = 2592000) DefaultExecutionDelay uint32 // Blocks between passing and execution (~1 day = 86400) DefaultQuorum uint8 // Default quorum percentage (e.g., 10%) ConstitutionalThreshold uint8 // Supermajority threshold (e.g., 67%) StandardThreshold uint8 // Simple majority (e.g., 51%) } // ToStackItem implements stackitem.Convertible interface. func (c *EligereConfig) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBigInteger(big.NewInt(int64(c.DefaultVotingPeriod))), stackitem.NewBigInteger(big.NewInt(int64(c.MinVotingPeriod))), stackitem.NewBigInteger(big.NewInt(int64(c.MaxVotingPeriod))), stackitem.NewBigInteger(big.NewInt(int64(c.DefaultExecutionDelay))), stackitem.NewBigInteger(big.NewInt(int64(c.DefaultQuorum))), stackitem.NewBigInteger(big.NewInt(int64(c.ConstitutionalThreshold))), stackitem.NewBigInteger(big.NewInt(int64(c.StandardThreshold))), }), nil } // FromStackItem implements stackitem.Convertible interface. func (c *EligereConfig) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") } if len(arr) < 7 { return errors.New("invalid config struct length") } defaultVotingPeriod, err := arr[0].TryInteger() if err != nil { return err } c.DefaultVotingPeriod = uint32(defaultVotingPeriod.Uint64()) minVotingPeriod, err := arr[1].TryInteger() if err != nil { return err } c.MinVotingPeriod = uint32(minVotingPeriod.Uint64()) maxVotingPeriod, err := arr[2].TryInteger() if err != nil { return err } c.MaxVotingPeriod = uint32(maxVotingPeriod.Uint64()) defaultExecutionDelay, err := arr[3].TryInteger() if err != nil { return err } c.DefaultExecutionDelay = uint32(defaultExecutionDelay.Uint64()) defaultQuorum, err := arr[4].TryInteger() if err != nil { return err } c.DefaultQuorum = uint8(defaultQuorum.Uint64()) constitutionalThreshold, err := arr[5].TryInteger() if err != nil { return err } c.ConstitutionalThreshold = uint8(constitutionalThreshold.Uint64()) standardThreshold, err := arr[6].TryInteger() if err != nil { return err } c.StandardThreshold = uint8(standardThreshold.Uint64()) return nil } // DefaultEligereConfig returns the default configuration for Eligere. func DefaultEligereConfig() *EligereConfig { return &EligereConfig{ DefaultVotingPeriod: 604800, // ~7 days at 1 block/second MinVotingPeriod: 86400, // ~1 day minimum MaxVotingPeriod: 2592000, // ~30 days maximum DefaultExecutionDelay: 86400, // ~1 day delay after passing DefaultQuorum: 10, // 10% participation required ConstitutionalThreshold: 67, // 67% supermajority for constitutional StandardThreshold: 51, // 51% simple majority } }