add token holder group

This commit is contained in:
Jennifer Wang 2022-09-22 23:28:46 -04:00 committed by Aayush
parent d11248b998
commit e88c2061f1
3 changed files with 289 additions and 1 deletions

287
cmd/lotus-shed/fip-0036.go Normal file
View File

@ -0,0 +1,287 @@
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/store"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/filecoin-project/lotus/node/repo"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
"github.com/filecoin-project/lotus/chain/types"
"github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/go-address"
)
const APPROVE = 49
type Vote struct {
OptionID uint64 `json: "optionId"`
SignerAddress address.Address `json: "signerAddress"`
}
type msigVote struct {
Multisig msigBriefInfo
AcceptanceSingers []address.Address
RejectionSigners []address.Address
}
// https://filpoll.io/poll/16
// snapshot height: 2162760
// state root: bafy2bzacebdnzh43hw66bmvguk65wiwr5ssaejlq44fpdei2ysfh3eefpdlqs
var fip0036PollResultcmd = &cli.Command{
Name: "fip0036poll",
Usage: "Process the FIP0036 FilPoll result",
ArgsUsage: "[state root, votes]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
Subcommands: []*cli.Command{
//spRBPCmd,
//spDealByteCmd,
//clientCmd,
tokenHolderCmd,
//coreDevCmd,
//finalResultCmd,
},
}
func getVoters(file string) ([]Vote, error) {
var votes []Vote
vb, err := ioutil.ReadFile(file)
if err != nil {
return nil, xerrors.Errorf("read vote: %w", err)
}
if err := json.Unmarshal(vb, &votes); err != nil {
return nil, xerrors.Errorf("unmarshal vote: %w", err)
}
return votes, nil
}
func getAllMsig(st *state.StateTree, store adt.Store) ([]msigBriefInfo, error) {
var msigActorsInfo []msigBriefInfo
err := st.ForEach(func(addr address.Address, act *types.Actor) error {
if builtin.IsMultisigActor(act.Code) {
ms, err := multisig.Load(store, act)
if err != nil {
return fmt.Errorf("load msig failed %v", err)
}
signers, _ := ms.Signers()
threshold, _ := ms.Threshold()
info := msigBriefInfo{
ID: addr,
Signer: signers,
Balance: act.Balance,
Threshold: threshold,
}
msigActorsInfo = append(msigActorsInfo, info)
}
return nil
})
if err != nil {
return nil, err
}
return msigActorsInfo, nil
}
var tokenHolderCmd = &cli.Command{
Name: "token-holder",
Usage: "get poll result for token holder group",
ArgsUsage: "[state root]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
Action: func(cctx *cli.Context) error {
if cctx.NArg() != 2 {
return xerrors.New("filpoll0036 token-holder [state root] [votes.json]")
}
ctx := context.TODO()
if !cctx.Args().Present() {
return fmt.Errorf("must pass state root")
}
sroot, err := cid.Decode(cctx.Args().First())
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}
fsrepo, err := repo.NewFS(cctx.String("repo"))
if err != nil {
return err
}
lkrepo, err := fsrepo.Lock(repo.FullNode)
if err != nil {
return err
}
defer lkrepo.Close() //nolint:errcheck
bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore)
if err != nil {
return fmt.Errorf("failed to open blockstore: %w", err)
}
defer func() {
if c, ok := bs.(io.Closer); ok {
if err := c.Close(); err != nil {
log.Warnf("failed to close blockstore: %s", err)
}
}
}()
mds, err := lkrepo.Datastore(context.Background(), "/metadata")
if err != nil {
return err
}
cs := store.NewChainStore(bs, bs, mds, filcns.Weight, nil)
defer cs.Close() //nolint:errcheck
cst := cbor.NewCborStore(bs)
store := adt.WrapStore(ctx, cst)
tree, err := state.LoadStateTree(cst, sroot)
if err != nil {
return err
}
//get all the votes' singer address && their vote
vj, err := homedir.Expand(cctx.Args().Get(1))
if err != nil {
return xerrors.Errorf("fail to get votes json")
}
votes, err := getVoters(vj)
if err != nil {
return xerrors.Errorf("fail to get votesrs: ", err)
}
//get all the msig
msigs, err := getAllMsig(tree, store)
var acceptanceVote []Vote
var rejectionVote []Vote
acceptanceBalance := abi.NewTokenAmount(0)
rejectionBalance := abi.NewTokenAmount(0)
msigPendingVotes := make(map[address.Address]msigVote)
msigFinalVotes := make(map[address.Address]msigVote)
for _, v := range votes {
a, err := tree.GetActor(v.SignerAddress)
if err != nil {
return xerrors.Errorf("fail to get account account for signer: ", v.SignerAddress)
}
//regular account
if v.OptionID == APPROVE {
acceptanceVote = append(acceptanceVote, v)
acceptanceBalance = types.BigAdd(acceptanceBalance, a.Balance)
} else {
rejectionVote = append(rejectionVote, v)
rejectionBalance = types.BigAdd(rejectionBalance, a.Balance)
}
//msig
si, err := tree.LookupID(v.SignerAddress)
if err != nil {
return xerrors.Errorf("cannot resolve singer: ", si)
}
for _, m := range msigs {
for _, ms := range m.Signer {
if ms == si {
if mv, found := msigPendingVotes[m.ID]; found { //other singer has voted
if v.OptionID == APPROVE {
mv.AcceptanceSingers = append(mv.AcceptanceSingers, v.SignerAddress)
} else {
mv.RejectionSigners = append(mv.RejectionSigners, v.SignerAddress)
}
//check if threshold meet
if uint64(len(mv.AcceptanceSingers)) == m.Threshold {
delete(msigPendingVotes, m.ID)
msigFinalVotes[m.ID] = mv
acceptanceBalance = types.BigAdd(acceptanceBalance, m.Balance)
} else if uint64(len(mv.RejectionSigners)) == m.Threshold {
delete(msigPendingVotes, m.ID)
msigFinalVotes[m.ID] = mv
rejectionBalance = types.BigAdd(rejectionBalance, m.Balance)
} else {
msigPendingVotes[m.ID] = mv
}
} else {
n := msigVote{
Multisig: m,
}
if v.OptionID == APPROVE {
n.AcceptanceSingers = append(n.AcceptanceSingers, v.SignerAddress)
} else {
n.RejectionSigners = append(n.RejectionSigners, v.SignerAddress)
}
//check if threshold meet
if uint64(len(mv.AcceptanceSingers)) == m.Threshold {
delete(msigPendingVotes, m.ID)
msigFinalVotes[m.ID] = mv
acceptanceBalance = types.BigAdd(acceptanceBalance, m.Balance)
} else if uint64(len(mv.RejectionSigners)) == m.Threshold {
delete(msigPendingVotes, m.ID)
msigFinalVotes[m.ID] = mv
rejectionBalance = types.BigAdd(rejectionBalance, m.Balance)
} else {
msigPendingVotes[m.ID] = mv
}
}
}
}
}
}
fmt.Printf("\nTotoal amount of singers: %v\n ", len(votes))
fmt.Printf("Totoal amount of valid multisig vote: %v\n ", len(msigFinalVotes))
fmt.Printf("Total balance: %v\n", types.BigAdd(acceptanceBalance, rejectionBalance).String())
fmt.Printf("Approve: %v\n", acceptanceBalance.String())
fmt.Printf("Reject: %v\n", rejectionBalance.String())
av := types.BigDivFloat(acceptanceBalance, types.BigAdd(acceptanceBalance, rejectionBalance))
rv := types.BigDivFloat(rejectionBalance, types.BigAdd(rejectionBalance, acceptanceBalance))
if av > 0.05 {
fmt.Printf("Token Holder Group Result: Pass. approve: %.5f, reject: %.5f\n", av, rv)
} else {
fmt.Printf("Token Holder Group Result: Not Pass. approve: %.5f, reject: %.5f\n", av, rv)
}
return nil
},
}

View File

@ -74,6 +74,7 @@ func main() {
diffCmd, diffCmd,
itestdCmd, itestdCmd,
msigCmd, msigCmd,
fip0036PollResultcmd,
} }
app := &cli.App{ app := &cli.App{

View File

@ -25,7 +25,7 @@ import (
type msigBriefInfo struct { type msigBriefInfo struct {
ID address.Address ID address.Address
Signer interface{} Signer []address.Address
Balance abi.TokenAmount Balance abi.TokenAmount
Threshold uint64 Threshold uint64
} }