github.com/aergoio/aergo@v1.3.1/contract/system/vote.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package system
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"encoding/json"
    12  	"math/big"
    13  
    14  	"github.com/aergoio/aergo/internal/enc"
    15  	"github.com/aergoio/aergo/state"
    16  	"github.com/aergoio/aergo/types"
    17  	"github.com/mr-tron/base58"
    18  )
    19  
    20  var defaultBpCount int
    21  
    22  var voteKey = []byte("vote")
    23  var sortKey = []byte("sort")
    24  
    25  const PeerIDLength = 39
    26  
    27  const VotingDelay = 60 * 60 * 24 //block interval
    28  //const VotingDelay = 5
    29  
    30  var defaultVoteKey = []byte(types.VoteBP)[2:]
    31  
    32  func voting(txBody *types.TxBody, sender, receiver *state.V, scs *state.ContractState,
    33  	blockNo types.BlockNo, context *SystemContext) (*types.Event, error) {
    34  	key := []byte(context.Call.Name)[2:]
    35  	oldvote := context.Vote
    36  	staked := context.Staked
    37  	//update block number
    38  	staked.When = blockNo
    39  	err := setStaking(scs, sender.ID(), staked)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	voteResult, err := loadVoteResult(scs, key)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	err = voteResult.SubVote(oldvote)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	if staked.GetAmountBigInt().Cmp(new(big.Int).SetUint64(0)) == 0 {
    55  		return nil, types.ErrMustStakeBeforeVote
    56  	}
    57  	vote := &types.Vote{Amount: staked.GetAmount()}
    58  	args, err := json.Marshal(context.Call.Args)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	var candidates []byte
    63  	if bytes.Equal(key, defaultVoteKey) {
    64  		for _, v := range context.Call.Args {
    65  			candidate, _ := base58.Decode(v.(string))
    66  			candidates = append(candidates, candidate...)
    67  		}
    68  		vote.Candidate = candidates
    69  	} else {
    70  		vote.Candidate = args
    71  	}
    72  
    73  	err = setVote(scs, key, sender.ID(), vote)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	err = voteResult.AddVote(vote)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	err = voteResult.Sync(scs)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return &types.Event{
    87  		ContractAddress: receiver.ID(),
    88  		EventIdx:        0,
    89  		EventName:       context.Call.Name[2:],
    90  		JsonArgs: `{"who":"` +
    91  			types.EncodeAddress(txBody.Account) +
    92  			`", "vote":` + string(args) + `}`,
    93  	}, nil
    94  }
    95  
    96  func refreshAllVote(txBody *types.TxBody, scs *state.ContractState,
    97  	context *SystemContext) error {
    98  	account := context.Sender.ID()
    99  	staked := context.Staked
   100  	stakedAmount := new(big.Int).SetBytes(staked.Amount)
   101  	for _, keystr := range types.AllVotes {
   102  		key := []byte(keystr[2:])
   103  		oldvote, err := getVote(scs, key, account)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		if oldvote.Amount == nil ||
   108  			new(big.Int).SetBytes(oldvote.Amount).Cmp(stakedAmount) <= 0 {
   109  			continue
   110  		}
   111  		voteResult, err := loadVoteResult(scs, key)
   112  		if err != nil {
   113  			return err
   114  		}
   115  		if err = voteResult.SubVote(oldvote); err != nil {
   116  			return err
   117  		}
   118  		oldvote.Amount = staked.GetAmount()
   119  		if err = setVote(scs, key, account, oldvote); err != nil {
   120  			return err
   121  		}
   122  		if err = voteResult.AddVote(oldvote); err != nil {
   123  			return err
   124  		}
   125  		if err = voteResult.Sync(scs); err != nil {
   126  			return err
   127  		}
   128  	}
   129  	return nil
   130  }
   131  
   132  //GetVote return amount, to, err
   133  func GetVote(scs *state.ContractState, voter []byte, title []byte) (*types.Vote, error) {
   134  	return getVote(scs, title, voter)
   135  }
   136  
   137  func getVote(scs *state.ContractState, key, voter []byte) (*types.Vote, error) {
   138  	dataKey := append(append(voteKey, key...), voter...)
   139  	data, err := scs.GetData(dataKey)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	var vote types.Vote
   144  	if len(data) != 0 {
   145  		if bytes.Equal(key, defaultVoteKey) {
   146  			return deserializeVote(data), nil
   147  		} else {
   148  			return deserializeVoteEx(data), nil
   149  		}
   150  	}
   151  
   152  	return &vote, nil
   153  }
   154  
   155  func setVote(scs *state.ContractState, key, voter []byte, vote *types.Vote) error {
   156  	dataKey := append(append(voteKey, key...), voter...)
   157  	if bytes.Equal(key, defaultVoteKey) {
   158  		return scs.SetData(dataKey, serializeVote(vote))
   159  	} else {
   160  		return scs.SetData(dataKey, serializeVoteEx(vote))
   161  	}
   162  }
   163  
   164  // BuildOrderedCandidates returns a candidate list ordered by votes.xs
   165  func BuildOrderedCandidates(vote map[string]*big.Int) []string {
   166  	// TODO: cleanup
   167  	voteResult := newVoteResult(defaultVoteKey)
   168  	voteResult.rmap = vote
   169  	l := voteResult.buildVoteList()
   170  	bps := make([]string, 0, len(l.Votes))
   171  	for _, v := range l.Votes {
   172  		bp := enc.ToString(v.Candidate)
   173  		bps = append(bps, bp)
   174  	}
   175  	return bps
   176  }
   177  
   178  // AccountStateReader is an interface for getting a system account state.
   179  type AccountStateReader interface {
   180  	GetSystemAccountState() (*state.ContractState, error)
   181  }
   182  
   183  // GetVoteResult returns the top n voting result from the system account state.
   184  func GetVoteResult(ar AccountStateReader, id []byte, n int) (*types.VoteList, error) {
   185  	scs, err := ar.GetSystemAccountState()
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	return getVoteResult(scs, id, n)
   190  }
   191  
   192  // InitDefaultBpCount sets defaultBpCount to bpCount.
   193  //
   194  // Caution: This function must be called only once before all the aergosvr
   195  // services start.
   196  func InitDefaultBpCount(bpCount int) {
   197  	// Ensure that it is not modified after it is initialized.
   198  	if defaultBpCount > 0 {
   199  		return
   200  	}
   201  	defaultBpCount = bpCount
   202  }
   203  
   204  func getDefaultBpCount() int {
   205  	return defaultBpCount
   206  }
   207  
   208  // GetRankers returns the IDs of the top n rankers.
   209  func GetRankers(ar AccountStateReader) ([]string, error) {
   210  	n := getDefaultBpCount()
   211  
   212  	vl, err := GetVoteResult(ar, defaultVoteKey, n)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	bps := make([]string, 0, n)
   218  	for _, v := range vl.Votes {
   219  		bps = append(bps, enc.ToString(v.Candidate))
   220  	}
   221  
   222  	return bps, nil
   223  }
   224  
   225  func serializeVoteList(vl *types.VoteList, ex bool) []byte {
   226  	var data []byte
   227  	for _, v := range vl.GetVotes() {
   228  		var serialized []byte
   229  		if ex {
   230  			serialized = serializeVoteEx(v)
   231  		} else {
   232  			serialized = serializeVote(v)
   233  		}
   234  		vsize := make([]byte, 8)
   235  		binary.LittleEndian.PutUint64(vsize, uint64(len(serialized)))
   236  		data = append(data, vsize...)
   237  		data = append(data, serialized...)
   238  	}
   239  	return data
   240  }
   241  
   242  func serializeVote(v *types.Vote) []byte {
   243  	var ret []byte
   244  	if v != nil {
   245  		ret = append(ret, v.GetCandidate()...)
   246  		ret = append(ret, v.GetAmount()...)
   247  	}
   248  	return ret
   249  }
   250  
   251  func serializeVoteEx(v *types.Vote) []byte {
   252  	var ret []byte
   253  	if v != nil {
   254  		size := make([]byte, 8)
   255  		binary.LittleEndian.PutUint64(size, uint64(len(v.Candidate)))
   256  		ret = append(ret, size...)
   257  		ret = append(ret, v.GetCandidate()...)
   258  		ret = append(ret, v.GetAmount()...)
   259  	}
   260  	return ret
   261  }
   262  
   263  func deserializeVote(data []byte) *types.Vote {
   264  	pos := len(data) % PeerIDLength
   265  	candidate := data[:len(data)-pos]
   266  	amount := data[len(data)-pos:]
   267  	if len(candidate)%PeerIDLength != 0 {
   268  		panic("voting data corruption")
   269  	}
   270  	return &types.Vote{Amount: amount, Candidate: candidate}
   271  }
   272  
   273  func deserializeVoteEx(data []byte) *types.Vote {
   274  	size := int(binary.LittleEndian.Uint64(data[:8]))
   275  	candidate := data[8 : 8+size]
   276  	amount := data[8+size:]
   277  	return &types.Vote{Amount: amount, Candidate: candidate}
   278  }
   279  
   280  func deserializeVoteList(data []byte, ex bool) *types.VoteList {
   281  	vl := &types.VoteList{Votes: []*types.Vote{}}
   282  	var end int
   283  	for offset := 0; offset < len(data); offset = end {
   284  		size := binary.LittleEndian.Uint64(data[offset : offset+8])
   285  		end = offset + 8 + int(size)
   286  		v := data[offset+8 : end]
   287  		if ex {
   288  			vl.Votes = append(vl.Votes, deserializeVoteEx(v))
   289  		} else {
   290  			vl.Votes = append(vl.Votes, deserializeVote(v))
   291  		}
   292  	}
   293  	return vl
   294  }