Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement clawback of airdrop from inactive genesis addresses #560

Merged
merged 12 commits into from
Nov 19, 2021
166 changes: 128 additions & 38 deletions cmd/osmosisd/cmd/genaccounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
appparams "github.com/osmosis-labs/osmosis/app/params"
claimtypes "github.com/osmosis-labs/osmosis/x/claim/types"
)

Expand Down Expand Up @@ -183,6 +183,116 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
return cmd
}

func GetOsmoSnapshot(inputFile string) (Snapshot, error) {
snapshotJSON, err := os.Open(inputFile)
if err != nil {
return Snapshot{}, err
}
defer snapshotJSON.Close()

byteValue, err := ioutil.ReadAll(snapshotJSON)
if err != nil {
return Snapshot{}, err
}

snapshot := Snapshot{}
err = json.Unmarshal(byteValue, &snapshot)
if err != nil {
return Snapshot{}, err
}
return snapshot, nil
}

func GetIonSnapshot(inputFile string) (map[string]int64, error) {
ionJSON, err := os.Open(inputFile)
if err != nil {
return nil, err
}
defer ionJSON.Close()
byteValue2, _ := ioutil.ReadAll(ionJSON)

var ionAmts map[string]int64
err = json.Unmarshal(byteValue2, &ionAmts)
if err != nil {
return nil, err
}
return ionAmts, nil
}

func CosmosToOsmoAddress(cosmosAddr string) (string, error) {
_, bz, err := bech32.DecodeAndConvert(cosmosAddr)
if err != nil {
return "", err
}

convertedAddr, err := bech32.ConvertAndEncode("osmo", bz)
if err != nil {
panic(err)
}

return convertedAddr, nil
}

func GetAirdropAccountsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get-airdrop-accounts [input-snapshot-file] [input-ions-file]",
Short: "Get list of all accounts that are being airdropped to at genesis",
Long: `Get list of all accounts that are being airdropped to at genesis
Both OSMO and ION recipients

Example:
osmosisd import-genesis-accounts-from-snapshot ../snapshot.json ../ions.json ./address.txt
`,
ValarDragon marked this conversation as resolved.
Show resolved Hide resolved
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
airdropAddrs := map[string]bool{}

// Read snapshot file
snapshot, err := GetOsmoSnapshot(args[0])
if err != nil {
return err
}

// Read ions file
ionAmts, err := GetIonSnapshot(args[1])
if err != nil {
return err
}

for _, acc := range snapshot.Accounts {
if !acc.OsmoBalance.Equal(sdk.ZeroInt()) {
osmoAddr, err := CosmosToOsmoAddress(acc.AtomAddress)
if err != nil {
return err
}

airdropAddrs[osmoAddr] = true
}
}

for addr := range ionAmts {
ionAddr, err := CosmosToOsmoAddress(addr)
mattverse marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

airdropAddrs[ionAddr] = true
}

airdropAddrsJSON, err := json.Marshal(airdropAddrs)
if err != nil {
return err
}
err = ioutil.WriteFile(args[2], airdropAddrsJSON, 0644)
return err
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

func ImportGenesisAccountsFromSnapshotCmd(defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "import-genesis-accounts-from-snapshot [input-snapshot-file] [input-ions-file]",
Expand Down Expand Up @@ -223,29 +333,13 @@ Example:
}

// Read snapshot file
snapshotInput := args[0]
snapshotJSON, err := os.Open(snapshotInput)
if err != nil {
return err
}
defer snapshotJSON.Close()
byteValue, _ := ioutil.ReadAll(snapshotJSON)
snapshot := Snapshot{}
err = json.Unmarshal(byteValue, &snapshot)
snapshot, err := GetOsmoSnapshot(args[0])
if err != nil {
return err
}

// Read ions file
ionInput := args[1]
ionJSON, err := os.Open(ionInput)
if err != nil {
return err
}
defer ionJSON.Close()
byteValue2, _ := ioutil.ReadAll(ionJSON)
var ionAmts map[string]int64
err = json.Unmarshal(byteValue2, &ionAmts)
ionAmts, err := GetIonSnapshot(args[1])
if err != nil {
return err
}
Expand All @@ -267,17 +361,15 @@ Example:
}

for addr, amt := range ionAmts {
setCosmosBech32Prefixes()
address, err := sdk.AccAddressFromBech32(addr)
address, err := CosmosToOsmoAddress(addr)
if err != nil {
return err
}
appparams.SetAddressPrefixes()

if val, ok := nonAirdropAccs[address.String()]; ok {
nonAirdropAccs[address.String()] = val.Add(sdk.NewCoin("uion", sdk.NewInt(amt).MulRaw(1_000_000)))
if val, ok := nonAirdropAccs[address]; ok {
nonAirdropAccs[address] = val.Add(sdk.NewCoin("uion", sdk.NewInt(amt).MulRaw(1_000_000)))
} else {
nonAirdropAccs[address.String()] = sdk.NewCoins(sdk.NewCoin("uion", sdk.NewInt(amt).MulRaw(1_000_000)))
nonAirdropAccs[address] = sdk.NewCoins(sdk.NewCoin("uion", sdk.NewInt(amt).MulRaw(1_000_000)))
}
}

Expand All @@ -293,18 +385,12 @@ Example:

// for each account in the snapshot
for _, acc := range snapshot.Accounts {
// set atom bech32 prefixes
setCosmosBech32Prefixes()

// read address from snapshot
address, err := sdk.AccAddressFromBech32(acc.AtomAddress)
// convert cosmos address to osmo address
address, err := CosmosToOsmoAddress(acc.AtomAddress)
if err != nil {
return err
}

// set osmo bech32 prefixes
appparams.SetAddressPrefixes()

// skip accounts with 0 balance
if !acc.OsmoBalanceBase.IsPositive() {
continue
Expand All @@ -318,29 +404,33 @@ Example:
liquidAmount := normalizedOsmoBalance.Mul(sdk.MustNewDecFromStr("0.2")).TruncateInt() // 20% of airdrop amount
liquidCoins := sdk.NewCoins(sdk.NewCoin(genesisParams.NativeCoinMetadatas[0].Base, liquidAmount))

if coins, ok := nonAirdropAccs[address.String()]; ok {
if coins, ok := nonAirdropAccs[address]; ok {
liquidCoins = liquidCoins.Add(coins...)
delete(nonAirdropAccs, address.String())
delete(nonAirdropAccs, address)
}

liquidBalances = append(liquidBalances, banktypes.Balance{
Address: address.String(),
Address: address,
Coins: liquidCoins,
})

// claimable balances
claimableAmount := normalizedOsmoBalance.Mul(sdk.MustNewDecFromStr("0.8")).TruncateInt()

claimRecords = append(claimRecords, claimtypes.ClaimRecord{
Address: address.String(),
Address: address,
InitialClaimableAmount: sdk.NewCoins(sdk.NewCoin(genesisParams.NativeCoinMetadatas[0].Base, claimableAmount)),
ActionCompleted: []bool{false, false, false, false},
})

claimModuleAccountBalance = claimModuleAccountBalance.Add(claimableAmount)

// Add the new account to the set of genesis accounts
baseAccount := authtypes.NewBaseAccount(address, nil, 0, 0)
sdkaddr, err := sdk.AccAddressFromBech32(address)
if err != nil {
return err
}
baseAccount := authtypes.NewBaseAccount(sdkaddr, nil, 0, 0)
if err := baseAccount.Validate(); err != nil {
return fmt.Errorf("failed to validate new genesis account: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/osmosisd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
genutilcli.ValidateGenesisCmd(osmosis.ModuleBasics),
AddGenesisAccountCmd(osmosis.DefaultNodeHome),
ExportAirdropSnapshotCmd(),
GetAirdropAccountsCmd(),
ExportDeriveBalancesCmd(),
PrepareGenesisCmd(osmosis.DefaultNodeHome, osmosis.ModuleBasics),
ImportGenesisAccountsFromSnapshotCmd(osmosis.DefaultNodeHome),
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ require (
github.com/lib/pq v1.2.0 // indirect
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
github.com/minio/highwayhash v1.0.1 // indirect
Expand Down Expand Up @@ -106,7 +106,7 @@ require (
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/ini.v1 v1.61.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

replace (
Expand All @@ -116,5 +116,4 @@ replace (
github.com/tendermint/tm-db => github.com/osmosis-labs/tm-db v0.6.5-0.20210911033928-ba9154613417
google.golang.org/grpc => google.golang.org/grpc v1.33.2


)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,9 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
Expand Down Expand Up @@ -875,8 +876,9 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
30 changes: 30 additions & 0 deletions x/claim/keeper/claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,36 @@ func (k Keeper) EndAirdrop(ctx sdk.Context) error {
return err
}
k.clearInitialClaimables(ctx)

k.ClawbackAirdrop(ctx)
return nil
}

// ClawbackAirdrop implements prop 32 by clawing back all the OSMO and IONs from airdrop
// recipient accounts with a sequence number of 0
func (k Keeper) ClawbackAirdrop(ctx sdk.Context) error {
for bechAddr := range types.AirdropAddrs {
addr, err := sdk.AccAddressFromBech32(bechAddr)
if err != nil {
return err
}

acc := k.accountKeeper.GetAccount(ctx, addr)
if acc != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to make sure I understand the prior bug. Why would it clawback from all accounts? (Or were the addresses just wrong)

Also is there any expected scenario where acc would be nil, or this is just in case something unexpected occurs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it was doing the opposite. It wasn't clawing back from any accounts.
See original testcases:
https://github.com/osmosis-labs/osmosis/blob/cf7ba91f7bd74e6b9e7d3559f5e0e0ff9da87b8b/x/claim/keeper/claim_test.go

It's cause the function was exiting early, b/c as soon as it saw an account that wasn't in the account store it would error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh that makes sense

seq, err := k.accountKeeper.GetSequence(ctx, addr)
if err != nil {
return err
}
if seq == 0 {
osmoBal := k.bankKeeper.GetBalance(ctx, addr, "uosmo")
ionBal := k.bankKeeper.GetBalance(ctx, addr, "uion")
err = k.distrKeeper.FundCommunityPool(ctx, sdk.NewCoins(osmoBal, ionBal), addr)
if err != nil {
return err
}
}
}
}
return nil
}

Expand Down
Loading