forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PVM] DAO voting, AddProposalTx, AddVoteTx
- Loading branch information
Showing
29 changed files
with
2,691 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package dao | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/ava-labs/avalanchego/utils/set" | ||
) | ||
|
||
var ( | ||
_ Proposal = (*ChangeBaseFeeProposal)(nil) | ||
|
||
errZeroFee = errors.New("base-fee option is zero") | ||
errNotUniqueOption = errors.New("not unique option") | ||
errNoOptions = errors.New("no options") | ||
errWrongVoteType = errors.New("wrong vote type") | ||
) | ||
|
||
type ChangeBaseFeeProposal struct { | ||
ProposalWithDuration `serialize:"true"` | ||
FeeOptions []uint64 `serialize:"true"` | ||
} | ||
|
||
func (p *ChangeBaseFeeProposal) Verify() error { | ||
// TODO@ make it strictly 3 ? thats requirement - to have 3 options for this proposal | ||
if len(p.FeeOptions) == 0 { | ||
return errNoOptions | ||
} | ||
|
||
uniqueFees := set.NewSet[uint64](len(p.FeeOptions)) | ||
for _, fee := range p.FeeOptions { | ||
if fee == 0 { | ||
return errZeroFee | ||
} | ||
if uniqueFees.Contains(fee) { | ||
return errNotUniqueOption | ||
} | ||
uniqueFees.Add(fee) | ||
} | ||
|
||
return p.ProposalWithDuration.Verify() | ||
} | ||
|
||
func (p *ChangeBaseFeeProposal) CanBeVotedWith(voteIntf Vote) bool { | ||
vote, ok := voteIntf.(*SimpleVote) | ||
if !ok { | ||
return false | ||
} | ||
if int(vote.OptionIndex) >= len(p.FeeOptions) { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func (p *ChangeBaseFeeProposal) Visit(visitor Visitor) error { | ||
return visitor.ChangeBaseFeeProposal(p) | ||
} | ||
|
||
func (p *ChangeBaseFeeProposal) canBeExecuted(proposalWithVotes *ProposalWithVotes) bool { | ||
_, weight, err := p.Result(proposalWithVotes.Votes) | ||
return err == nil && weight > proposalWithVotes.SuccessThreshold() | ||
} | ||
|
||
// Votes must be valid for this proposal, could panic otherwise. | ||
func (p *ChangeBaseFeeProposal) Result(votes []VoteWithAddr) (uint64, uint32, error) { // TODO@ optimize | ||
if len(votes) == 0 { | ||
return 0, 0, errNoVotes | ||
} | ||
|
||
uncertain := false | ||
optionWeights := make([]uint32, len(p.FeeOptions)) | ||
mostVotedOptionIndex := uint32(0) | ||
|
||
for i := range votes { | ||
vote, ok := votes[i].Vote.(*SimpleVote) | ||
if !ok { | ||
return 0, 0, errWrongVoteType | ||
} | ||
optionWeights[vote.OptionIndex]++ | ||
if vote.OptionIndex != mostVotedOptionIndex && | ||
optionWeights[vote.OptionIndex] == optionWeights[mostVotedOptionIndex] { | ||
uncertain = true | ||
} else if optionWeights[vote.OptionIndex] > optionWeights[mostVotedOptionIndex] { | ||
mostVotedOptionIndex = vote.OptionIndex | ||
uncertain = false | ||
} | ||
} | ||
|
||
if uncertain { | ||
return 0, 0, errUncertainResult | ||
} | ||
|
||
return p.FeeOptions[mostVotedOptionIndex], optionWeights[mostVotedOptionIndex], nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright (C) 2023, Chain4Travel AG. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package dao | ||
|
||
import ( | ||
"errors" | ||
"time" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/vms/components/verify" | ||
) | ||
|
||
const MaxProposalSize = 2048 // TODO@ | ||
|
||
var ( | ||
errEndNotAfterStart = errors.New("proposal end-time is not after start-time") | ||
errNoVotes = errors.New("no votes") | ||
errUncertainResult = errors.New("uncertain result") | ||
) | ||
|
||
type Visitor interface { | ||
ChangeBaseFeeProposal(*ChangeBaseFeeProposal) error | ||
} | ||
|
||
type Proposal interface { | ||
verify.Verifiable | ||
|
||
CanBeVotedWith(Vote) bool | ||
StartTime() time.Time | ||
EndTime() time.Time | ||
IsActiveAt(time time.Time) bool | ||
Visit(Visitor) error | ||
|
||
canBeExecuted(*ProposalWithVotes) bool | ||
} | ||
|
||
type ProposalWithVotes struct { | ||
Proposal `serialize:"true"` | ||
AllowedVoters []ids.ShortID `serialize:"true"` // TODO@ optimize | ||
Votes []VoteWithAddr `serialize:"true"` // TODO@ optimize | ||
} | ||
|
||
func (p *ProposalWithVotes) SuccessThreshold() uint32 { | ||
return uint32(len(p.AllowedVoters) / 2) | ||
} | ||
|
||
func (p *ProposalWithVotes) CanBeVotedBy(voterAddr ids.ShortID) bool { // TODO@ optimize | ||
for _, allowedVoterAddress := range p.AllowedVoters { | ||
if allowedVoterAddress == voterAddr { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (p *ProposalWithVotes) WasVotedBy(voterAddr ids.ShortID) bool { // TODO@ optimize | ||
for _, vote := range p.Votes { | ||
if vote.VoterAddress == voterAddr { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (p *ProposalWithVotes) CanBeExecuted() bool { | ||
return p.Proposal.canBeExecuted(p) | ||
} | ||
|
||
// Will return modified proposal with added vote, original proposal will not be modified! | ||
func (p *ProposalWithVotes) AddVote(voterAddress ids.ShortID, vote Vote) *ProposalWithVotes { | ||
updatedProposal := *p | ||
updatedProposal.Votes = append(p.Votes, VoteWithAddr{ | ||
Vote: vote, | ||
VoterAddress: voterAddress, | ||
}) | ||
return &updatedProposal | ||
} | ||
|
||
type TxProposalWrapper struct { | ||
Proposal `serialize:"true"` | ||
} | ||
|
||
type ProposalWithStartTime struct { // some proposals will have fixed duration | ||
Start uint64 `serialize:"true"` | ||
} | ||
|
||
func (p *ProposalWithStartTime) StartTime() time.Time { | ||
return time.Unix(int64(p.Start), 0) | ||
} | ||
|
||
type ProposalWithDuration struct { // some proposals will have flexible duration | ||
ProposalWithStartTime `serialize:"true"` | ||
End uint64 `serialize:"true"` | ||
} | ||
|
||
func (p *ProposalWithDuration) EndTime() time.Time { | ||
return time.Unix(int64(p.End), 0) | ||
} | ||
|
||
func (p *ProposalWithDuration) Verify() error { | ||
if p.Start >= p.End { | ||
return errEndNotAfterStart | ||
} | ||
return nil | ||
} | ||
|
||
func (p *ProposalWithDuration) IsActiveAt(time time.Time) bool { | ||
timestamp := uint64(time.Unix()) | ||
return p.Start >= timestamp && timestamp <= p.End | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package dao | ||
|
||
import ( | ||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/vms/components/verify" | ||
) | ||
|
||
const MaxVoteSize = 2048 // TODO@ | ||
|
||
var _ Vote = (*SimpleVote)(nil) | ||
|
||
type Vote interface { | ||
verify.Verifiable | ||
} | ||
|
||
type VoteWithAddr struct { | ||
Vote `serialize:"true"` | ||
VoterAddress ids.ShortID `serialize:"true"` | ||
} | ||
|
||
type TxVoteWrapper struct { | ||
Vote `serialize:"true"` | ||
} | ||
|
||
type SimpleVote struct { | ||
OptionIndex uint32 `serialize:"true"` | ||
} | ||
|
||
func (*SimpleVote) Verify() error { | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.