├── .gitignore ├── README.md ├── app ├── app.go ├── encoding.go ├── export.go ├── genesis.go ├── params │ ├── encoding.go │ └── proto.go └── types.go ├── cmd └── metachaind │ ├── cmd │ ├── app.go │ ├── genaccounts.go │ └── root.go │ └── main.go ├── config.yml ├── decision_policies ├── README.md └── threshold.md ├── go.mod ├── go.sum ├── internal └── tools │ └── tools.go ├── proto ├── cosmos │ └── base │ │ └── query │ │ └── v1beta1 │ │ └── pagination.proto ├── dao │ └── v1beta │ │ ├── genesis.proto │ │ ├── query.proto │ │ ├── tx.proto │ │ └── types.proto └── group │ └── v1alpha1 │ ├── events.proto │ ├── genesis.proto │ ├── query.proto │ ├── tx.proto │ └── types.proto ├── questions.md ├── scripts └── protocgen ├── third_party └── proto │ ├── confio │ └── proofs.proto │ ├── cosmos_proto │ └── cosmos.proto │ ├── gogoproto │ └── gogo.proto │ ├── google │ ├── api │ │ ├── annotations.proto │ │ ├── http.proto │ │ └── httpbody.proto │ └── protobuf │ │ ├── any.proto │ │ └── descriptor.proto │ └── tendermint │ ├── abci │ └── types.proto │ ├── crypto │ ├── keys.proto │ └── proof.proto │ ├── libs │ └── bits │ │ └── types.proto │ ├── types │ ├── evidence.proto │ ├── params.proto │ ├── types.proto │ └── validator.proto │ └── version │ └── types.proto ├── use_cases ├── README.md ├── governance.md ├── multi-sig.md └── off-chain.md └── x ├── dao ├── client │ ├── cli │ │ ├── query.go │ │ └── tx.go │ └── rest │ │ └── rest.go ├── exported │ └── expected_keepers.go ├── genesis.go ├── handler.go ├── keeper │ ├── grpc_query.go │ ├── keeper.go │ └── querier.go ├── keys.go ├── module │ └── module.go ├── server │ └── server.go ├── spec │ ├── 00_diff.md │ ├── 01_concepts.md │ ├── 02_state.md │ ├── 03_messages.md │ ├── 04_events.md │ └── README.md └── types │ ├── codec.go │ ├── errors.go │ ├── genesis.go │ ├── genesis.pb.go │ ├── keys.go │ ├── querier.go │ ├── querier.pb.go │ └── types.go └── group ├── client ├── query.go ├── testsuite │ ├── query.go │ └── tx.go ├── tx.go └── util.go ├── codec.go ├── errors.go ├── events.pb.go ├── exported └── expected_keepers.go ├── genesis.go ├── genesis.pb.go ├── keys.go ├── module └── module.go ├── msgs.go ├── msgs_test.go ├── proposal.go ├── query.pb.go ├── query_cosmos.pb.go ├── server ├── genesis.go ├── handler.go ├── invariants.go ├── invariants_test.go ├── msg_server.go ├── operations.go ├── proposal_executor.go ├── query_server.go ├── server.go ├── server_test.go └── testsuite │ ├── genesis.go │ └── suite.go ├── simulation ├── genesis.go └── operations.go ├── spec ├── 00_diff.md ├── 01_concepts.md ├── 02_state.md ├── 03_messages.md ├── 04_events.md └── README.md ├── testdata ├── tx.go ├── tx.pb.go └── tx.proto ├── tx.pb.go ├── tx_cosmos.pb.go ├── types.go ├── types.pb.go ├── types_test.go └── typesupport.go /.gitignore: -------------------------------------------------------------------------------- 1 | frontend/node_modules 2 | frontend/dist 3 | frontend/.cache 4 | secret.yml 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metachain 2 | 3 | An experimental chain to explore governance typologies within the Cosmos 4 | 5 | > NOTE: Focus currently resides in design work of the spec. Implementation is currently 6 | > untouched. 7 | 8 | ## Contents 9 | 10 | - Background 11 | - [Groups/Gov Sync Discussion](https://github.com/cosmos/cosmos-sdk/discussions/9066) 12 | 13 | - Groups Module 14 | - [Proposed Spec](./x/group/spec/README.md) 15 | - [Concepts](./x/group/spec/01_concepts.md) 16 | - [State](./x/group/spec/02_state.md) 17 | - [Messages](./x/group/spec/03_messages.md) 18 | - [Events](./x/group/spec/04_events.md) 19 | - [Original Spec](https://github.com/regen-network/regen-ledger/tree/v1.0.0/x/group/spec) 20 | - [Diff](./x/group/spec/00_diff.md) 21 | 22 | - DAO Module 23 | - [Proposed Spec](./x/dao/spec/README.md) 24 | - [Concepts](./x/dao/spec/01_concepts.md) 25 | - [State](./x/dao/spec/02_state.md) 26 | - [Messages](./x/dao/spec/03_messages.md) 27 | - [Events](./x/dao/spec/04_events.md) 28 | - Derived from 29 | [Gov Spec](https://github.com/cosmos/cosmos-sdk/tree/v0.42.4/x/gov/spec) 30 | - [Diff](./x/dao/spec/00_diff.md) 31 | 32 | - [Use Cases](./use_cases/README.md) 33 | 34 | - [Decision Policies](./decision_policies/README.md) 35 | 36 | - [Questions and Unknowns](./questions.md) 37 | 38 | - Resources 39 | - [PolicyKit](https://policykit.readthedocs.io/en/latest/index.html) 40 | - [Colony](https://colony.io/) 41 | - [Gnosis Safe](https://gnosis-safe.io/) 42 | - [Commonwealth](https://commonwealth.im/) 43 | 44 | --- 45 | 46 | Generated by [Starport](https://github.com/tendermint/starport) -------------------------------------------------------------------------------- /app/encoding.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/std" 5 | "github.com/interchainberlin/metachain/app/params" 6 | ) 7 | 8 | // MakeEncodingConfig creates an EncodingConfig for testing 9 | func MakeEncodingConfig() params.EncodingConfig { 10 | encodingConfig := params.MakeEncodingConfig() 11 | std.RegisterLegacyAminoCodec(encodingConfig.Amino) 12 | std.RegisterInterfaces(encodingConfig.InterfaceRegistry) 13 | ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) 14 | ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) 15 | return encodingConfig 16 | } 17 | -------------------------------------------------------------------------------- /app/export.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 8 | 9 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" 12 | "github.com/cosmos/cosmos-sdk/x/staking" 13 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 14 | ) 15 | 16 | // ExportAppStateAndValidators exports the state of the application for a genesis 17 | // file. 18 | func (app *App) ExportAppStateAndValidators( 19 | forZeroHeight bool, jailAllowedAddrs []string, 20 | ) (servertypes.ExportedApp, error) { 21 | 22 | // as if they could withdraw from the start of the next block 23 | ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) 24 | 25 | // We export at last height + 1, because that's the height at which 26 | // Tendermint will start InitChain. 27 | height := app.LastBlockHeight() + 1 28 | if forZeroHeight { 29 | height = 0 30 | app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) 31 | } 32 | 33 | genState := app.mm.ExportGenesis(ctx, app.appCodec) 34 | appState, err := json.MarshalIndent(genState, "", " ") 35 | if err != nil { 36 | return servertypes.ExportedApp{}, err 37 | } 38 | 39 | validators, err := staking.WriteValidators(ctx, app.StakingKeeper) 40 | if err != nil { 41 | return servertypes.ExportedApp{}, err 42 | } 43 | 44 | return servertypes.ExportedApp{ 45 | AppState: appState, 46 | Validators: validators, 47 | Height: height, 48 | ConsensusParams: app.BaseApp.GetConsensusParams(ctx), 49 | }, nil 50 | } 51 | 52 | // prepare for fresh start at zero height 53 | // NOTE zero height genesis is a temporary feature which will be deprecated 54 | // in favour of export at a block height 55 | func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { 56 | applyAllowedAddrs := false 57 | 58 | // check if there is a allowed address list 59 | if len(jailAllowedAddrs) > 0 { 60 | applyAllowedAddrs = true 61 | } 62 | 63 | allowedAddrsMap := make(map[string]bool) 64 | 65 | for _, addr := range jailAllowedAddrs { 66 | _, err := sdk.ValAddressFromBech32(addr) 67 | if err != nil { 68 | log.Fatal(err) 69 | } 70 | allowedAddrsMap[addr] = true 71 | } 72 | 73 | /* Just to be safe, assert the invariants on current state. */ 74 | app.CrisisKeeper.AssertInvariants(ctx) 75 | 76 | /* Handle fee distribution state. */ 77 | 78 | // withdraw all validator commission 79 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { 80 | _, err := app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) 81 | if err != nil { 82 | panic(err) 83 | } 84 | return false 85 | }) 86 | 87 | // withdraw all delegator rewards 88 | dels := app.StakingKeeper.GetAllDelegations(ctx) 89 | for _, delegation := range dels { 90 | _, err := app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr()) 91 | if err != nil { 92 | panic(err) 93 | } 94 | } 95 | 96 | // clear validator slash events 97 | app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) 98 | 99 | // clear validator historical rewards 100 | app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) 101 | 102 | // set context height to zero 103 | height := ctx.BlockHeight() 104 | ctx = ctx.WithBlockHeight(0) 105 | 106 | // reinitialize all validators 107 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { 108 | // donate any unwithdrawn outstanding reward fraction tokens to the community pool 109 | scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) 110 | feePool := app.DistrKeeper.GetFeePool(ctx) 111 | feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) 112 | app.DistrKeeper.SetFeePool(ctx, feePool) 113 | 114 | app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) 115 | return false 116 | }) 117 | 118 | // reinitialize all delegations 119 | for _, del := range dels { 120 | app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) 121 | app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) 122 | } 123 | 124 | // reset context height 125 | ctx = ctx.WithBlockHeight(height) 126 | 127 | /* Handle staking state. */ 128 | 129 | // iterate through redelegations, reset creation height 130 | app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { 131 | for i := range red.Entries { 132 | red.Entries[i].CreationHeight = 0 133 | } 134 | app.StakingKeeper.SetRedelegation(ctx, red) 135 | return false 136 | }) 137 | 138 | // iterate through unbonding delegations, reset creation height 139 | app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { 140 | for i := range ubd.Entries { 141 | ubd.Entries[i].CreationHeight = 0 142 | } 143 | app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) 144 | return false 145 | }) 146 | 147 | // Iterate through validators by power descending, reset bond heights, and 148 | // update bond intra-tx counters. 149 | store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) 150 | iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) 151 | counter := int16(0) 152 | 153 | for ; iter.Valid(); iter.Next() { 154 | addr := sdk.ValAddress(iter.Key()[1:]) 155 | validator, found := app.StakingKeeper.GetValidator(ctx, addr) 156 | if !found { 157 | panic("expected validator, not found") 158 | } 159 | 160 | validator.UnbondingHeight = 0 161 | if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { 162 | validator.Jailed = true 163 | } 164 | 165 | app.StakingKeeper.SetValidator(ctx, validator) 166 | counter++ 167 | } 168 | 169 | iter.Close() 170 | 171 | _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) 172 | if err != nil { 173 | panic(err) 174 | } 175 | 176 | /* Handle slashing state. */ 177 | 178 | // reset start height on signing infos 179 | app.SlashingKeeper.IterateValidatorSigningInfos( 180 | ctx, 181 | func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { 182 | info.StartHeight = 0 183 | app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) 184 | return false 185 | }, 186 | ) 187 | } 188 | 189 | -------------------------------------------------------------------------------- /app/genesis.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // The genesis state of the blockchain is represented here as a map of raw json 8 | // messages key'd by a identifier string. 9 | // The identifier is used to determine which module genesis information belongs 10 | // to so it may be appropriately routed during init chain. 11 | // Within this application default genesis information is retrieved from 12 | // the ModuleBasicManager which populates json from each BasicModule 13 | // object provided to it during init. 14 | type GenesisState map[string]json.RawMessage 15 | 16 | // NewDefaultGenesisState generates the default state for the application. 17 | func NewDefaultGenesisState() GenesisState { 18 | encCfg := MakeEncodingConfig() 19 | return ModuleBasics.DefaultGenesis(encCfg.Marshaler) 20 | } 21 | -------------------------------------------------------------------------------- /app/params/encoding.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/client" 5 | "github.com/cosmos/cosmos-sdk/codec" 6 | "github.com/cosmos/cosmos-sdk/codec/types" 7 | ) 8 | 9 | // EncodingConfig specifies the concrete encoding types to use for a given app. 10 | // This is provided for compatibility between protobuf and amino implementations. 11 | type EncodingConfig struct { 12 | InterfaceRegistry types.InterfaceRegistry 13 | Marshaler codec.Marshaler 14 | TxConfig client.TxConfig 15 | Amino *codec.LegacyAmino 16 | } 17 | -------------------------------------------------------------------------------- /app/params/proto.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | "github.com/cosmos/cosmos-sdk/codec/types" 6 | "github.com/cosmos/cosmos-sdk/x/auth/tx" 7 | ) 8 | 9 | // MakeEncodingConfig creates an EncodingConfig for an amino based test configuration. 10 | func MakeEncodingConfig() EncodingConfig { 11 | amino := codec.NewLegacyAmino() 12 | interfaceRegistry := types.NewInterfaceRegistry() 13 | marshaler := codec.NewProtoCodec(interfaceRegistry) 14 | txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) 15 | 16 | return EncodingConfig{ 17 | InterfaceRegistry: interfaceRegistry, 18 | Marshaler: marshaler, 19 | TxConfig: txCfg, 20 | Amino: amino, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/types.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | abci "github.com/tendermint/tendermint/abci/types" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | // App implements the common methods for a Cosmos SDK-based application 12 | // specific blockchain. 13 | type CosmosApp interface { 14 | // The assigned name of the app. 15 | Name() string 16 | 17 | // The application types codec. 18 | // NOTE: This shoult be sealed before being returned. 19 | LegacyAmino() *codec.LegacyAmino 20 | 21 | // Application updates every begin block. 22 | BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock 23 | 24 | // Application updates every end block. 25 | EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock 26 | 27 | // Application update at chain (i.e app) initialization. 28 | InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain 29 | 30 | // Loads the app at a given height. 31 | LoadHeight(height int64) error 32 | 33 | // Exports the state of the application for a genesis file. 34 | ExportAppStateAndValidators( 35 | forZeroHeight bool, jailAllowedAddrs []string, 36 | ) (servertypes.ExportedApp, error) 37 | 38 | // All the registered module account addreses. 39 | ModuleAccountAddrs() map[string]bool 40 | } 41 | 42 | -------------------------------------------------------------------------------- /cmd/metachaind/cmd/app.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | const appName = "metachain" 4 | -------------------------------------------------------------------------------- /cmd/metachaind/cmd/genaccounts.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/spf13/cobra" 10 | 11 | "github.com/cosmos/cosmos-sdk/client" 12 | "github.com/cosmos/cosmos-sdk/client/flags" 13 | "github.com/cosmos/cosmos-sdk/codec" 14 | "github.com/cosmos/cosmos-sdk/crypto/keyring" 15 | "github.com/cosmos/cosmos-sdk/server" 16 | sdk "github.com/cosmos/cosmos-sdk/types" 17 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 18 | authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" 19 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 20 | "github.com/cosmos/cosmos-sdk/x/genutil" 21 | genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" 22 | ) 23 | 24 | const ( 25 | flagVestingStart = "vesting-start-time" 26 | flagVestingEnd = "vesting-end-time" 27 | flagVestingAmt = "vesting-amount" 28 | ) 29 | 30 | // AddGenesisAccountCmd returns add-genesis-account cobra Command. 31 | func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command { 32 | cmd := &cobra.Command{ 33 | Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", 34 | Short: "Add a genesis account to genesis.json", 35 | Long: `Add a genesis account to genesis.json. The provided account must specify 36 | the account address or key name and a list of initial coins. If a key name is given, 37 | the address will be looked up in the local Keybase. The list of initial tokens must 38 | contain valid denominations. Accounts may optionally be supplied with vesting parameters. 39 | `, 40 | Args: cobra.ExactArgs(2), 41 | RunE: func(cmd *cobra.Command, args []string) error { 42 | clientCtx := client.GetClientContextFromCmd(cmd) 43 | depCdc := clientCtx.JSONMarshaler 44 | cdc := depCdc.(codec.Marshaler) 45 | 46 | serverCtx := server.GetServerContextFromCmd(cmd) 47 | config := serverCtx.Config 48 | 49 | config.SetRoot(clientCtx.HomeDir) 50 | 51 | addr, err := sdk.AccAddressFromBech32(args[0]) 52 | if err != nil { 53 | inBuf := bufio.NewReader(cmd.InOrStdin()) 54 | keyringBackend, err := cmd.Flags().GetString(flags.FlagKeyringBackend) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | // attempt to lookup address from Keybase if no address was provided 60 | kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | info, err := kb.Key(args[0]) 66 | if err != nil { 67 | return fmt.Errorf("failed to get address from Keybase: %w", err) 68 | } 69 | 70 | addr = info.GetAddress() 71 | } 72 | 73 | coins, err := sdk.ParseCoinsNormalized(args[1]) 74 | if err != nil { 75 | return fmt.Errorf("failed to parse coins: %w", err) 76 | } 77 | 78 | vestingStart, err := cmd.Flags().GetInt64(flagVestingStart) 79 | if err != nil { 80 | return err 81 | } 82 | vestingEnd, err := cmd.Flags().GetInt64(flagVestingEnd) 83 | if err != nil { 84 | return err 85 | } 86 | vestingAmtStr, err := cmd.Flags().GetString(flagVestingAmt) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr) 92 | if err != nil { 93 | return fmt.Errorf("failed to parse vesting amount: %w", err) 94 | } 95 | 96 | // create concrete account type based on input parameters 97 | var genAccount authtypes.GenesisAccount 98 | 99 | balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} 100 | baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) 101 | 102 | if !vestingAmt.IsZero() { 103 | baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) 104 | 105 | if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || 106 | baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { 107 | return errors.New("vesting amount cannot be greater than total amount") 108 | } 109 | 110 | switch { 111 | case vestingStart != 0 && vestingEnd != 0: 112 | genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) 113 | 114 | case vestingEnd != 0: 115 | genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) 116 | 117 | default: 118 | return errors.New("invalid vesting parameters; must supply start and end time or end time") 119 | } 120 | } else { 121 | genAccount = baseAccount 122 | } 123 | 124 | if err := genAccount.Validate(); err != nil { 125 | return fmt.Errorf("failed to validate new genesis account: %w", err) 126 | } 127 | 128 | genFile := config.GenesisFile() 129 | appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) 130 | if err != nil { 131 | return fmt.Errorf("failed to unmarshal genesis state: %w", err) 132 | } 133 | 134 | authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) 135 | 136 | accs, err := authtypes.UnpackAccounts(authGenState.Accounts) 137 | if err != nil { 138 | return fmt.Errorf("failed to get accounts from any: %w", err) 139 | } 140 | 141 | if accs.Contains(addr) { 142 | return fmt.Errorf("cannot add account at existing address %s", addr) 143 | } 144 | 145 | // Add the new account to the set of genesis accounts and sanitize the 146 | // accounts afterwards. 147 | accs = append(accs, genAccount) 148 | accs = authtypes.SanitizeGenesisAccounts(accs) 149 | 150 | genAccs, err := authtypes.PackAccounts(accs) 151 | if err != nil { 152 | return fmt.Errorf("failed to convert accounts into any's: %w", err) 153 | } 154 | authGenState.Accounts = genAccs 155 | 156 | authGenStateBz, err := cdc.MarshalJSON(&authGenState) 157 | if err != nil { 158 | return fmt.Errorf("failed to marshal auth genesis state: %w", err) 159 | } 160 | 161 | appState[authtypes.ModuleName] = authGenStateBz 162 | 163 | bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState) 164 | bankGenState.Balances = append(bankGenState.Balances, balances) 165 | bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) 166 | 167 | bankGenStateBz, err := cdc.MarshalJSON(bankGenState) 168 | if err != nil { 169 | return fmt.Errorf("failed to marshal bank genesis state: %w", err) 170 | } 171 | 172 | appState[banktypes.ModuleName] = bankGenStateBz 173 | 174 | appStateJSON, err := json.Marshal(appState) 175 | if err != nil { 176 | return fmt.Errorf("failed to marshal application genesis state: %w", err) 177 | } 178 | 179 | genDoc.AppState = appStateJSON 180 | return genutil.ExportGenesisFile(genDoc, genFile) 181 | }, 182 | } 183 | 184 | cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") 185 | cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") 186 | cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") 187 | cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") 188 | flags.AddQueryFlagsToCmd(cmd) 189 | 190 | return cmd 191 | } 192 | -------------------------------------------------------------------------------- /cmd/metachaind/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/cosmos/cosmos-sdk/codec" 10 | "github.com/cosmos/cosmos-sdk/snapshots" 11 | "github.com/interchainberlin/metachain/app/params" 12 | 13 | "github.com/spf13/cast" 14 | "github.com/spf13/cobra" 15 | tmcli "github.com/tendermint/tendermint/libs/cli" 16 | "github.com/tendermint/tendermint/libs/log" 17 | dbm "github.com/tendermint/tm-db" 18 | 19 | "github.com/cosmos/cosmos-sdk/baseapp" 20 | "github.com/cosmos/cosmos-sdk/client" 21 | "github.com/cosmos/cosmos-sdk/client/debug" 22 | "github.com/cosmos/cosmos-sdk/client/flags" 23 | "github.com/cosmos/cosmos-sdk/client/keys" 24 | "github.com/cosmos/cosmos-sdk/client/rpc" 25 | "github.com/cosmos/cosmos-sdk/server" 26 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 27 | "github.com/cosmos/cosmos-sdk/store" 28 | sdk "github.com/cosmos/cosmos-sdk/types" 29 | authclient "github.com/cosmos/cosmos-sdk/x/auth/client" 30 | authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" 31 | "github.com/cosmos/cosmos-sdk/x/auth/types" 32 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 33 | genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" 34 | "github.com/interchainberlin/metachain/app" 35 | ) 36 | 37 | // NewRootCmd creates a new root command for simd. It is called once in the 38 | // main function. 39 | func NewRootCmd() (*cobra.Command, params.EncodingConfig) { 40 | encodingConfig := app.MakeEncodingConfig() 41 | initClientCtx := client.Context{}. 42 | WithJSONMarshaler(encodingConfig.Marshaler). 43 | WithInterfaceRegistry(encodingConfig.InterfaceRegistry). 44 | WithTxConfig(encodingConfig.TxConfig). 45 | WithLegacyAmino(encodingConfig.Amino). 46 | WithInput(os.Stdin). 47 | WithAccountRetriever(types.AccountRetriever{}). 48 | WithBroadcastMode(flags.BroadcastBlock). 49 | WithHomeDir(app.DefaultNodeHome(appName)) 50 | 51 | rootCmd := &cobra.Command{ 52 | Use: appName, 53 | Short: "Stargate CosmosHub App", 54 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { 55 | if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { 56 | return err 57 | } 58 | 59 | return server.InterceptConfigsPreRunHandler(cmd) 60 | }, 61 | } 62 | 63 | initRootCmd(rootCmd, encodingConfig) 64 | 65 | return rootCmd, encodingConfig 66 | } 67 | 68 | // Execute executes the root command. 69 | func Execute(rootCmd *cobra.Command) error { 70 | // Create and set a client.Context on the command's Context. During the pre-run 71 | // of the root command, a default initialized client.Context is provided to 72 | // seed child command execution with values such as AccountRetriver, Keyring, 73 | // and a Tendermint RPC. This requires the use of a pointer reference when 74 | // getting and setting the client.Context. Ideally, we utilize 75 | // https://github.com/spf13/cobra/pull/1118. 76 | ctx := context.Background() 77 | ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) 78 | ctx = context.WithValue(ctx, server.ServerContextKey, server.NewDefaultContext()) 79 | 80 | executor := tmcli.PrepareBaseCmd(rootCmd, "", app.DefaultNodeHome(appName)) 81 | return executor.ExecuteContext(ctx) 82 | } 83 | 84 | func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { 85 | authclient.Codec = encodingConfig.Marshaler 86 | 87 | rootCmd.AddCommand( 88 | genutilcli.InitCmd(app.ModuleBasics, app.DefaultNodeHome(appName)), 89 | genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome(appName)), 90 | genutilcli.MigrateGenesisCmd(), 91 | genutilcli.GenTxCmd(app.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome(appName)), 92 | genutilcli.ValidateGenesisCmd(app.ModuleBasics), 93 | AddGenesisAccountCmd(app.DefaultNodeHome(appName)), 94 | tmcli.NewCompletionCmd(rootCmd, true), 95 | debug.Cmd(), 96 | ) 97 | 98 | server.AddCommands(rootCmd, app.DefaultNodeHome(appName), newApp, createSimappAndExport, func(startCmd *cobra.Command) {}) 99 | 100 | // add keybase, auxiliary RPC, query, and tx child commands 101 | rootCmd.AddCommand( 102 | rpc.StatusCommand(), 103 | queryCommand(), 104 | txCommand(), 105 | keys.Commands(app.DefaultNodeHome(appName)), 106 | ) 107 | } 108 | 109 | func queryCommand() *cobra.Command { 110 | cmd := &cobra.Command{ 111 | Use: "query", 112 | Aliases: []string{"q"}, 113 | Short: "Querying subcommands", 114 | DisableFlagParsing: true, 115 | SuggestionsMinimumDistance: 2, 116 | RunE: client.ValidateCmd, 117 | } 118 | 119 | cmd.AddCommand( 120 | authcmd.GetAccountCmd(), 121 | rpc.ValidatorCommand(), 122 | rpc.BlockCommand(), 123 | authcmd.QueryTxsByEventsCmd(), 124 | authcmd.QueryTxCmd(), 125 | ) 126 | 127 | app.ModuleBasics.AddQueryCommands(cmd) 128 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") 129 | 130 | return cmd 131 | } 132 | 133 | func txCommand() *cobra.Command { 134 | cmd := &cobra.Command{ 135 | Use: "tx", 136 | Short: "Transactions subcommands", 137 | DisableFlagParsing: true, 138 | SuggestionsMinimumDistance: 2, 139 | RunE: client.ValidateCmd, 140 | } 141 | 142 | cmd.AddCommand( 143 | authcmd.GetSignCommand(), 144 | authcmd.GetSignBatchCommand(), 145 | authcmd.GetMultiSignCommand(), 146 | authcmd.GetValidateSignaturesCommand(), 147 | flags.LineBreak, 148 | authcmd.GetBroadcastCommand(), 149 | authcmd.GetEncodeCommand(), 150 | authcmd.GetDecodeCommand(), 151 | flags.LineBreak, 152 | ) 153 | 154 | app.ModuleBasics.AddTxCommands(cmd) 155 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") 156 | 157 | return cmd 158 | } 159 | 160 | func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { 161 | var cache sdk.MultiStorePersistentCache 162 | 163 | if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { 164 | cache = store.NewCommitKVStoreCacheManager() 165 | } 166 | 167 | skipUpgradeHeights := make(map[int64]bool) 168 | for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { 169 | skipUpgradeHeights[int64(h)] = true 170 | } 171 | 172 | pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) 173 | if err != nil { 174 | panic(err) 175 | } 176 | 177 | snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") 178 | snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir) 179 | if err != nil { 180 | panic(err) 181 | } 182 | snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir) 183 | if err != nil { 184 | panic(err) 185 | } 186 | 187 | return app.New( 188 | appName, logger, db, traceStore, true, skipUpgradeHeights, 189 | cast.ToString(appOpts.Get(flags.FlagHome)), 190 | cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), 191 | app.MakeEncodingConfig(), // Ideally, we would reuse the one created by NewRootCmd. 192 | baseapp.SetPruning(pruningOpts), 193 | baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), 194 | baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), 195 | baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), 196 | baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), 197 | baseapp.SetInterBlockCache(cache), 198 | baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), 199 | baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), 200 | baseapp.SetSnapshotStore(snapshotStore), 201 | baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval))), 202 | baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent))), 203 | ) 204 | } 205 | 206 | func createSimappAndExport( 207 | logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, 208 | appOpts servertypes.AppOptions, 209 | ) (servertypes.ExportedApp, error) { 210 | 211 | encCfg := app.MakeEncodingConfig() // Ideally, we would reuse the one created by NewRootCmd. 212 | encCfg.Marshaler = codec.NewProtoCodec(encCfg.InterfaceRegistry) 213 | var a *app.App 214 | if height != -1 { 215 | a = app.New(appName, logger, db, traceStore, false, map[int64]bool{}, "", uint(1), encCfg) 216 | 217 | if err := a.LoadHeight(height); err != nil { 218 | return servertypes.ExportedApp{}, err 219 | } 220 | } else { 221 | a = app.New(appName, logger, db, traceStore, true, map[int64]bool{}, "", uint(1), encCfg) 222 | } 223 | 224 | return a.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) 225 | } 226 | -------------------------------------------------------------------------------- /cmd/metachaind/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/interchainberlin/metachain/cmd/metachaind/cmd" 7 | ) 8 | 9 | func main() { 10 | rootCmd, _ := cmd.NewRootCmd() 11 | if err := cmd.Execute(rootCmd); err != nil { 12 | os.Exit(1) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | accounts: 3 | - name: user1 4 | coins: ["1000token", "100000000stake"] 5 | - name: user2 6 | coins: ["500token"] 7 | validator: 8 | name: user1 9 | staked: "100000000stake" 10 | -------------------------------------------------------------------------------- /decision_policies/README.md: -------------------------------------------------------------------------------- 1 | # Decision Policies 2 | 3 | This directory hosts are set of decision policy algorithms that can be deployed 4 | by dao groups. 5 | 6 | ## Contents 7 | 8 | - [Threshold](./threshold.md) -------------------------------------------------------------------------------- /decision_policies/threshold.md: -------------------------------------------------------------------------------- 1 | # Threshold Decision Policy -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/interchainberlin/metachain 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/cockroachdb/apd/v2 v2.0.2 7 | github.com/cosmos/cosmos-sdk v0.42.4 8 | github.com/gogo/protobuf v1.3.3 9 | github.com/google/go-cmp v0.5.4 // indirect 10 | github.com/gorilla/mux v1.8.0 11 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 12 | github.com/regen-network/cosmos-proto v0.3.1 13 | github.com/regen-network/regen-ledger/orm v0.0.0-20210511173659-ca20342aa853 14 | github.com/regen-network/regen-ledger/types v0.0.0-20210511173659-ca20342aa853 15 | github.com/spf13/cast v1.3.1 16 | github.com/spf13/cobra v1.1.3 17 | github.com/stretchr/testify v1.7.0 18 | github.com/tendermint/tendermint v0.34.10 19 | github.com/tendermint/tm-db v0.6.4 20 | google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea // indirect 21 | google.golang.org/grpc v1.37.0 22 | ) 23 | 24 | replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alpha.regen.4 25 | -------------------------------------------------------------------------------- /internal/tools/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/regen-network/cosmos-proto/protoc-gen-gocosmos" 7 | ) 8 | -------------------------------------------------------------------------------- /proto/cosmos/base/query/v1beta1/pagination.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package cosmos.base.query.v1beta1; 3 | 4 | option go_package = "github.com/cosmos/cosmos-sdk/types/query"; 5 | 6 | // PageRequest is to be embedded in gRPC request messages for efficient 7 | // pagination. Ex: 8 | // 9 | // message SomeRequest { 10 | // Foo some_parameter = 1; 11 | // PageRequest pagination = 2; 12 | // } 13 | message PageRequest { 14 | // key is a value returned in PageResponse.next_key to begin 15 | // querying the next page most efficiently. Only one of offset or key 16 | // should be set. 17 | bytes key = 1; 18 | 19 | // offset is a numeric offset that can be used when key is unavailable. 20 | // It is less efficient than using key. Only one of offset or key should 21 | // be set. 22 | uint64 offset = 2; 23 | 24 | // limit is the total number of results to be returned in the result page. 25 | // If left empty it will default to a value to be set by each app. 26 | uint64 limit = 3; 27 | 28 | // count_total is set to true to indicate that the result set should include 29 | // a count of the total number of items available for pagination in UIs. count_total 30 | // is only respected when offset is used. It is ignored when key is set. 31 | bool count_total = 4; 32 | } 33 | 34 | // PageResponse is to be embedded in gRPC response messages where the corresponding 35 | // request message has used PageRequest. 36 | // 37 | // message SomeResponse { 38 | // repeated Bar results = 1; 39 | // PageResponse page = 2; 40 | // } 41 | message PageResponse { 42 | // next_key is the key to be passed to PageRequest.key to 43 | // query the next page most efficiently 44 | bytes next_key = 1; 45 | 46 | // total is total number of results available if PageRequest.count_total 47 | // was set, its value is undefined otherwise 48 | uint64 total = 2; 49 | } 50 | -------------------------------------------------------------------------------- /proto/dao/v1beta/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package metachain.dao.v1beta1; 3 | 4 | option go_package = "github.com/interchainberlin/metachain/x/dao"; 5 | 6 | // GenesisState defines the capability module's genesis state. 7 | message GenesisState { 8 | } 9 | 10 | -------------------------------------------------------------------------------- /proto/dao/v1beta/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package metachain.dao.v1beta1; 3 | 4 | import "cosmos/base/query/v1beta1/pagination.proto"; 5 | // this line is used by starport scaffolding # 1 6 | 7 | 8 | option go_package = "github.com/interchainberlin/metachain/x/dao"; 9 | 10 | // Query defines the gRPC querier service. 11 | service Query { 12 | // this line is used by starport scaffolding # 2 13 | } 14 | 15 | // this line is used by starport scaffolding # 3 16 | -------------------------------------------------------------------------------- /proto/dao/v1beta/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package metachain.dao.v1beta1; 3 | 4 | import "cosmos/base/v1beta1/coin.proto"; 5 | 6 | option go_package = "github.com/interchainberlin/metachain/x/dao"; 7 | 8 | service Msg { 9 | rpc CreatePolity(MsgCreatePolityRequest) returns (MsgCreatePolityResponse); 10 | 11 | rpc UpdatePolity(MsgUpdatePolityRequest) returns (MsgUpdatePolityResponse); 12 | 13 | rpc DestroyPolity(MsgDestroyPolityRequest) returns (MsgDestroyPolityResponse); 14 | 15 | rpc CreateProposal(MsgCreateProposalRequest) returns (MsgCreateProposalResponse); 16 | 17 | rpc AmendProposal(MsgAmendProposalRequest) returns (MsgAmendProposalResponse); 18 | 19 | rpc WithdrawProposal(MsgWithdrawProposalRequest) returns (MsgWithdrawProposalResponse); 20 | 21 | rpc Vote(MsgVoteRequest) returns (MsgVoteResponse); 22 | 23 | rpc Delegate(MsgDelegateRequest) returns (MsgDelegateResponse); 24 | 25 | rpc Undelegate(MsgUndelegateRequest) returns (MsgUndelegateResponse); 26 | 27 | rpc Exec(MsgExecRequest) returns (MsgExecResponse); 28 | } 29 | 30 | message MsgCreatePolityRequest { 31 | uint64 group_id = 1; 32 | 33 | // decision_policy is the updated group account decision policy. 34 | google.protobuf.Any decision_policy = 2 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 35 | 36 | // proposal_filter filters incoming proposals. 37 | google.protobuf.Any proposal_filter = 3 [(cosmos_proto.accepts_interface) = "ProposalFilter"]; 38 | 39 | // the voting window for each proposal 40 | google.protobuf.Duration voting_period = 4; 41 | 42 | // indicates whether proposals can be received from anyone or members only 43 | bool public = 5; 44 | 45 | // an optional parent polity which has administative power over the polity 46 | string parent_address = 6; 47 | } 48 | 49 | message MsgCreateProposalResponse { 50 | // the collective address of the polity 51 | string address = 1; 52 | } 53 | 54 | message MsgUpdatePolityRequest { 55 | string address = 1; 56 | 57 | // decision_policy is the updated group account decision policy. 58 | google.protobuf.Any decision_policy = 2 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 59 | 60 | // proposal_filter filters incoming proposals. 61 | google.protobuf.Any proposal_filter = 3 [(cosmos_proto.accepts_interface) = "ProposalFilter"]; 62 | 63 | // the voting window for each proposal 64 | google.protobuf.Duration voting_period = 4; 65 | 66 | // indicates whether proposals can be received from anyone or members only 67 | bool public = 5; 68 | 69 | // an optional parent polity which has administative power over the polity 70 | string parent_address = 6; 71 | } 72 | 73 | message MsgUpdatePolityResponse{} 74 | 75 | message MsgDestroyPolityRequest{ 76 | string address = 1; 77 | } 78 | 79 | message MsgUpdatePolityResponse{} 80 | 81 | // MsgCreateProposalRequest is the Msg/CreateProposal request type. 82 | message MsgCreateProposalRequest { 83 | option (gogoproto.goproto_getters) = false; 84 | 85 | // group address is the group account address. 86 | string address = 1; 87 | 88 | // proposer is the account address of the proposers. 89 | string proposer = 2; 90 | 91 | // the deposit associated with the proposal 92 | repeated cosmos.base.v1beta1.Coin deposit = 3; 93 | 94 | // content represents some endpoint which can lead 95 | // a discussion / forum or simple state the case for the proposal 96 | string content = 5; 97 | 98 | // metadata is any arbitrary metadata to attached to the proposal. 99 | bytes metadata = 6; 100 | 101 | // messages is a list of Msgs that will be executed if the proposal passes. 102 | repeated google.protobuf.Any messages = 7; 103 | } 104 | 105 | // MsgCreateProposalResponse is the Msg/CreateProposal response type. 106 | message MsgCreateProposalResponse { 107 | 108 | // proposal is the unique ID of the proposal. 109 | uint64 proposal_id = 1; 110 | } 111 | 112 | message MsgAmendProposalRequest { 113 | // unique id of the proposal 114 | uint64 proposal_id = 1; 115 | 116 | // content represents some endpoint which can lead 117 | // a discussion / forum or simple state the case for // the proposal 118 | string content = 2; 119 | 120 | // metadata is any arbitrary metadata to attached to the proposal. 121 | bytes metadata = 3; 122 | 123 | // messages is a list of Msgs that will be executed if the proposal passes. 124 | repeated google.protobuf.Any messages = 4; 125 | } 126 | 127 | message MsgAmendProposalResponse {} 128 | 129 | message MsgWithdrawProposalRequest { 130 | uint64 proposal_id = 1; 131 | } 132 | 133 | message MsgWithdrawProposalResponse {} 134 | 135 | // MsgVoteRequest is the Msg/Vote request type. 136 | message MsgVoteRequest { 137 | // proposal is the unique ID of the proposal. 138 | uint64 proposal_id = 1; 139 | 140 | // voter is the voter's address. 141 | string voter = 2; 142 | 143 | // choice is the voter's choice on the proposal. 144 | Choice choice = 3; 145 | 146 | // the weight behind their choice. This defaults to the entire weight 147 | // of the member if none is described. This allows for split voting. 148 | string weight = 4; 149 | } 150 | 151 | // MsgVoteResponse is the Msg/Vote response type. 152 | message MsgVoteResponse { } 153 | 154 | message MsgDelegateRequest { 155 | // the account transferring their voting power 156 | string delegator = 1; 157 | 158 | // the receiving of the voting power 159 | string candidate = 2; 160 | 161 | // the weight they are transferring the delegation to 162 | string weight = 3; 163 | } 164 | 165 | message MsgDelegateResponse {} 166 | 167 | message MsgUndelegateRequest { 168 | // the delegator 169 | string delegator = 1; 170 | 171 | // the address being removed of the delegation 172 | string candidate = 2; 173 | 174 | // the weight they are transferring the delegation to 175 | string weight = 3; 176 | } 177 | 178 | message MsgUndelegateResponse {} 179 | 180 | // MsgExecRequest is the Msg/Exec request type. 181 | message MsgExecRequest { 182 | 183 | // proposal is the unique ID of the proposal. 184 | uint64 proposal_id = 1; 185 | 186 | // signer is the account address used to execute the proposal. 187 | string signer = 2; 188 | } 189 | 190 | // MsgExecResponse is the Msg/Exec request type. 191 | message MsgExecResponse {} -------------------------------------------------------------------------------- /proto/dao/v1beta/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package metachain.dao.v1beta1; 3 | 4 | option go_package = "github.com/interchainberlin/metachain/x/dao"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "google/protobuf/duration.proto"; 8 | import "google/protobuf/timestamp.proto"; 9 | import "cosmos_proto/cosmos.proto"; 10 | import "google/protobuf/any.proto"; 11 | 12 | // A polity is an account governed by a group 13 | message Polity { 14 | // the account or address that collectively represents the polity 15 | string account = 1; 16 | 17 | // the group interface for reading member sets 18 | google.protobuf.Any group = 2 [(cosmos_proto.accepts_interface) = "Group"]; 19 | 20 | // decision_policy is the updated group account decision policy. 21 | google.protobuf.Any decision_policy = 3 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 22 | 23 | // proposal_filter filters incoming proposals. 24 | google.protobuf.Any proposal_filter = 4 [(cosmos_proto.accepts_interface) = "ProposalFilter"]; 25 | 26 | // the voting window for each proposal 27 | google.protobuf.Duration voting_period = 5; 28 | 29 | // indicates whether proposals can be received from anyone or members only 30 | bool public = 6; 31 | 32 | // indicates which account has control over the polity i.e. changing 33 | // the decision policy or the proposal filter. This allows for nested polity's 34 | // where a parent polity has partial governance over this polity. Leave it empty 35 | // to indicate that the polity is soverign 36 | string parent = 7; 37 | } 38 | 39 | // Proposal defines a group proposal. 40 | message Proposal { 41 | // the unique id of the proposal 42 | uint64 proposal_id = 1; 43 | 44 | // the account proposing the action 45 | string proposer = 2; 46 | 47 | // the group account that will execute the action, define 48 | // the members eligible to vote as well as the proposal filter 49 | // and decision policy involved with the proposal 50 | string group_address = 3; 51 | 52 | // the deposit associated with the proposal 53 | repeated sdk.Coin deposit = 4; 54 | 55 | // usually a url making the case for the proposal 56 | string content = 5; 57 | 58 | // additional meta data that might be used as part of 59 | // decision policy 60 | bytes metadata = 6; 61 | 62 | // an array of messages to be executed upon acceptance 63 | repeated sdk.Msg messages = 7; 64 | 65 | // When the proposal finishes 66 | google.protobuf.Time deadline = 8; 67 | 68 | // The current tally of the vote 69 | Tally tally = 9; 70 | 71 | // The final outcome of the proposal 72 | Choice outcome = 9; 73 | } 74 | 75 | message Tally { 76 | // the proposal the tally is tracking 77 | uint64 proposal_id = 1; 78 | 79 | // votes is an array of the choices made by group members 80 | repeated Choice votes = 2; 81 | 82 | // relevant meta data. This is useful for caching infomation like the total 83 | // accumulated weight for each choice 84 | bytes meta_data = 3; 85 | } 86 | 87 | // Vote represents a vote for a proposal. 88 | message Vote { 89 | // the member of the group that voted 90 | string voter = 1; 91 | 92 | // the proposal that they are voting on 93 | uint64 proposal_id = 2; 94 | 95 | // their choice 96 | Choice choice = 3; 97 | 98 | // the weight behind their choice. This defaults to the entire weight 99 | // of the member if none is described. This allows for split voting. 100 | string weight = 4; 101 | } 102 | 103 | // ThresholdDecisionPolicy implements the DecisionPolicy interface 104 | message ThresholdDecisionPolicy { 105 | option (cosmos_proto.implements_interface) = "DecisionPolicy"; 106 | 107 | // threshold is the minimum weighted sum of yes votes that must be met or exceeded for a proposal to succeed. 108 | string threshold = 1; 109 | 110 | // timeout is the duration from submission of a proposal to the end of voting period 111 | // Within this times votes and exec messages can be submitted. 112 | google.protobuf.Duration timeout = 2 [(gogoproto.nullable) = false]; 113 | } 114 | -------------------------------------------------------------------------------- /proto/group/v1alpha1/events.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package regen.group.v1alpha1; 4 | 5 | option go_package = "github.com/interchainberlin/metachain/x/group"; 6 | 7 | // EventCreateGroup is an event emitted when a group is created. 8 | message EventCreateGroup { 9 | 10 | // group_id is the unique ID of the group. 11 | uint64 group_id = 1; 12 | } 13 | 14 | // EventUpdateGroup is an event emitted when a group is updated. 15 | message EventUpdateGroup { 16 | 17 | // group_id is the unique ID of the group. 18 | uint64 group_id = 1; 19 | } 20 | 21 | // EventCreateGroupAccount is an event emitted when a group account is created. 22 | message EventCreateGroupAccount { 23 | 24 | // address is the address of the group account. 25 | string address = 1; 26 | } 27 | 28 | // EventUpdateGroupAccount is an event emitted when a group account is updated. 29 | message EventUpdateGroupAccount { 30 | 31 | // address is the address of the group account. 32 | string address = 1; 33 | } 34 | 35 | // EventCreateProposal is an event emitted when a proposal is created. 36 | message EventCreateProposal { 37 | 38 | // proposal_id is the unique ID of the proposal. 39 | uint64 proposal_id = 1; 40 | } 41 | 42 | // EventVote is an event emitted when a voter votes on a proposal. 43 | message EventVote { 44 | 45 | // proposal_id is the unique ID of the proposal. 46 | uint64 proposal_id = 1; 47 | } 48 | 49 | // EventExec is an event emitted when a proposal is executed. 50 | message EventExec { 51 | 52 | // proposal_id is the unique ID of the proposal. 53 | uint64 proposal_id = 1; 54 | } 55 | -------------------------------------------------------------------------------- /proto/group/v1alpha1/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package regen.group.v1alpha1; 4 | 5 | option go_package = "github.com/interchainberlin/metachain/x/group"; 6 | 7 | import "gogoproto/gogo.proto"; 8 | import "regen/group/v1alpha1/types.proto"; 9 | 10 | // GenesisState defines the group module's genesis state. 11 | message GenesisState { 12 | 13 | // group_seq is the group table orm.Sequence, 14 | // it is used to get the next group ID. 15 | uint64 group_seq = 1; 16 | 17 | // groups is the list of groups info. 18 | repeated GroupInfo groups = 2; 19 | 20 | // group_members is the list of groups members. 21 | repeated GroupMember group_members = 3; 22 | 23 | // group_account_seq is the group account table orm.Sequence, 24 | // it is used to generate the next group account address. 25 | uint64 group_account_seq = 4; 26 | 27 | // group_accounts is the list of group accounts info. 28 | repeated GroupAccountInfo group_accounts = 5; 29 | 30 | // proposal_seq is the proposal table orm.Sequence, 31 | // it is used to get the next proposal ID. 32 | uint64 proposal_seq = 6; 33 | 34 | // proposals is the list of proposals. 35 | repeated Proposal proposals = 7; 36 | 37 | // votes is the list of votes. 38 | repeated Vote votes = 8; 39 | } 40 | -------------------------------------------------------------------------------- /proto/group/v1alpha1/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package regen.group.v1alpha1; 4 | 5 | import "regen/group/v1alpha1/types.proto"; 6 | import "gogoproto/gogo.proto"; 7 | import "cosmos/base/query/v1beta1/pagination.proto"; 8 | 9 | option go_package = "github.com/interchainberlin/metachain/x/group"; 10 | 11 | // Query is the regen.group.v1alpha1 Query service. 12 | service Query { 13 | 14 | // GroupInfo queries group info based on group id. 15 | rpc GroupInfo(QueryGroupInfoRequest) returns (QueryGroupInfoResponse); 16 | 17 | // GroupAccountInfo queries group account info based on group account address. 18 | rpc GroupAccountInfo(QueryGroupAccountInfoRequest) returns (QueryGroupAccountInfoResponse); 19 | 20 | // GroupMembers queries members of a group 21 | rpc GroupMembers(QueryGroupMembersRequest) returns (QueryGroupMembersResponse); 22 | 23 | // GroupsByAdmin queries groups by admin address. 24 | rpc GroupsByAdmin(QueryGroupsByAdminRequest) returns (QueryGroupsByAdminResponse); 25 | 26 | // GroupAccountsByGroup queries group accounts by group id. 27 | rpc GroupAccountsByGroup(QueryGroupAccountsByGroupRequest) returns (QueryGroupAccountsByGroupResponse); 28 | 29 | // GroupsByAdmin queries group accounts by admin address. 30 | rpc GroupAccountsByAdmin(QueryGroupAccountsByAdminRequest) returns (QueryGroupAccountsByAdminResponse); 31 | 32 | // Proposal queries a proposal based on proposal id. 33 | rpc Proposal(QueryProposalRequest) returns (QueryProposalResponse); 34 | 35 | // ProposalsByGroupAccount queries proposals based on group account address. 36 | rpc ProposalsByGroupAccount(QueryProposalsByGroupAccountRequest) returns (QueryProposalsByGroupAccountResponse); 37 | 38 | // VoteByProposalVoter queries a vote by proposal id and voter. 39 | rpc VoteByProposalVoter(QueryVoteByProposalVoterRequest) returns (QueryVoteByProposalVoterResponse); 40 | 41 | // VotesByProposal queries a vote by proposal. 42 | rpc VotesByProposal(QueryVotesByProposalRequest) returns (QueryVotesByProposalResponse); 43 | 44 | // VotesByVoter queries a vote by voter. 45 | rpc VotesByVoter(QueryVotesByVoterRequest) returns (QueryVotesByVoterResponse); 46 | } 47 | 48 | // QueryGroupInfoRequest is the Query/GroupInfo request type. 49 | message QueryGroupInfoRequest { 50 | 51 | // group_id is the unique ID of the group. 52 | uint64 group_id = 1; 53 | } 54 | 55 | // QueryGroupInfoResponse is the Query/GroupInfo response type. 56 | message QueryGroupInfoResponse { 57 | 58 | // info is the GroupInfo for the group. 59 | GroupInfo info = 1; 60 | } 61 | 62 | // QueryGroupAccountInfoRequest is the Query/GroupAccountInfo request type. 63 | message QueryGroupAccountInfoRequest { 64 | 65 | // address is the account address of the group account. 66 | string address = 1; 67 | } 68 | 69 | // QueryGroupAccountInfoResponse is the Query/GroupAccountInfo response type. 70 | message QueryGroupAccountInfoResponse { 71 | 72 | // info is the GroupAccountInfo for the group account. 73 | GroupAccountInfo info = 1; 74 | } 75 | 76 | // QueryGroupMembersRequest is the Query/GroupMembersRequest request type. 77 | message QueryGroupMembersRequest { 78 | 79 | // group_id is the unique ID of the group. 80 | uint64 group_id = 1; 81 | 82 | // pagination defines an optional pagination for the request. 83 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 84 | } 85 | 86 | // QueryGroupMembersResponse is the Query/GroupMembersResponse response type. 87 | message QueryGroupMembersResponse { 88 | 89 | // members are the members of the group with given group_id. 90 | repeated GroupMember members = 1; 91 | 92 | // pagination defines the pagination in the response. 93 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 94 | } 95 | 96 | // QueryGroupsByAdminRequest is the Query/GroupsByAdminRequest request type. 97 | message QueryGroupsByAdminRequest { 98 | 99 | // admin is the account address of a group's admin. 100 | string admin = 1; 101 | 102 | // pagination defines an optional pagination for the request. 103 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 104 | } 105 | 106 | // QueryGroupsByAdminResponse is the Query/GroupsByAdminResponse response type. 107 | message QueryGroupsByAdminResponse { 108 | 109 | // groups are the groups info with the provided admin. 110 | repeated GroupInfo groups = 1; 111 | 112 | // pagination defines the pagination in the response. 113 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 114 | } 115 | 116 | // QueryGroupAccountsByGroupRequest is the Query/GroupAccountsByGroup request type. 117 | message QueryGroupAccountsByGroupRequest { 118 | 119 | // group_id is the unique ID of the group account's group. 120 | uint64 group_id = 1; 121 | 122 | // pagination defines an optional pagination for the request. 123 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 124 | } 125 | 126 | // QueryGroupAccountsByGroupResponse is the Query/GroupAccountsByGroup response type. 127 | message QueryGroupAccountsByGroupResponse { 128 | 129 | // group_accounts are the group accounts info associated with the provided group. 130 | repeated GroupAccountInfo group_accounts = 1; 131 | 132 | // pagination defines the pagination in the response. 133 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 134 | } 135 | 136 | // QueryGroupAccountsByAdminRequest is the Query/GroupAccountsByAdmin request type. 137 | message QueryGroupAccountsByAdminRequest { 138 | 139 | // admin is the admin address of the group account. 140 | string admin = 1; 141 | 142 | // pagination defines an optional pagination for the request. 143 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 144 | } 145 | 146 | // QueryGroupAccountsByAdminResponse is the Query/GroupAccountsByAdmin response type. 147 | message QueryGroupAccountsByAdminResponse { 148 | 149 | // group_accounts are the group accounts info with provided admin. 150 | repeated GroupAccountInfo group_accounts = 1; 151 | 152 | // pagination defines the pagination in the response. 153 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 154 | } 155 | 156 | // QueryProposalRequest is the Query/Proposal request type. 157 | message QueryProposalRequest { 158 | 159 | // proposal_id is the unique ID of a proposal. 160 | uint64 proposal_id = 1; 161 | } 162 | 163 | // QueryProposalResponse is the Query/Proposal response type. 164 | message QueryProposalResponse { 165 | 166 | // proposal is the proposal info. 167 | Proposal proposal = 1; 168 | } 169 | 170 | // QueryProposalsByGroupAccountRequest is the Query/ProposalByGroupAccount request type. 171 | message QueryProposalsByGroupAccountRequest { 172 | 173 | // address is the group account address related to proposals. 174 | string address = 1; 175 | 176 | // pagination defines an optional pagination for the request. 177 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 178 | } 179 | 180 | // QueryProposalsByGroupAccountResponse is the Query/ProposalByGroupAccount response type. 181 | message QueryProposalsByGroupAccountResponse { 182 | 183 | // proposals are the proposals with given group account. 184 | repeated Proposal proposals = 1; 185 | 186 | // pagination defines the pagination in the response. 187 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 188 | } 189 | 190 | // QueryVoteByProposalVoterResponse is the Query/VoteByProposalVoter request type. 191 | message QueryVoteByProposalVoterRequest { 192 | 193 | // proposal_id is the unique ID of a proposal. 194 | uint64 proposal_id = 1; 195 | 196 | // voter is a proposal voter account address. 197 | string voter = 2; 198 | } 199 | 200 | // QueryVoteByProposalVoterResponse is the Query/VoteByProposalVoter response type. 201 | message QueryVoteByProposalVoterResponse { 202 | 203 | // vote is the vote with given proposal_id and voter. 204 | Vote vote = 1; 205 | } 206 | 207 | // QueryVotesByProposalResponse is the Query/VotesByProposal request type. 208 | message QueryVotesByProposalRequest { 209 | 210 | // proposal_id is the unique ID of a proposal. 211 | uint64 proposal_id = 1; 212 | 213 | // pagination defines an optional pagination for the request. 214 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 215 | } 216 | 217 | // QueryVotesByProposalResponse is the Query/VotesByProposal response type. 218 | message QueryVotesByProposalResponse { 219 | 220 | // votes are the list of votes for given proposal_id. 221 | repeated Vote votes = 1; 222 | 223 | // pagination defines the pagination in the response. 224 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 225 | } 226 | 227 | // QueryVotesByVoterResponse is the Query/VotesByVoter request type. 228 | message QueryVotesByVoterRequest { 229 | // voter is a proposal voter account address. 230 | string voter = 1; 231 | 232 | // pagination defines an optional pagination for the request. 233 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 234 | } 235 | 236 | // QueryVotesByVoterResponse is the Query/VotesByVoter response type. 237 | message QueryVotesByVoterResponse { 238 | 239 | // votes are the list of votes by given voter. 240 | repeated Vote votes = 1; 241 | 242 | // pagination defines the pagination in the response. 243 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 244 | } 245 | -------------------------------------------------------------------------------- /proto/group/v1alpha1/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package regen.group.v1alpha1; 4 | 5 | option go_package = "github.com/interchainberlin/metachain/x/group"; 6 | 7 | import "gogoproto/gogo.proto"; 8 | import "cosmos_proto/cosmos.proto"; 9 | import "google/protobuf/any.proto"; 10 | import "regen/group/v1alpha1/types.proto"; 11 | 12 | // Msg is the regen.group.v1alpha1 Msg service. 13 | service Msg { 14 | 15 | // CreateGroup creates a new group with an admin account address, a list of members and some optional metadata. 16 | rpc CreateGroup(MsgCreateGroupRequest) returns (MsgCreateGroupResponse); 17 | 18 | // UpdateGroupMembers updates the group members with given group id and admin address. 19 | rpc UpdateGroupMembers(MsgUpdateGroupMembersRequest) returns (MsgUpdateGroupMembersResponse); 20 | 21 | // UpdateGroupAdmin updates the group admin with given group id and previous admin address. 22 | rpc UpdateGroupAdmin(MsgUpdateGroupAdminRequest) returns (MsgUpdateGroupAdminResponse); 23 | 24 | // UpdateGroupMetadata updates the group metadata with given group id and admin address. 25 | rpc UpdateGroupMetadata(MsgUpdateGroupMetadataRequest) returns (MsgUpdateGroupMetadataResponse); 26 | 27 | // CreateGroupAccount creates a new group account using given DecisionPolicy. 28 | rpc CreateGroupAccount(MsgCreateGroupAccountRequest) returns (MsgCreateGroupAccountResponse); 29 | 30 | // UpdateGroupAccountAdmin updates a group account admin. 31 | rpc UpdateGroupAccountAdmin(MsgUpdateGroupAccountAdminRequest) returns (MsgUpdateGroupAccountAdminResponse); 32 | 33 | // UpdateGroupAccountDecisionPolicy allows a group account decision policy to be updated. 34 | rpc UpdateGroupAccountDecisionPolicy(MsgUpdateGroupAccountDecisionPolicyRequest) returns (MsgUpdateGroupAccountDecisionPolicyResponse); 35 | 36 | // UpdateGroupAccountMetadata updates a group account metadata. 37 | rpc UpdateGroupAccountMetadata(MsgUpdateGroupAccountMetadataRequest) returns (MsgUpdateGroupAccountMetadataResponse); 38 | 39 | } 40 | 41 | // 42 | // Groups 43 | // 44 | 45 | // MsgCreateGroupRequest is the Msg/CreateGroup request type. 46 | message MsgCreateGroupRequest { 47 | 48 | // admin is the account address of the group admin. 49 | string admin = 1; 50 | 51 | // members defines the group members. 52 | repeated Member members = 2 [(gogoproto.nullable) = false]; 53 | 54 | // metadata is any arbitrary metadata to attached to the group. 55 | bytes metadata = 3; 56 | } 57 | 58 | // MsgCreateGroupResponse is the Msg/CreateGroup response type. 59 | message MsgCreateGroupResponse { 60 | 61 | // group_id is the unique ID of the newly created group. 62 | uint64 group_id = 1; 63 | } 64 | 65 | // MsgUpdateGroupMembersRequest is the Msg/UpdateGroupMembers request type. 66 | message MsgUpdateGroupMembersRequest { 67 | 68 | // admin is the account address of the group admin. 69 | string admin = 1; 70 | 71 | // group_id is the unique ID of the group. 72 | uint64 group_id = 2; 73 | 74 | // member_updates is the list of members to update, 75 | // set weight to 0 to remove a member. 76 | repeated Member member_updates = 3 [(gogoproto.nullable) = false]; 77 | } 78 | 79 | // MsgUpdateGroupMembersResponse is the Msg/UpdateGroupMembers response type. 80 | message MsgUpdateGroupMembersResponse { } 81 | 82 | // MsgUpdateGroupAdminRequest is the Msg/UpdateGroupAdmin request type. 83 | message MsgUpdateGroupAdminRequest { 84 | 85 | // admin is the current account address of the group admin. 86 | string admin = 1; 87 | 88 | // group_id is the unique ID of the group. 89 | uint64 group_id = 2; 90 | 91 | // new_admin is the group new admin account address. 92 | string new_admin = 3; 93 | } 94 | 95 | // MsgUpdateGroupAdminResponse is the Msg/UpdateGroupAdmin response type. 96 | message MsgUpdateGroupAdminResponse { } 97 | 98 | // MsgUpdateGroupMetadataRequest is the Msg/UpdateGroupMetadata request type. 99 | message MsgUpdateGroupMetadataRequest { 100 | 101 | // admin is the account address of the group admin. 102 | string admin = 1; 103 | 104 | // group_id is the unique ID of the group. 105 | uint64 group_id = 2; 106 | 107 | // metadata is the updated group's metadata. 108 | bytes metadata = 3; 109 | } 110 | 111 | // MsgUpdateGroupMetadataResponse is the Msg/UpdateGroupMetadata response type. 112 | message MsgUpdateGroupMetadataResponse { } 113 | 114 | // 115 | // Group Accounts 116 | // 117 | 118 | // MsgCreateGroupAccountRequest is the Msg/CreateGroupAccount request type. 119 | message MsgCreateGroupAccountRequest { 120 | option (gogoproto.goproto_getters) = false; 121 | 122 | // admin is the account address of the group admin. 123 | string admin = 1; 124 | 125 | // group_id is the unique ID of the group. 126 | uint64 group_id = 2; 127 | 128 | // metadata is any arbitrary metadata to attached to the group account. 129 | bytes metadata = 3; 130 | 131 | // decision_policy specifies the group account's decision policy. 132 | google.protobuf.Any decision_policy = 4 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 133 | } 134 | 135 | // MsgCreateGroupAccountResponse is the Msg/CreateGroupAccount response type. 136 | message MsgCreateGroupAccountResponse { 137 | 138 | // address is the account address of the newly created group account. 139 | string address = 1; 140 | } 141 | 142 | // MsgUpdateGroupAccountAdminRequest is the Msg/UpdateGroupAccountAdmin request type. 143 | message MsgUpdateGroupAccountAdminRequest { 144 | 145 | // admin is the account address of the group admin. 146 | string admin = 1; 147 | 148 | // address is the group account address. 149 | string address = 2; 150 | 151 | // new_admin is the new group account admin. 152 | string new_admin = 3; 153 | } 154 | 155 | // MsgUpdateGroupAccountAdminResponse is the Msg/UpdateGroupAccountAdmin response type. 156 | message MsgUpdateGroupAccountAdminResponse { } 157 | 158 | // MsgUpdateGroupAccountDecisionPolicyRequest is the Msg/UpdateGroupAccountDecisionPolicy request type. 159 | message MsgUpdateGroupAccountDecisionPolicyRequest { 160 | option (gogoproto.goproto_getters) = false; 161 | 162 | // admin is the account address of the group admin. 163 | string admin = 1; 164 | 165 | // address is the group account address. 166 | string address = 2; 167 | 168 | // decision_policy is the updated group account decision policy. 169 | google.protobuf.Any decision_policy = 3 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 170 | } 171 | 172 | // MsgUpdateGroupAccountDecisionPolicyResponse is the Msg/UpdateGroupAccountDecisionPolicy response type. 173 | message MsgUpdateGroupAccountDecisionPolicyResponse { } 174 | 175 | // MsgUpdateGroupAccountMetadataRequest is the Msg/UpdateGroupAccountMetadata request type. 176 | message MsgUpdateGroupAccountMetadataRequest { 177 | 178 | // admin is the account address of the group admin. 179 | string admin = 1; 180 | 181 | // address is the group account address. 182 | string address = 2; 183 | 184 | // metadata is the updated group account metadata. 185 | bytes metadata = 3; 186 | } 187 | 188 | // MsgUpdateGroupAccountMetadataResponse is the Msg/UpdateGroupAccountMetadata response type. 189 | message MsgUpdateGroupAccountMetadataResponse { } 190 | 191 | -------------------------------------------------------------------------------- /proto/group/v1alpha1/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package regen.group.v1alpha1; 4 | 5 | option go_package = "github.com/interchainberlin/metachain/x/group"; 6 | 7 | import "gogoproto/gogo.proto"; 8 | import "google/protobuf/duration.proto"; 9 | import "google/protobuf/timestamp.proto"; 10 | import "cosmos_proto/cosmos.proto"; 11 | import "google/protobuf/any.proto"; 12 | 13 | // Member represents a group member with an account address, 14 | // non-zero weight and metadata. 15 | message Member { 16 | 17 | // address is the member's account address. 18 | string address = 1; 19 | 20 | // weight is the member's voting weight that should be greater than 0. 21 | string weight = 2; 22 | 23 | // metadata is any arbitrary metadata to attached to the member. 24 | bytes metadata = 3; 25 | } 26 | 27 | // Members defines a repeated slice of Member objects. 28 | message Members { 29 | 30 | // members is the list of members. 31 | repeated Member members = 1 [(gogoproto.nullable) = false]; 32 | } 33 | 34 | 35 | // 36 | // State 37 | // 38 | 39 | // GroupInfo represents the high-level on-chain information for a group. 40 | message GroupInfo { 41 | 42 | // group_id is the unique ID of the group. 43 | uint64 group_id = 1; 44 | 45 | // admin is the account address of the group's admin. 46 | string admin = 2; 47 | 48 | // metadata is any arbitrary metadata to attached to the group. 49 | bytes metadata = 3; 50 | 51 | // slices keeps track of all the heights that snapshots of the membership 52 | // set are stored 53 | repeated uint64 slices = 4; 54 | 55 | // total_weight is the current sum of the group members' weights. 56 | string total_weight = 5; 57 | } 58 | 59 | // GroupMember represents the relationship between a group and a member. 60 | message GroupMember { 61 | 62 | // group_id is the unique ID of the group. 63 | uint64 group_id = 1; 64 | 65 | // member is the member data. 66 | Member member = 2; 67 | } 68 | 69 | // GroupAccountInfo represents the high-level on-chain information for a group account. 70 | message GroupAccountInfo { 71 | option (gogoproto.equal) = true; 72 | option (gogoproto.goproto_getters) = false; 73 | 74 | // address is the group account address. 75 | string address = 1; 76 | 77 | // group_id is the unique ID of the group. 78 | uint64 group_id = 2; 79 | 80 | // admin is the account address of the group admin. 81 | string admin = 3; 82 | 83 | // metadata is any arbitrary metadata attached to the group account. 84 | bytes metadata = 4; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /questions.md: -------------------------------------------------------------------------------- 1 | # Questions and Unknowns 2 | 3 | This section is designed to capture some of the outstanding questions and 4 | unknowns with this project. 5 | 6 | 1. I see decision policies as being categorized into three groups based on 7 | what point in time the weight of the voter is counted. This could be a) Upon 8 | the start of the propsal, b) Upon receiving the vote and c) Upon the end of 9 | the proposal period. Each of these require different tally systems to 10 | accompany the decision policy and thus could be a lot of overhead. Do we want 11 | to allow such flexibility or should the `dao` module be fixed on a single 12 | style (in this case, my preference would be upon the start of the proposal)? -------------------------------------------------------------------------------- /scripts/protocgen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # see pre-requests: 4 | # - https://grpc.io/docs/languages/go/quickstart/ 5 | # - gocosmos plugin is automatically installed during scaffolding. 6 | 7 | set -eo pipefail 8 | 9 | proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 10 | for dir in $proto_dirs; do 11 | protoc \ 12 | -I "proto" \ 13 | -I "third_party/proto" \ 14 | --gocosmos_out=plugins=interfacetype+grpc,\ 15 | Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \ 16 | $(find "${dir}" -maxdepth 1 -name '*.proto') 17 | done 18 | 19 | # move proto files to the right places 20 | cp -r github.com/interchainberlin/metachain/* ./ 21 | rm -rf github.com 22 | 23 | -------------------------------------------------------------------------------- /third_party/proto/confio/proofs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package ics23; 4 | option go_package = "github.com/confio/ics23/go"; 5 | 6 | enum HashOp { 7 | // NO_HASH is the default if no data passed. Note this is an illegal argument some places. 8 | NO_HASH = 0; 9 | SHA256 = 1; 10 | SHA512 = 2; 11 | KECCAK = 3; 12 | RIPEMD160 = 4; 13 | BITCOIN = 5; // ripemd160(sha256(x)) 14 | } 15 | 16 | /** 17 | LengthOp defines how to process the key and value of the LeafOp 18 | to include length information. After encoding the length with the given 19 | algorithm, the length will be prepended to the key and value bytes. 20 | (Each one with it's own encoded length) 21 | */ 22 | enum LengthOp { 23 | // NO_PREFIX don't include any length info 24 | NO_PREFIX = 0; 25 | // VAR_PROTO uses protobuf (and go-amino) varint encoding of the length 26 | VAR_PROTO = 1; 27 | // VAR_RLP uses rlp int encoding of the length 28 | VAR_RLP = 2; 29 | // FIXED32_BIG uses big-endian encoding of the length as a 32 bit integer 30 | FIXED32_BIG = 3; 31 | // FIXED32_LITTLE uses little-endian encoding of the length as a 32 bit integer 32 | FIXED32_LITTLE = 4; 33 | // FIXED64_BIG uses big-endian encoding of the length as a 64 bit integer 34 | FIXED64_BIG = 5; 35 | // FIXED64_LITTLE uses little-endian encoding of the length as a 64 bit integer 36 | FIXED64_LITTLE = 6; 37 | // REQUIRE_32_BYTES is like NONE, but will fail if the input is not exactly 32 bytes (sha256 output) 38 | REQUIRE_32_BYTES = 7; 39 | // REQUIRE_64_BYTES is like NONE, but will fail if the input is not exactly 64 bytes (sha512 output) 40 | REQUIRE_64_BYTES = 8; 41 | } 42 | 43 | /** 44 | ExistenceProof takes a key and a value and a set of steps to perform on it. 45 | The result of peforming all these steps will provide a "root hash", which can 46 | be compared to the value in a header. 47 | 48 | Since it is computationally infeasible to produce a hash collission for any of the used 49 | cryptographic hash functions, if someone can provide a series of operations to transform 50 | a given key and value into a root hash that matches some trusted root, these key and values 51 | must be in the referenced merkle tree. 52 | 53 | The only possible issue is maliablity in LeafOp, such as providing extra prefix data, 54 | which should be controlled by a spec. Eg. with lengthOp as NONE, 55 | prefix = FOO, key = BAR, value = CHOICE 56 | and 57 | prefix = F, key = OOBAR, value = CHOICE 58 | would produce the same value. 59 | 60 | With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field 61 | in the ProofSpec is valuable to prevent this mutability. And why all trees should 62 | length-prefix the data before hashing it. 63 | */ 64 | message ExistenceProof { 65 | bytes key = 1; 66 | bytes value = 2; 67 | LeafOp leaf = 3; 68 | repeated InnerOp path = 4; 69 | } 70 | 71 | /* 72 | NonExistenceProof takes a proof of two neighbors, one left of the desired key, 73 | one right of the desired key. If both proofs are valid AND they are neighbors, 74 | then there is no valid proof for the given key. 75 | */ 76 | message NonExistenceProof { 77 | bytes key = 1; // TODO: remove this as unnecessary??? we prove a range 78 | ExistenceProof left = 2; 79 | ExistenceProof right = 3; 80 | } 81 | 82 | /* 83 | CommitmentProof is either an ExistenceProof or a NonExistenceProof, or a Batch of such messages 84 | */ 85 | message CommitmentProof { 86 | oneof proof { 87 | ExistenceProof exist = 1; 88 | NonExistenceProof nonexist = 2; 89 | BatchProof batch = 3; 90 | CompressedBatchProof compressed = 4; 91 | } 92 | } 93 | 94 | /** 95 | LeafOp represents the raw key-value data we wish to prove, and 96 | must be flexible to represent the internal transformation from 97 | the original key-value pairs into the basis hash, for many existing 98 | merkle trees. 99 | 100 | key and value are passed in. So that the signature of this operation is: 101 | leafOp(key, value) -> output 102 | 103 | To process this, first prehash the keys and values if needed (ANY means no hash in this case): 104 | hkey = prehashKey(key) 105 | hvalue = prehashValue(value) 106 | 107 | Then combine the bytes, and hash it 108 | output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue) 109 | */ 110 | message LeafOp { 111 | HashOp hash = 1; 112 | HashOp prehash_key = 2; 113 | HashOp prehash_value = 3; 114 | LengthOp length = 4; 115 | // prefix is a fixed bytes that may optionally be included at the beginning to differentiate 116 | // a leaf node from an inner node. 117 | bytes prefix = 5; 118 | } 119 | 120 | /** 121 | InnerOp represents a merkle-proof step that is not a leaf. 122 | It represents concatenating two children and hashing them to provide the next result. 123 | 124 | The result of the previous step is passed in, so the signature of this op is: 125 | innerOp(child) -> output 126 | 127 | The result of applying InnerOp should be: 128 | output = op.hash(op.prefix || child || op.suffix) 129 | 130 | where the || operator is concatenation of binary data, 131 | and child is the result of hashing all the tree below this step. 132 | 133 | Any special data, like prepending child with the length, or prepending the entire operation with 134 | some value to differentiate from leaf nodes, should be included in prefix and suffix. 135 | If either of prefix or suffix is empty, we just treat it as an empty string 136 | */ 137 | message InnerOp { 138 | HashOp hash = 1; 139 | bytes prefix = 2; 140 | bytes suffix = 3; 141 | } 142 | 143 | 144 | /** 145 | ProofSpec defines what the expected parameters are for a given proof type. 146 | This can be stored in the client and used to validate any incoming proofs. 147 | 148 | verify(ProofSpec, Proof) -> Proof | Error 149 | 150 | As demonstrated in tests, if we don't fix the algorithm used to calculate the 151 | LeafHash for a given tree, there are many possible key-value pairs that can 152 | generate a given hash (by interpretting the preimage differently). 153 | We need this for proper security, requires client knows a priori what 154 | tree format server uses. But not in code, rather a configuration object. 155 | */ 156 | message ProofSpec { 157 | // any field in the ExistenceProof must be the same as in this spec. 158 | // except Prefix, which is just the first bytes of prefix (spec can be longer) 159 | LeafOp leaf_spec = 1; 160 | InnerSpec inner_spec = 2; 161 | // max_depth (if > 0) is the maximum number of InnerOps allowed (mainly for fixed-depth tries) 162 | int32 max_depth = 3; 163 | // min_depth (if > 0) is the minimum number of InnerOps allowed (mainly for fixed-depth tries) 164 | int32 min_depth = 4; 165 | } 166 | 167 | /* 168 | InnerSpec contains all store-specific structure info to determine if two proofs from a 169 | given store are neighbors. 170 | 171 | This enables: 172 | 173 | isLeftMost(spec: InnerSpec, op: InnerOp) 174 | isRightMost(spec: InnerSpec, op: InnerOp) 175 | isLeftNeighbor(spec: InnerSpec, left: InnerOp, right: InnerOp) 176 | */ 177 | message InnerSpec { 178 | // Child order is the ordering of the children node, must count from 0 179 | // iavl tree is [0, 1] (left then right) 180 | // merk is [0, 2, 1] (left, right, here) 181 | repeated int32 child_order = 1; 182 | int32 child_size = 2; 183 | int32 min_prefix_length = 3; 184 | int32 max_prefix_length = 4; 185 | // empty child is the prehash image that is used when one child is nil (eg. 20 bytes of 0) 186 | bytes empty_child = 5; 187 | // hash is the algorithm that must be used for each InnerOp 188 | HashOp hash = 6; 189 | } 190 | 191 | /* 192 | BatchProof is a group of multiple proof types than can be compressed 193 | */ 194 | message BatchProof { 195 | repeated BatchEntry entries = 1; 196 | } 197 | 198 | // Use BatchEntry not CommitmentProof, to avoid recursion 199 | message BatchEntry { 200 | oneof proof { 201 | ExistenceProof exist = 1; 202 | NonExistenceProof nonexist = 2; 203 | } 204 | } 205 | 206 | 207 | /****** all items here are compressed forms *******/ 208 | 209 | message CompressedBatchProof { 210 | repeated CompressedBatchEntry entries = 1; 211 | repeated InnerOp lookup_inners = 2; 212 | } 213 | 214 | // Use BatchEntry not CommitmentProof, to avoid recursion 215 | message CompressedBatchEntry { 216 | oneof proof { 217 | CompressedExistenceProof exist = 1; 218 | CompressedNonExistenceProof nonexist = 2; 219 | } 220 | } 221 | 222 | message CompressedExistenceProof { 223 | bytes key = 1; 224 | bytes value = 2; 225 | LeafOp leaf = 3; 226 | // these are indexes into the lookup_inners table in CompressedBatchProof 227 | repeated int32 path = 4; 228 | } 229 | 230 | message CompressedNonExistenceProof { 231 | bytes key = 1; // TODO: remove this as unnecessary??? we prove a range 232 | CompressedExistenceProof left = 2; 233 | CompressedExistenceProof right = 3; 234 | } 235 | -------------------------------------------------------------------------------- /third_party/proto/cosmos_proto/cosmos.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package cosmos_proto; 3 | 4 | import "google/protobuf/descriptor.proto"; 5 | 6 | option go_package = "github.com/regen-network/cosmos-proto"; 7 | 8 | extend google.protobuf.MessageOptions { 9 | string interface_type = 93001; 10 | 11 | string implements_interface = 93002; 12 | } 13 | 14 | extend google.protobuf.FieldOptions { 15 | string accepts_interface = 93001; 16 | } 17 | -------------------------------------------------------------------------------- /third_party/proto/gogoproto/gogo.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers for Go with Gadgets 2 | // 3 | // Copyright (c) 2013, The GoGo Authors. All rights reserved. 4 | // http://github.com/gogo/protobuf 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are 8 | // met: 9 | // 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above 13 | // copyright notice, this list of conditions and the following disclaimer 14 | // in the documentation and/or other materials provided with the 15 | // distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | syntax = "proto2"; 30 | package gogoproto; 31 | 32 | import "google/protobuf/descriptor.proto"; 33 | 34 | option java_package = "com.google.protobuf"; 35 | option java_outer_classname = "GoGoProtos"; 36 | option go_package = "github.com/gogo/protobuf/gogoproto"; 37 | 38 | extend google.protobuf.EnumOptions { 39 | optional bool goproto_enum_prefix = 62001; 40 | optional bool goproto_enum_stringer = 62021; 41 | optional bool enum_stringer = 62022; 42 | optional string enum_customname = 62023; 43 | optional bool enumdecl = 62024; 44 | } 45 | 46 | extend google.protobuf.EnumValueOptions { 47 | optional string enumvalue_customname = 66001; 48 | } 49 | 50 | extend google.protobuf.FileOptions { 51 | optional bool goproto_getters_all = 63001; 52 | optional bool goproto_enum_prefix_all = 63002; 53 | optional bool goproto_stringer_all = 63003; 54 | optional bool verbose_equal_all = 63004; 55 | optional bool face_all = 63005; 56 | optional bool gostring_all = 63006; 57 | optional bool populate_all = 63007; 58 | optional bool stringer_all = 63008; 59 | optional bool onlyone_all = 63009; 60 | 61 | optional bool equal_all = 63013; 62 | optional bool description_all = 63014; 63 | optional bool testgen_all = 63015; 64 | optional bool benchgen_all = 63016; 65 | optional bool marshaler_all = 63017; 66 | optional bool unmarshaler_all = 63018; 67 | optional bool stable_marshaler_all = 63019; 68 | 69 | optional bool sizer_all = 63020; 70 | 71 | optional bool goproto_enum_stringer_all = 63021; 72 | optional bool enum_stringer_all = 63022; 73 | 74 | optional bool unsafe_marshaler_all = 63023; 75 | optional bool unsafe_unmarshaler_all = 63024; 76 | 77 | optional bool goproto_extensions_map_all = 63025; 78 | optional bool goproto_unrecognized_all = 63026; 79 | optional bool gogoproto_import = 63027; 80 | optional bool protosizer_all = 63028; 81 | optional bool compare_all = 63029; 82 | optional bool typedecl_all = 63030; 83 | optional bool enumdecl_all = 63031; 84 | 85 | optional bool goproto_registration = 63032; 86 | optional bool messagename_all = 63033; 87 | 88 | optional bool goproto_sizecache_all = 63034; 89 | optional bool goproto_unkeyed_all = 63035; 90 | } 91 | 92 | extend google.protobuf.MessageOptions { 93 | optional bool goproto_getters = 64001; 94 | optional bool goproto_stringer = 64003; 95 | optional bool verbose_equal = 64004; 96 | optional bool face = 64005; 97 | optional bool gostring = 64006; 98 | optional bool populate = 64007; 99 | optional bool stringer = 67008; 100 | optional bool onlyone = 64009; 101 | 102 | optional bool equal = 64013; 103 | optional bool description = 64014; 104 | optional bool testgen = 64015; 105 | optional bool benchgen = 64016; 106 | optional bool marshaler = 64017; 107 | optional bool unmarshaler = 64018; 108 | optional bool stable_marshaler = 64019; 109 | 110 | optional bool sizer = 64020; 111 | 112 | optional bool unsafe_marshaler = 64023; 113 | optional bool unsafe_unmarshaler = 64024; 114 | 115 | optional bool goproto_extensions_map = 64025; 116 | optional bool goproto_unrecognized = 64026; 117 | 118 | optional bool protosizer = 64028; 119 | optional bool compare = 64029; 120 | 121 | optional bool typedecl = 64030; 122 | 123 | optional bool messagename = 64033; 124 | 125 | optional bool goproto_sizecache = 64034; 126 | optional bool goproto_unkeyed = 64035; 127 | } 128 | 129 | extend google.protobuf.FieldOptions { 130 | optional bool nullable = 65001; 131 | optional bool embed = 65002; 132 | optional string customtype = 65003; 133 | optional string customname = 65004; 134 | optional string jsontag = 65005; 135 | optional string moretags = 65006; 136 | optional string casttype = 65007; 137 | optional string castkey = 65008; 138 | optional string castvalue = 65009; 139 | 140 | optional bool stdtime = 65010; 141 | optional bool stdduration = 65011; 142 | optional bool wktpointer = 65012; 143 | 144 | optional string castrepeated = 65013; 145 | } 146 | -------------------------------------------------------------------------------- /third_party/proto/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /third_party/proto/google/api/httpbody.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | 16 | syntax = "proto3"; 17 | 18 | package google.api; 19 | 20 | import "google/protobuf/any.proto"; 21 | 22 | option cc_enable_arenas = true; 23 | option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; 24 | option java_multiple_files = true; 25 | option java_outer_classname = "HttpBodyProto"; 26 | option java_package = "com.google.api"; 27 | option objc_class_prefix = "GAPI"; 28 | 29 | // Message that represents an arbitrary HTTP body. It should only be used for 30 | // payload formats that can't be represented as JSON, such as raw binary or 31 | // an HTML page. 32 | // 33 | // 34 | // This message can be used both in streaming and non-streaming API methods in 35 | // the request as well as the response. 36 | // 37 | // It can be used as a top-level request field, which is convenient if one 38 | // wants to extract parameters from either the URL or HTTP template into the 39 | // request fields and also want access to the raw HTTP body. 40 | // 41 | // Example: 42 | // 43 | // message GetResourceRequest { 44 | // // A unique request id. 45 | // string request_id = 1; 46 | // 47 | // // The raw HTTP body is bound to this field. 48 | // google.api.HttpBody http_body = 2; 49 | // } 50 | // 51 | // service ResourceService { 52 | // rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); 53 | // rpc UpdateResource(google.api.HttpBody) returns 54 | // (google.protobuf.Empty); 55 | // } 56 | // 57 | // Example with streaming methods: 58 | // 59 | // service CaldavService { 60 | // rpc GetCalendar(stream google.api.HttpBody) 61 | // returns (stream google.api.HttpBody); 62 | // rpc UpdateCalendar(stream google.api.HttpBody) 63 | // returns (stream google.api.HttpBody); 64 | // } 65 | // 66 | // Use of this type only changes how the request and response bodies are 67 | // handled, all other features will continue to work unchanged. 68 | message HttpBody { 69 | // The HTTP Content-Type header value specifying the content type of the body. 70 | string content_type = 1; 71 | 72 | // The HTTP request/response body as raw binary. 73 | bytes data = 2; 74 | 75 | // Application specific response metadata. Must be set in the first response 76 | // for streaming APIs. 77 | repeated google.protobuf.Any extensions = 3; 78 | } -------------------------------------------------------------------------------- /third_party/proto/google/protobuf/any.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | import "gogoproto/gogo.proto"; 36 | 37 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 38 | option go_package = "types"; 39 | option java_package = "com.google.protobuf"; 40 | option java_outer_classname = "AnyProto"; 41 | option java_multiple_files = true; 42 | option objc_class_prefix = "GPB"; 43 | 44 | // `Any` contains an arbitrary serialized protocol buffer message along with a 45 | // URL that describes the type of the serialized message. 46 | // 47 | // Protobuf library provides support to pack/unpack Any values in the form 48 | // of utility functions or additional generated methods of the Any type. 49 | // 50 | // Example 1: Pack and unpack a message in C++. 51 | // 52 | // Foo foo = ...; 53 | // Any any; 54 | // any.PackFrom(foo); 55 | // ... 56 | // if (any.UnpackTo(&foo)) { 57 | // ... 58 | // } 59 | // 60 | // Example 2: Pack and unpack a message in Java. 61 | // 62 | // Foo foo = ...; 63 | // Any any = Any.pack(foo); 64 | // ... 65 | // if (any.is(Foo.class)) { 66 | // foo = any.unpack(Foo.class); 67 | // } 68 | // 69 | // Example 3: Pack and unpack a message in Python. 70 | // 71 | // foo = Foo(...) 72 | // any = Any() 73 | // any.Pack(foo) 74 | // ... 75 | // if any.Is(Foo.DESCRIPTOR): 76 | // any.Unpack(foo) 77 | // ... 78 | // 79 | // Example 4: Pack and unpack a message in Go 80 | // 81 | // foo := &pb.Foo{...} 82 | // any, err := ptypes.MarshalAny(foo) 83 | // ... 84 | // foo := &pb.Foo{} 85 | // if err := ptypes.UnmarshalAny(any, foo); err != nil { 86 | // ... 87 | // } 88 | // 89 | // The pack methods provided by protobuf library will by default use 90 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 91 | // methods only use the fully qualified type name after the last '/' 92 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 93 | // name "y.z". 94 | // 95 | // 96 | // JSON 97 | // ==== 98 | // The JSON representation of an `Any` value uses the regular 99 | // representation of the deserialized, embedded message, with an 100 | // additional field `@type` which contains the type URL. Example: 101 | // 102 | // package google.profile; 103 | // message Person { 104 | // string first_name = 1; 105 | // string last_name = 2; 106 | // } 107 | // 108 | // { 109 | // "@type": "type.googleapis.com/google.profile.Person", 110 | // "firstName": , 111 | // "lastName": 112 | // } 113 | // 114 | // If the embedded message type is well-known and has a custom JSON 115 | // representation, that representation will be embedded adding a field 116 | // `value` which holds the custom JSON in addition to the `@type` 117 | // field. Example (for message [google.protobuf.Duration][]): 118 | // 119 | // { 120 | // "@type": "type.googleapis.com/google.protobuf.Duration", 121 | // "value": "1.212s" 122 | // } 123 | // 124 | message Any { 125 | // A URL/resource name that uniquely identifies the type of the serialized 126 | // protocol buffer message. This string must contain at least 127 | // one "/" character. The last segment of the URL's path must represent 128 | // the fully qualified name of the type (as in 129 | // `path/google.protobuf.Duration`). The name should be in a canonical form 130 | // (e.g., leading "." is not accepted). 131 | // 132 | // In practice, teams usually precompile into the binary all types that they 133 | // expect it to use in the context of Any. However, for URLs which use the 134 | // scheme `http`, `https`, or no scheme, one can optionally set up a type 135 | // server that maps type URLs to message definitions as follows: 136 | // 137 | // * If no scheme is provided, `https` is assumed. 138 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][] 139 | // value in binary format, or produce an error. 140 | // * Applications are allowed to cache lookup results based on the 141 | // URL, or have them precompiled into a binary to avoid any 142 | // lookup. Therefore, binary compatibility needs to be preserved 143 | // on changes to types. (Use versioned type names to manage 144 | // breaking changes.) 145 | // 146 | // Note: this functionality is not currently available in the official 147 | // protobuf release, and it is not used for type URLs beginning with 148 | // type.googleapis.com. 149 | // 150 | // Schemes other than `http`, `https` (or the empty scheme) might be 151 | // used with implementation specific semantics. 152 | // 153 | string type_url = 1; 154 | 155 | // Must be a valid serialized protocol buffer of the above specified type. 156 | bytes value = 2; 157 | 158 | option (gogoproto.typedecl) = false; 159 | } 160 | 161 | option (gogoproto.goproto_registration) = false; 162 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/crypto/keys.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.crypto; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | 8 | // PublicKey defines the keys available for use with Tendermint Validators 9 | message PublicKey { 10 | option (gogoproto.compare) = true; 11 | option (gogoproto.equal) = true; 12 | 13 | oneof sum { 14 | bytes ed25519 = 1; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/crypto/proof.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.crypto; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | 8 | message Proof { 9 | int64 total = 1; 10 | int64 index = 2; 11 | bytes leaf_hash = 3; 12 | repeated bytes aunts = 4; 13 | } 14 | 15 | message ValueOp { 16 | // Encoded in ProofOp.Key. 17 | bytes key = 1; 18 | 19 | // To encode in ProofOp.Data 20 | Proof proof = 2; 21 | } 22 | 23 | message DominoOp { 24 | string key = 1; 25 | string input = 2; 26 | string output = 3; 27 | } 28 | 29 | // ProofOp defines an operation used for calculating Merkle root 30 | // The data could be arbitrary format, providing nessecary data 31 | // for example neighbouring node hash 32 | message ProofOp { 33 | string type = 1; 34 | bytes key = 2; 35 | bytes data = 3; 36 | } 37 | 38 | // ProofOps is Merkle proof defined by the list of ProofOps 39 | message ProofOps { 40 | repeated ProofOp ops = 1 [(gogoproto.nullable) = false]; 41 | } 42 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/libs/bits/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.libs.bits; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/libs/bits"; 5 | 6 | message BitArray { 7 | int64 bits = 1; 8 | repeated uint64 elems = 2; 9 | } 10 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/evidence.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | import "tendermint/types/types.proto"; 9 | import "tendermint/crypto/keys.proto"; 10 | 11 | // DuplicateVoteEvidence contains evidence a validator signed two conflicting 12 | // votes. 13 | message DuplicateVoteEvidence { 14 | Vote vote_a = 1; 15 | Vote vote_b = 2; 16 | 17 | google.protobuf.Timestamp timestamp = 3 18 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 19 | } 20 | 21 | message Evidence { 22 | oneof sum { 23 | DuplicateVoteEvidence duplicate_vote_evidence = 1; 24 | } 25 | } 26 | 27 | // EvidenceData contains any evidence of malicious wrong-doing by validators 28 | message EvidenceData { 29 | repeated Evidence evidence = 1 [(gogoproto.nullable) = false]; 30 | bytes hash = 2; 31 | } 32 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/params.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "google/protobuf/duration.proto"; 8 | 9 | option (gogoproto.equal_all) = true; 10 | 11 | // ConsensusParams contains consensus critical parameters that determine the 12 | // validity of blocks. 13 | message ConsensusParams { 14 | BlockParams block = 1 [(gogoproto.nullable) = false]; 15 | EvidenceParams evidence = 2 [(gogoproto.nullable) = false]; 16 | ValidatorParams validator = 3 [(gogoproto.nullable) = false]; 17 | VersionParams version = 4 [(gogoproto.nullable) = false]; 18 | } 19 | 20 | // BlockParams contains limits on the block size. 21 | message BlockParams { 22 | // Max block size, in bytes. 23 | // Note: must be greater than 0 24 | int64 max_bytes = 1; 25 | // Max gas per block. 26 | // Note: must be greater or equal to -1 27 | int64 max_gas = 2; 28 | // Minimum time increment between consecutive blocks (in milliseconds) If the 29 | // block header timestamp is ahead of the system clock, decrease this value. 30 | // 31 | // Not exposed to the application. 32 | int64 time_iota_ms = 3; 33 | } 34 | 35 | // EvidenceParams determine how we handle evidence of malfeasance. 36 | message EvidenceParams { 37 | // Max age of evidence, in blocks. 38 | // 39 | // The basic formula for calculating this is: MaxAgeDuration / {average block 40 | // time}. 41 | int64 max_age_num_blocks = 1; 42 | 43 | // Max age of evidence, in time. 44 | // 45 | // It should correspond with an app's "unbonding period" or other similar 46 | // mechanism for handling [Nothing-At-Stake 47 | // attacks](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed). 48 | google.protobuf.Duration max_age_duration = 2 49 | [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; 50 | 51 | // This sets the maximum number of evidence that can be committed in a single block. 52 | // and should fall comfortably under the max block bytes when we consider the size of 53 | // each evidence (See MaxEvidenceBytes). The maximum number is MaxEvidencePerBlock. 54 | // Default is 50 55 | uint32 max_num = 3; 56 | } 57 | 58 | // ValidatorParams restrict the public key types validators can use. 59 | // NOTE: uses ABCI pubkey naming, not Amino names. 60 | message ValidatorParams { 61 | option (gogoproto.populate) = true; 62 | option (gogoproto.equal) = true; 63 | 64 | repeated string pub_key_types = 1; 65 | } 66 | 67 | // VersionParams contains the ABCI application version. 68 | message VersionParams { 69 | option (gogoproto.populate) = true; 70 | option (gogoproto.equal) = true; 71 | 72 | uint64 app_version = 1; 73 | } 74 | 75 | // HashedParams is a subset of ConsensusParams. 76 | // 77 | // It is hashed into the Header.ConsensusHash. 78 | message HashedParams { 79 | int64 block_max_bytes = 1; 80 | int64 block_max_gas = 2; 81 | } 82 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | import "tendermint/libs/bits/types.proto"; 9 | import "tendermint/crypto/proof.proto"; 10 | import "tendermint/version/types.proto"; 11 | import "tendermint/types/validator.proto"; 12 | 13 | // BlockIdFlag indicates which BlcokID the signature is for 14 | enum BlockIDFlag { 15 | option (gogoproto.goproto_enum_stringer) = true; 16 | option (gogoproto.goproto_enum_prefix) = false; 17 | 18 | BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; 19 | BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; 20 | BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; 21 | BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; 22 | } 23 | 24 | // SignedMsgType is a type of signed message in the consensus. 25 | enum SignedMsgType { 26 | option (gogoproto.goproto_enum_stringer) = true; 27 | option (gogoproto.goproto_enum_prefix) = false; 28 | 29 | SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; 30 | // Votes 31 | SIGNED_MSG_TYPE_PREVOTE = 1 [(gogoproto.enumvalue_customname) = "PrevoteType"]; 32 | SIGNED_MSG_TYPE_PRECOMMIT = 2 [(gogoproto.enumvalue_customname) = "PrecommitType"]; 33 | 34 | // Proposals 35 | SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; 36 | } 37 | 38 | // PartsetHeader 39 | message PartSetHeader { 40 | uint32 total = 1; 41 | bytes hash = 2; 42 | } 43 | 44 | message Part { 45 | uint32 index = 1; 46 | bytes bytes = 2; 47 | tendermint.crypto.Proof proof = 3 [(gogoproto.nullable) = false]; 48 | } 49 | 50 | // BlockID 51 | message BlockID { 52 | bytes hash = 1; 53 | PartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; 54 | } 55 | 56 | // -------------------------------- 57 | 58 | // Header defines the structure of a Tendermint block header. 59 | message Header { 60 | // basic block info 61 | tendermint.version.Consensus version = 1 [(gogoproto.nullable) = false]; 62 | string chain_id = 2 [(gogoproto.customname) = "ChainID"]; 63 | int64 height = 3; 64 | google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 65 | 66 | // prev block info 67 | BlockID last_block_id = 5 [(gogoproto.nullable) = false]; 68 | 69 | // hashes of block data 70 | bytes last_commit_hash = 6; // commit from validators from the last block 71 | bytes data_hash = 7; // transactions 72 | 73 | // hashes from the app output from the prev block 74 | bytes validators_hash = 8; // validators for the current block 75 | bytes next_validators_hash = 9; // validators for the next block 76 | bytes consensus_hash = 10; // consensus params for current block 77 | bytes app_hash = 11; // state after txs from the previous block 78 | bytes last_results_hash = 12; // root hash of all results from the txs from the previous block 79 | 80 | // consensus info 81 | bytes evidence_hash = 13; // evidence included in the block 82 | bytes proposer_address = 14; // original proposer of the block 83 | } 84 | 85 | // Data contains the set of transactions included in the block 86 | message Data { 87 | // Txs that will be applied by state @ block.Height+1. 88 | // NOTE: not all txs here are valid. We're just agreeing on the order first. 89 | // This means that block.AppHash does not include these txs. 90 | repeated bytes txs = 1; 91 | // Volatile 92 | bytes hash = 2; 93 | } 94 | 95 | // Vote represents a prevote, precommit, or commit vote from validators for 96 | // consensus. 97 | message Vote { 98 | SignedMsgType type = 1; 99 | int64 height = 2; 100 | int32 round = 3; 101 | BlockID block_id = 4 102 | [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. 103 | google.protobuf.Timestamp timestamp = 5 104 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 105 | bytes validator_address = 6; 106 | int32 validator_index = 7; 107 | bytes signature = 8; 108 | } 109 | 110 | // Commit contains the evidence that a block was committed by a set of validators. 111 | message Commit { 112 | int64 height = 1; 113 | int32 round = 2; 114 | BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; 115 | repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; 116 | bytes hash = 5; 117 | tendermint.libs.bits.BitArray bit_array = 6; 118 | } 119 | 120 | // CommitSig is a part of the Vote included in a Commit. 121 | message CommitSig { 122 | BlockIDFlag block_id_flag = 1; 123 | bytes validator_address = 2; 124 | google.protobuf.Timestamp timestamp = 3 125 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 126 | bytes signature = 4; 127 | } 128 | 129 | message Proposal { 130 | SignedMsgType type = 1; 131 | int64 height = 2; 132 | int32 round = 3; 133 | int32 pol_round = 4; 134 | BlockID block_id = 5 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; 135 | google.protobuf.Timestamp timestamp = 6 136 | [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; 137 | bytes signature = 7; 138 | } 139 | 140 | message SignedHeader { 141 | Header header = 1; 142 | Commit commit = 2; 143 | } 144 | 145 | message LightBlock { 146 | SignedHeader signed_header = 1; 147 | tendermint.types.ValidatorSet validator_set = 2; 148 | } 149 | 150 | message BlockMeta { 151 | BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; 152 | int64 block_size = 2; 153 | Header header = 3 [(gogoproto.nullable) = false]; 154 | int64 num_txs = 4; 155 | } 156 | 157 | // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. 158 | message TxProof { 159 | bytes root_hash = 1; 160 | bytes data = 2; 161 | tendermint.crypto.Proof proof = 3; 162 | } 163 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/types/validator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.types; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "tendermint/crypto/keys.proto"; 8 | 9 | message ValidatorSet { 10 | repeated Validator validators = 1; 11 | Validator proposer = 2; 12 | int64 total_voting_power = 3; 13 | } 14 | 15 | message Validator { 16 | bytes address = 1; 17 | tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; 18 | int64 voting_power = 3; 19 | int64 proposer_priority = 4; 20 | } 21 | 22 | message SimpleValidator { 23 | tendermint.crypto.PublicKey pub_key = 1; 24 | int64 voting_power = 2; 25 | } 26 | -------------------------------------------------------------------------------- /third_party/proto/tendermint/version/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package tendermint.version; 3 | 4 | option go_package = "github.com/tendermint/tendermint/proto/tendermint/version"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | 8 | // App includes the protocol and software version for the application. 9 | // This information is included in ResponseInfo. The App.Protocol can be 10 | // updated in ResponseEndBlock. 11 | message App { 12 | uint64 protocol = 1; 13 | string software = 2; 14 | } 15 | 16 | // Consensus captures the consensus rules for processing a block in the blockchain, 17 | // including all blockchain data structures and the rules of the application's 18 | // state transition machine. 19 | message Consensus { 20 | option (gogoproto.equal) = true; 21 | 22 | uint64 block = 1; 23 | uint64 app = 2; 24 | } 25 | -------------------------------------------------------------------------------- /use_cases/README.md: -------------------------------------------------------------------------------- 1 | # Use Cases 2 | 3 | This section explores some of the common use cases that the dao/groups combo 4 | aims at fulfilling. 5 | 6 | ## Contents 7 | 8 | - [Chain-wide Governance](./governance.md) 9 | - [Multi-Sig Party](./multi-sig.md) 10 | - [Off-Chain Governance](./off-chain.md) -------------------------------------------------------------------------------- /use_cases/governance.md: -------------------------------------------------------------------------------- 1 | # Chain-wide Governance 2 | 3 | The almost archetypal use case for the new dao system is to obtain feature 4 | parity with the current governance module. 5 | 6 | # User story 7 | 8 | A community member, Alice, wants to submit an on-chain proposal to change a parameter, the average number of blocks per year, which is used to calculate the inflation rate for the chain. To do this Alice first asks in a chat forum discord for instance whether this is a good idea and something the community would like to see happen. There is some initial discussion to confirm that this is in fact something the community wants. Another community member, Bob, also offers to collaborate on the proposal. 9 | 10 | Alice and Bob have a zoom call and start working in a google doc to draft the proposal synchronously, after which Alice finishes the draft and Bob reviews her work. Alice then opens a pull request on the governance repo that includes the text document as well as the json message required to make the parameter proposal on chain. 11 | 12 | Alice solicits community feedback on the PR, sharing it to the Discord and among validators, and is asked to make some minor changes, which are completed before the PR is finalized and merged by the governance repo owner. 13 | Once the proposal has been finalized an IPFS hash of the README.md is added to the json. 14 | The proposal is then submitted on chain through the CLI and a Cosmos forum post is made to notify the community that the proposal has been submitted. Links to the forum post are then shared in various community channels and on twitter. The merits of the proposal are discussed in these respective channels and validators / ATOM holders vote. 15 | 16 | 17 | ## Basic Implementation 18 | 19 | In this situation, the staking account creates and manages a group containing 20 | validators and delegators with weights tracking the amount of tokens staked. The 21 | `dao` module can create a gov `Polity` in which members can make proposals to 22 | change params and spend community funds. Proposals contain abstract messages so 23 | it is up to the other modules, `params` and `distribution` to grant authority 24 | and safely update authority when it changes [NOTE: I'm not sure how the `authz` 25 | comes into this]. 26 | 27 | The `ProposalFilter` is used to ensure a minimum deposit is fulfilled. A 28 | proposal should include a transfer of the deposit back to the proposer. 29 | 30 | The `DecisionPolicy` should use a `ThresholdPolicy` 31 | 32 | ## Further Options 33 | 34 | 1. **Creating a council** - the chain may want to delegate responsibility for 35 | certain parts of the network to be managed to a subgroup. To do this you 36 | would: 37 | - Create a new proposal with the messages to create a new group with the 38 | members and weights of the council members. Set the admin as the parent 39 | `Polity` so that they can update the member set when needed. Add the create 40 | polity message setting the parent chain as -------------------------------------------------------------------------------- /use_cases/multi-sig.md: -------------------------------------------------------------------------------- 1 | # Multi-sign Party -------------------------------------------------------------------------------- /use_cases/off-chain.md: -------------------------------------------------------------------------------- 1 | # Off-Chain Governance 2 | -------------------------------------------------------------------------------- /x/dao/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | // "strings" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/cosmos/cosmos-sdk/client" 10 | // "github.com/cosmos/cosmos-sdk/client/flags" 11 | // sdk "github.com/cosmos/cosmos-sdk/types" 12 | 13 | "github.com/interchainberlin/metachain/x/dao/types" 14 | ) 15 | 16 | // GetQueryCmd returns the cli query commands for this module 17 | func GetQueryCmd(queryRoute string) *cobra.Command { 18 | // Group metachain queries under a subcommand 19 | cmd := &cobra.Command{ 20 | Use: types.ModuleName, 21 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 22 | DisableFlagParsing: true, 23 | SuggestionsMinimumDistance: 2, 24 | RunE: client.ValidateCmd, 25 | } 26 | 27 | // this line is used by starport scaffolding # 1 28 | 29 | return cmd 30 | } 31 | 32 | -------------------------------------------------------------------------------- /x/dao/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | // "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/interchainberlin/metachain/x/dao/types" 11 | ) 12 | 13 | // GetTxCmd returns the transaction commands for this module 14 | func GetTxCmd() *cobra.Command { 15 | cmd := &cobra.Command{ 16 | Use: types.ModuleName, 17 | Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), 18 | DisableFlagParsing: true, 19 | SuggestionsMinimumDistance: 2, 20 | RunE: client.ValidateCmd, 21 | } 22 | 23 | // this line is used by starport scaffolding # 1 24 | 25 | return cmd 26 | } 27 | -------------------------------------------------------------------------------- /x/dao/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | 6 | "github.com/cosmos/cosmos-sdk/client" 7 | // this line is used by starport scaffolding # 1 8 | ) 9 | 10 | const ( 11 | MethodGet = "GET" 12 | ) 13 | 14 | // RegisterRoutes registers metachain-related REST handlers to a router 15 | func RegisterRoutes(clientCtx client.Context, r *mux.Router) { 16 | // this line is used by starport scaffolding # 2 17 | } 18 | 19 | func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { 20 | // this line is used by starport scaffolding # 3 21 | } 22 | 23 | func registerTxHandlers(clientCtx client.Context, r *mux.Router) { 24 | // this line is used by starport scaffolding # 4 25 | } 26 | 27 | -------------------------------------------------------------------------------- /x/dao/exported/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package exported -------------------------------------------------------------------------------- /x/dao/genesis.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/interchainberlin/metachain/x/dao/keeper" 6 | "github.com/interchainberlin/metachain/x/dao/types" 7 | ) 8 | 9 | // InitGenesis initializes the capability module's state from a provided genesis 10 | // state. 11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { 12 | } 13 | 14 | // ExportGenesis returns the capability module's exported genesis. 15 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { 16 | return types.DefaultGenesis() 17 | } 18 | -------------------------------------------------------------------------------- /x/dao/handler.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/interchainberlin/metachain/x/dao/keeper" 8 | "github.com/interchainberlin/metachain/x/dao/types" 9 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 10 | ) 11 | 12 | // NewHandler ... 13 | func NewHandler(k keeper.Keeper) sdk.Handler { 14 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 15 | ctx = ctx.WithEventManager(sdk.NewEventManager()) 16 | 17 | switch msg := msg.(type) { 18 | // this line is used by starport scaffolding # 1 19 | default: 20 | errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg) 21 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /x/dao/keeper/grpc_query.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "github.com/interchainberlin/metachain/x/dao/types" 5 | ) 6 | 7 | var _ types.QueryServer = Keeper{} 8 | -------------------------------------------------------------------------------- /x/dao/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/tendermint/tendermint/libs/log" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/interchainberlin/metachain/x/dao/types" 11 | ) 12 | 13 | type ( 14 | Keeper struct { 15 | cdc codec.Marshaler 16 | storeKey sdk.StoreKey 17 | memKey sdk.StoreKey 18 | } 19 | ) 20 | 21 | func NewKeeper(cdc codec.Marshaler, storeKey, memKey sdk.StoreKey) *Keeper { 22 | return &Keeper{ 23 | cdc: cdc, 24 | storeKey: storeKey, 25 | memKey: memKey, 26 | } 27 | } 28 | 29 | func (k Keeper) Logger(ctx sdk.Context) log.Logger { 30 | return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) 31 | } 32 | -------------------------------------------------------------------------------- /x/dao/keeper/querier.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | // this line is used by starport scaffolding # 1 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 9 | "github.com/interchainberlin/metachain/x/dao/types" 10 | 11 | abci "github.com/tendermint/tendermint/abci/types" 12 | ) 13 | 14 | func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { 15 | return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { 16 | var ( 17 | res []byte 18 | err error 19 | ) 20 | 21 | switch path[0] { 22 | // this line is used by starport scaffolding # 2 23 | default: 24 | err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) 25 | } 26 | 27 | return res, err 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /x/dao/keys.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | const ( 4 | // ModuleName is the module name constant used in many places 5 | ModuleName = "dao" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | DefaultParamspace = ModuleName 11 | 12 | // RouterKey defines the module's message routing key 13 | RouterKey = ModuleName 14 | ) 15 | -------------------------------------------------------------------------------- /x/dao/module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/rand" 7 | 8 | "github.com/gorilla/mux" 9 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 10 | "github.com/spf13/cobra" 11 | 12 | sdkclient "github.com/cosmos/cosmos-sdk/client" 13 | "github.com/cosmos/cosmos-sdk/codec" 14 | "github.com/cosmos/cosmos-sdk/codec/types" 15 | sdk "github.com/cosmos/cosmos-sdk/types" 16 | "github.com/cosmos/cosmos-sdk/types/module" 17 | 18 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 19 | 20 | "github.com/interchainberlin/metachain/x/dao" 21 | 22 | "github.com/interchainberlin/metachain/x/group" 23 | "github.com/interchainberlin/metachain/x/group/client" 24 | climodule "github.com/regen-network/regen-ledger/types/module/client/cli" 25 | 26 | "github.com/interchainberlin/metachain/x/group/exported" 27 | "github.com/interchainberlin/metachain/x/group/server" 28 | "github.com/interchainberlin/metachain/x/group/simulation" 29 | 30 | servermodule "github.com/regen-network/regen-ledger/types/module/server" 31 | ) 32 | 33 | type Module struct { 34 | Registry types.InterfaceRegistry 35 | BankKeeper exported.BankKeeper 36 | AccountKeeper exported.AccountKeeper 37 | } 38 | 39 | var _ module.AppModuleBasic = Module{} 40 | var _ module.AppModuleSimulation = Module{} 41 | var _ servermodule.Module = Module{} 42 | var _ climodule.Module = Module{} 43 | var _ servermodule.LegacyRouteModule = Module{} 44 | 45 | func (a Module) Name() string { 46 | return dao.ModuleName 47 | } 48 | 49 | // RegisterInterfaces registers module concrete types into protobuf Any. 50 | func (a Module) RegisterInterfaces(registry types.InterfaceRegistry) { 51 | group.RegisterTypes(registry) 52 | } 53 | 54 | func (a Module) RegisterServices(configurator servermodule.Configurator) { 55 | server.RegisterServices(configurator, a.AccountKeeper, a.BankKeeper) 56 | } 57 | 58 | func (a Module) DefaultGenesis(marshaler codec.JSONMarshaler) json.RawMessage { 59 | return marshaler.MustMarshalJSON(group.NewGenesisState()) 60 | } 61 | 62 | func (a Module) ValidateGenesis(cdc codec.JSONMarshaler, config sdkclient.TxEncodingConfig, bz json.RawMessage) error { 63 | var data group.GenesisState 64 | if err := cdc.UnmarshalJSON(bz, &data); err != nil { 65 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", group.ModuleName, err) 66 | } 67 | return data.Validate() 68 | } 69 | 70 | func (a Module) RegisterGRPCGatewayRoutes(sdkclient.Context, *runtime.ServeMux) {} 71 | 72 | func (a Module) GetTxCmd() *cobra.Command { 73 | return client.TxCmd(a.Name()) 74 | } 75 | 76 | func (a Module) GetQueryCmd() *cobra.Command { 77 | return client.QueryCmd(a.Name()) 78 | } 79 | 80 | /**** DEPRECATED ****/ 81 | func (a Module) RegisterRESTRoutes(sdkclient.Context, *mux.Router) {} 82 | func (a Module) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 83 | group.RegisterLegacyAminoCodec(cdc) 84 | } 85 | 86 | func (a Module) Route(configurator servermodule.Configurator) sdk.Route { 87 | return sdk.NewRoute(group.RouterKey, server.NewHandler(configurator, a.AccountKeeper, a.BankKeeper)) 88 | } 89 | 90 | // AppModuleSimulation functions 91 | 92 | // GenerateGenesisState creates a randomized GenesisState of the group module. 93 | func (Module) GenerateGenesisState(simState *module.SimulationState) { 94 | simulation.RandomizedGenState(simState) 95 | } 96 | 97 | // ProposalContents returns all the group content functions used to 98 | // simulate proposals. 99 | func (Module) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { 100 | return nil 101 | } 102 | 103 | // RandomizedParams creates randomized group param changes for the simulator. 104 | func (Module) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { 105 | return nil 106 | } 107 | 108 | // RegisterStoreDecoder registers a decoder for group module's types 109 | func (Module) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 110 | } 111 | 112 | // WeightedOperations returns all the group module operations with their respective weights. 113 | // NOTE: This is no longer needed for the modules which uses ADR-33, group module `WeightedOperations` 114 | // registered in the `x/group/server` package. 115 | func (Module) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /x/dao/server/server.go: -------------------------------------------------------------------------------- 1 | package server -------------------------------------------------------------------------------- /x/dao/spec/00_diff.md: -------------------------------------------------------------------------------- 1 | # Diff 2 | 3 | This page tries to capture the difference between the existing 4 | [Gov](https://github.com/cosmos/cosmos-sdk/tree/v0.42.4/x/gov/spec) module and 5 | this DAO one. 6 | 7 | Note that there is a considerable change in what the DAO module tries to achieve 8 | from the outset. Gov may be considered the governance of the entire chain as a 9 | whole only whereas DAO represents the toolkit for groups to govern one another 10 | where one group most likely represents the chain as a whole (the staking group). 11 | Some of the diff described here may be more in relation to the groups module 12 | which also housed data structures such as `Vote` and `Proposal` 13 | 14 | ## Proposal Filter 15 | 16 | This is a relatively novel feature (taken from 17 | [PolicyKit](https://policykit.readthedocs.io/en/latest/policy_model.html#filter)). 18 | Predominantly this allows for categorization of proposals and thus a divide and 19 | conquer form of governannce or representational forms of governance. A standard 20 | example might be to create a sub group that oversees only specific param 21 | proposals. It can also be used as a means of mitigating spam or settting other 22 | constrains (i.e. a max withdrawal amount). The interface: 23 | 24 | ```golang 25 | type ProposalFilter interface { 26 | Validate(group GroupAccount, proposal Proposal, otherProposals []Proposal) error 27 | } 28 | ``` 29 | 30 | Also allows for comparison with other running proposals for logic that requires 31 | a more complete overview of the currently running proposals within a DAO. 32 | -------------------------------------------------------------------------------- /x/dao/spec/02_state.md: -------------------------------------------------------------------------------- 1 | # State -------------------------------------------------------------------------------- /x/dao/spec/03_messages.md: -------------------------------------------------------------------------------- 1 | # Messages 2 | 3 | ## MsgCreatePolity 4 | 5 | ```proto 6 | message MsgCreatePolityRequest { 7 | string group_address = 1; 8 | 9 | // decision_policy is the updated group account decision policy. 10 | google.protobuf.Any decision_policy = 2 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 11 | 12 | // proposal_filter filters incoming proposals. 13 | google.protobuf.Any proposal_filter = 3 [(cosmos_proto.accepts_interface) = "ProposalFilter"]; 14 | 15 | // the voting window for each proposal 16 | google.protobuf.Duration voting_period = 4; 17 | 18 | // indicates whether proposals can be received from anyone or members only 19 | bool public = 5; 20 | } 21 | ``` 22 | 23 | `voting_period` must be less than `max_voting_period` (default: 3 weeks) 24 | 25 | This will update the admin of the group account to the address of the `dao` 26 | module itself 27 | 28 | ## MsgUpdatePolity 29 | 30 | ```proto 31 | message MsgUpdatePolityRequest { 32 | string address = 1; 33 | 34 | // decision_policy is the updated group account decision policy. 35 | google.protobuf.Any decision_policy = 2 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; 36 | 37 | // proposal_filter filters incoming proposals. 38 | google.protobuf.Any proposal_filter = 3 [(cosmos_proto.accepts_interface) = "ProposalFilter"]; 39 | 40 | // the voting window for each proposal 41 | google.protobuf.Duration voting_period = 4; 42 | 43 | // indicates whether proposals can be received from anyone or members only 44 | bool public = 5; 45 | 46 | // an optional parent polity which has administative power over the polity 47 | string parent_address = 6; 48 | } 49 | ``` 50 | 51 | ## MsgDestroyPolity 52 | 53 | ```proto 54 | message MsgDestroyPolityRequest{ 55 | string address = 1; 56 | } 57 | ``` 58 | 59 | ## MsgCreateProposal 60 | 61 | ```proto 62 | message MsgCreateProposalRequest { 63 | option (gogoproto.goproto_getters) = false; 64 | 65 | // group address is the group account address. 66 | string address = 1; 67 | 68 | // proposer is the account address of the proposers. 69 | string proposer = 2; 70 | 71 | // the deposit associated with the proposal 72 | repeated cosmos.base.v1beta1.Coin deposit = 3; 73 | 74 | // content represents some endpoint which can lead 75 | // a discussion / forum or simple state the case for // the proposal 76 | string content = 5; 77 | 78 | // metadata is any arbitrary metadata to attached to the proposal. 79 | bytes metadata = 6; 80 | 81 | // messages is a list of Msgs that will be executed if the proposal passes. 82 | repeated google.protobuf.Any messages = 7; 83 | } 84 | ``` 85 | 86 | ## MsgAmendProposal 87 | 88 | ```proto 89 | message MsgAmendProposalRequest { 90 | // unique id of the proposal 91 | uint64 proposal_id = 1; 92 | 93 | // content represents some endpoint which can lead 94 | // a discussion / forum or simple state the case for // the proposal 95 | string content = 2; 96 | 97 | // metadata is any arbitrary metadata to attached to the proposal. 98 | bytes metadata = 3; 99 | 100 | // messages is a list of Msgs that will be executed if the proposal passes. 101 | repeated google.protobuf.Any messages = 4; 102 | } 103 | ``` 104 | 105 | The signer of the message must be the proposer of that proposal. Any field left 106 | empty leaves that part of the proposal untouched. The amendment must pass 107 | `ProposalFilter.Amend` function without error. 108 | 109 | ## MsgWithdrawProposal 110 | 111 | ```proto 112 | message MsgWithdrawProposalRequest { 113 | uint64 proposal_id = 1; 114 | } 115 | ``` 116 | 117 | The signer of the message must be the proposer of that proposal. In addition the 118 | proposal must pass the `ProposalFilter.Withdraw()` function without an error. 119 | 120 | ## MsgVote 121 | 122 | ```proto 123 | // MsgVoteRequest is the Msg/Vote request type. 124 | message MsgVoteRequest { 125 | // proposal is the unique ID of the proposal. 126 | uint64 proposal_id = 1; 127 | 128 | // voter is the voter's address. 129 | string voter = 2; 130 | 131 | // choice is the voter's choice on the proposal. 132 | Choice choice = 3; 133 | 134 | // the weight behind their choice. This defaults to the entire weight 135 | // of the member if none is described. This allows for split voting. 136 | string weight = 4; 137 | } 138 | ``` 139 | 140 | The `proposal_id` must be for a valid and open proposal, the voter must be a 141 | member of the group corresponding to the proposal. A weight of 0 corresponds to 142 | the entire weight of a member. 143 | 144 | > NOTE: There's nothing stopping a member from voting multiple times. The 145 | > decision policy is in charge of correctly handling multiple votes. In the 146 | > future we may want to support secret ballots. They most often involve two 147 | > votes, one for each phase. 148 | 149 | ## MsgDelegate 150 | 151 | ```proto 152 | message MsgDelegateVoteRequest { 153 | // the account transferring their voting power 154 | string delegator = 1; 155 | 156 | // the receiving of the voting power 157 | string candidate = 2; 158 | 159 | // the weight they are transferring the delegation to 160 | string weight = 3; 161 | } 162 | ``` 163 | 164 | To delegate, both the `delegator` and `candidate` must be part of the group and 165 | the `weight` must be less than or equal to the `delegator`'s total weight. A 166 | weight of 0 implies the `delegator`'s total weight. 167 | 168 | > NOTE: We may want to introduce rules to prevent delegation circle i.e. I 169 | > delegate to you and you delegate to me. 170 | 171 | ## MsgUndelegate 172 | 173 | ```proto 174 | message MsgUndelegateRequest { 175 | // the delegator 176 | string delegator = 1; 177 | 178 | // the address being removed of the delegation 179 | string candidate = 2; 180 | 181 | // the weight they are transferring the delegation to 182 | string weight = 3; 183 | } 184 | ``` 185 | 186 | To undelegate, a delegator must have already delegated greater than or equal to 187 | `weight` of their weight. 188 | 189 | # MsgExec 190 | 191 | 192 | ```proto 193 | // MsgExecRequest is the Msg/Exec request type. 194 | message MsgExecRequest { 195 | 196 | // proposal is the unique ID of the proposal. 197 | uint64 proposal_id = 1; 198 | 199 | // signer is the account address used to execute the proposal. 200 | string signer = 2; 201 | } 202 | ``` -------------------------------------------------------------------------------- /x/dao/spec/04_events.md: -------------------------------------------------------------------------------- 1 | # Events -------------------------------------------------------------------------------- /x/dao/spec/README.md: -------------------------------------------------------------------------------- 1 | # DAO 2 | 3 | ## Content 4 | 5 | - [Concepts](./01_concepts.md) -------------------------------------------------------------------------------- /x/dao/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 6 | // this line is used by starport scaffolding # 1 7 | ) 8 | 9 | func RegisterCodec(cdc *codec.LegacyAmino) { 10 | // this line is used by starport scaffolding # 2 11 | } 12 | 13 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { 14 | // this line is used by starport scaffolding # 3 15 | } 16 | 17 | var ( 18 | amino = codec.NewLegacyAmino() 19 | ModuleCdc = codec.NewAminoCodec(amino) 20 | ) 21 | -------------------------------------------------------------------------------- /x/dao/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | ) 8 | 9 | // x/metachain module sentinel errors 10 | var ( 11 | ErrSample = sdkerrors.Register(ModuleName, 1100, "sample error") 12 | ) 13 | -------------------------------------------------------------------------------- /x/dao/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // DefaultIndex is the default capability global index 4 | const DefaultIndex uint64 = 1 5 | 6 | // DefaultGenesis returns the default Capability genesis state 7 | func DefaultGenesis() *GenesisState { 8 | return &GenesisState{} 9 | } 10 | 11 | // Validate performs basic genesis state validation returning an error upon any 12 | // failure. 13 | func (gs GenesisState) Validate() error { 14 | return nil 15 | } 16 | -------------------------------------------------------------------------------- /x/dao/types/genesis.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: metachain/v1beta/genesis.proto 3 | 4 | package types 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/gogo/protobuf/proto" 9 | io "io" 10 | math "math" 11 | math_bits "math/bits" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | // This is a compile-time assertion to ensure that this generated file 20 | // is compatible with the proto package it is being compiled against. 21 | // A compilation error at this line likely means your copy of the 22 | // proto package needs to be updated. 23 | const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package 24 | 25 | // GenesisState defines the capability module's genesis state. 26 | type GenesisState struct { 27 | } 28 | 29 | func (m *GenesisState) Reset() { *m = GenesisState{} } 30 | func (m *GenesisState) String() string { return proto.CompactTextString(m) } 31 | func (*GenesisState) ProtoMessage() {} 32 | func (*GenesisState) Descriptor() ([]byte, []int) { 33 | return fileDescriptor_a3fb1317fb18a255, []int{0} 34 | } 35 | func (m *GenesisState) XXX_Unmarshal(b []byte) error { 36 | return m.Unmarshal(b) 37 | } 38 | func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 39 | if deterministic { 40 | return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) 41 | } else { 42 | b = b[:cap(b)] 43 | n, err := m.MarshalToSizedBuffer(b) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return b[:n], nil 48 | } 49 | } 50 | func (m *GenesisState) XXX_Merge(src proto.Message) { 51 | xxx_messageInfo_GenesisState.Merge(m, src) 52 | } 53 | func (m *GenesisState) XXX_Size() int { 54 | return m.Size() 55 | } 56 | func (m *GenesisState) XXX_DiscardUnknown() { 57 | xxx_messageInfo_GenesisState.DiscardUnknown(m) 58 | } 59 | 60 | var xxx_messageInfo_GenesisState proto.InternalMessageInfo 61 | 62 | func init() { 63 | proto.RegisterType((*GenesisState)(nil), "metachain.metachain.v1beta1.GenesisState") 64 | } 65 | 66 | func init() { proto.RegisterFile("metachain/v1beta/genesis.proto", fileDescriptor_a3fb1317fb18a255) } 67 | 68 | var fileDescriptor_a3fb1317fb18a255 = []byte{ 69 | // 148 bytes of a gzipped FileDescriptorProto 70 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcb, 0x4d, 0x2d, 0x49, 71 | 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0xd4, 0x4f, 0x4f, 0xcd, 72 | 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xcb, 0xeb, 0x21, 73 | 0x58, 0x10, 0x95, 0x86, 0x4a, 0x7c, 0x5c, 0x3c, 0xee, 0x10, 0xd5, 0xc1, 0x25, 0x89, 0x25, 0xa9, 74 | 0x4e, 0x81, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 75 | 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x9e, 0x9e, 0x59, 76 | 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x99, 0x57, 0x92, 0x5a, 0x04, 0x36, 0x28, 77 | 0x29, 0xb5, 0x28, 0x27, 0x33, 0x4f, 0x1f, 0xe1, 0x84, 0x0a, 0x24, 0x76, 0x49, 0x65, 0x41, 0x6a, 78 | 0x71, 0x12, 0x1b, 0xd8, 0x19, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf7, 0xb1, 0x16, 0x46, 79 | 0xa8, 0x00, 0x00, 0x00, 80 | } 81 | 82 | func (m *GenesisState) Marshal() (dAtA []byte, err error) { 83 | size := m.Size() 84 | dAtA = make([]byte, size) 85 | n, err := m.MarshalToSizedBuffer(dAtA[:size]) 86 | if err != nil { 87 | return nil, err 88 | } 89 | return dAtA[:n], nil 90 | } 91 | 92 | func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { 93 | size := m.Size() 94 | return m.MarshalToSizedBuffer(dAtA[:size]) 95 | } 96 | 97 | func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { 98 | i := len(dAtA) 99 | _ = i 100 | var l int 101 | _ = l 102 | return len(dAtA) - i, nil 103 | } 104 | 105 | func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { 106 | offset -= sovGenesis(v) 107 | base := offset 108 | for v >= 1<<7 { 109 | dAtA[offset] = uint8(v&0x7f | 0x80) 110 | v >>= 7 111 | offset++ 112 | } 113 | dAtA[offset] = uint8(v) 114 | return base 115 | } 116 | func (m *GenesisState) Size() (n int) { 117 | if m == nil { 118 | return 0 119 | } 120 | var l int 121 | _ = l 122 | return n 123 | } 124 | 125 | func sovGenesis(x uint64) (n int) { 126 | return (math_bits.Len64(x|1) + 6) / 7 127 | } 128 | func sozGenesis(x uint64) (n int) { 129 | return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 130 | } 131 | func (m *GenesisState) Unmarshal(dAtA []byte) error { 132 | l := len(dAtA) 133 | iNdEx := 0 134 | for iNdEx < l { 135 | preIndex := iNdEx 136 | var wire uint64 137 | for shift := uint(0); ; shift += 7 { 138 | if shift >= 64 { 139 | return ErrIntOverflowGenesis 140 | } 141 | if iNdEx >= l { 142 | return io.ErrUnexpectedEOF 143 | } 144 | b := dAtA[iNdEx] 145 | iNdEx++ 146 | wire |= uint64(b&0x7F) << shift 147 | if b < 0x80 { 148 | break 149 | } 150 | } 151 | fieldNum := int32(wire >> 3) 152 | wireType := int(wire & 0x7) 153 | if wireType == 4 { 154 | return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") 155 | } 156 | if fieldNum <= 0 { 157 | return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) 158 | } 159 | switch fieldNum { 160 | default: 161 | iNdEx = preIndex 162 | skippy, err := skipGenesis(dAtA[iNdEx:]) 163 | if err != nil { 164 | return err 165 | } 166 | if skippy < 0 { 167 | return ErrInvalidLengthGenesis 168 | } 169 | if (iNdEx + skippy) < 0 { 170 | return ErrInvalidLengthGenesis 171 | } 172 | if (iNdEx + skippy) > l { 173 | return io.ErrUnexpectedEOF 174 | } 175 | iNdEx += skippy 176 | } 177 | } 178 | 179 | if iNdEx > l { 180 | return io.ErrUnexpectedEOF 181 | } 182 | return nil 183 | } 184 | func skipGenesis(dAtA []byte) (n int, err error) { 185 | l := len(dAtA) 186 | iNdEx := 0 187 | depth := 0 188 | for iNdEx < l { 189 | var wire uint64 190 | for shift := uint(0); ; shift += 7 { 191 | if shift >= 64 { 192 | return 0, ErrIntOverflowGenesis 193 | } 194 | if iNdEx >= l { 195 | return 0, io.ErrUnexpectedEOF 196 | } 197 | b := dAtA[iNdEx] 198 | iNdEx++ 199 | wire |= (uint64(b) & 0x7F) << shift 200 | if b < 0x80 { 201 | break 202 | } 203 | } 204 | wireType := int(wire & 0x7) 205 | switch wireType { 206 | case 0: 207 | for shift := uint(0); ; shift += 7 { 208 | if shift >= 64 { 209 | return 0, ErrIntOverflowGenesis 210 | } 211 | if iNdEx >= l { 212 | return 0, io.ErrUnexpectedEOF 213 | } 214 | iNdEx++ 215 | if dAtA[iNdEx-1] < 0x80 { 216 | break 217 | } 218 | } 219 | case 1: 220 | iNdEx += 8 221 | case 2: 222 | var length int 223 | for shift := uint(0); ; shift += 7 { 224 | if shift >= 64 { 225 | return 0, ErrIntOverflowGenesis 226 | } 227 | if iNdEx >= l { 228 | return 0, io.ErrUnexpectedEOF 229 | } 230 | b := dAtA[iNdEx] 231 | iNdEx++ 232 | length |= (int(b) & 0x7F) << shift 233 | if b < 0x80 { 234 | break 235 | } 236 | } 237 | if length < 0 { 238 | return 0, ErrInvalidLengthGenesis 239 | } 240 | iNdEx += length 241 | case 3: 242 | depth++ 243 | case 4: 244 | if depth == 0 { 245 | return 0, ErrUnexpectedEndOfGroupGenesis 246 | } 247 | depth-- 248 | case 5: 249 | iNdEx += 4 250 | default: 251 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 252 | } 253 | if iNdEx < 0 { 254 | return 0, ErrInvalidLengthGenesis 255 | } 256 | if depth == 0 { 257 | return iNdEx, nil 258 | } 259 | } 260 | return 0, io.ErrUnexpectedEOF 261 | } 262 | 263 | var ( 264 | ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") 265 | ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") 266 | ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") 267 | ) 268 | -------------------------------------------------------------------------------- /x/dao/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "metachain" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | // RouterKey is the message route for slashing 11 | RouterKey = ModuleName 12 | 13 | // QuerierRoute defines the module's query routing key 14 | QuerierRoute = ModuleName 15 | 16 | // MemStoreKey defines the in-memory store key 17 | MemStoreKey = "mem_capability" 18 | ) 19 | 20 | func KeyPrefix(p string) []byte { 21 | return []byte(p) 22 | } 23 | -------------------------------------------------------------------------------- /x/dao/types/querier.go: -------------------------------------------------------------------------------- 1 | package types 2 | -------------------------------------------------------------------------------- /x/dao/types/querier.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: metachain/v1beta/querier.proto 3 | 4 | package types 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | _ "github.com/cosmos/cosmos-sdk/types/query" 10 | grpc1 "github.com/gogo/protobuf/grpc" 11 | proto "github.com/gogo/protobuf/proto" 12 | grpc "google.golang.org/grpc" 13 | math "math" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | // This is a compile-time assertion to ensure that this generated file 22 | // is compatible with the proto package it is being compiled against. 23 | // A compilation error at this line likely means your copy of the 24 | // proto package needs to be updated. 25 | const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package 26 | 27 | func init() { proto.RegisterFile("metachain/v1beta/querier.proto", fileDescriptor_a4eb7742e5632b88) } 28 | 29 | var fileDescriptor_a4eb7742e5632b88 = []byte{ 30 | // 175 bytes of a gzipped FileDescriptorProto 31 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcb, 0x4d, 0x2d, 0x49, 32 | 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0xd4, 0x2f, 0x2c, 0x4d, 33 | 0x2d, 0xca, 0x4c, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0xcb, 0xeb, 0x21, 34 | 0x58, 0x10, 0x95, 0x86, 0x52, 0x5a, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x49, 0x89, 0xc5, 35 | 0xa9, 0x60, 0x7d, 0x95, 0x50, 0x43, 0x0c, 0xf5, 0x0b, 0x12, 0xd3, 0x33, 0xf3, 0x12, 0x4b, 0x32, 36 | 0xf3, 0xf3, 0x20, 0x06, 0x19, 0xb1, 0x73, 0xb1, 0x06, 0x82, 0x54, 0x38, 0x05, 0x9e, 0x78, 0x24, 37 | 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 38 | 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x79, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 39 | 0x72, 0x7e, 0xae, 0x7e, 0x66, 0x5e, 0x49, 0x6a, 0x11, 0xd8, 0xb6, 0xa4, 0xd4, 0xa2, 0x9c, 0xcc, 40 | 0x3c, 0x7d, 0x84, 0x3b, 0x2b, 0x90, 0xd8, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0x2b, 41 | 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb1, 0x01, 0x30, 0xb4, 0xcd, 0x00, 0x00, 0x00, 42 | } 43 | 44 | // Reference imports to suppress errors if they are not otherwise used. 45 | var _ context.Context 46 | var _ grpc.ClientConn 47 | 48 | // This is a compile-time assertion to ensure that this generated file 49 | // is compatible with the grpc package it is being compiled against. 50 | const _ = grpc.SupportPackageIsVersion4 51 | 52 | // QueryClient is the client API for Query service. 53 | // 54 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 55 | type QueryClient interface { 56 | } 57 | 58 | type queryClient struct { 59 | cc grpc1.ClientConn 60 | } 61 | 62 | func NewQueryClient(cc grpc1.ClientConn) QueryClient { 63 | return &queryClient{cc} 64 | } 65 | 66 | // QueryServer is the server API for Query service. 67 | type QueryServer interface { 68 | } 69 | 70 | // UnimplementedQueryServer can be embedded to have forward compatible implementations. 71 | type UnimplementedQueryServer struct { 72 | } 73 | 74 | func RegisterQueryServer(s grpc1.Server, srv QueryServer) { 75 | s.RegisterService(&_Query_serviceDesc, srv) 76 | } 77 | 78 | var _Query_serviceDesc = grpc.ServiceDesc{ 79 | ServiceName: "metachain.metachain.v1beta1.Query", 80 | HandlerType: (*QueryServer)(nil), 81 | Methods: []grpc.MethodDesc{}, 82 | Streams: []grpc.StreamDesc{}, 83 | Metadata: "metachain/v1beta/querier.proto", 84 | } 85 | -------------------------------------------------------------------------------- /x/dao/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | -------------------------------------------------------------------------------- /x/group/client/util.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/cosmos/cosmos-sdk/client" 7 | "github.com/interchainberlin/metachain/x/group" 8 | ) 9 | 10 | func parseMembers(clientCtx client.Context, membersFile string) ([]group.Member, error) { 11 | members := group.Members{} 12 | 13 | if membersFile == "" { 14 | return members.Members, nil 15 | } 16 | 17 | contents, err := ioutil.ReadFile(membersFile) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | err = clientCtx.JSONMarshaler.UnmarshalJSON(contents, &members) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return members.Members, nil 28 | } 29 | -------------------------------------------------------------------------------- /x/group/codec.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 6 | cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/types/msgservice" 9 | ) 10 | 11 | // RegisterLegacyAminoCodec registers all the necessary group module concrete 12 | // types and interfaces with the provided codec reference. 13 | // These types are used for Amino JSON serialization. 14 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 15 | cdc.RegisterInterface((*DecisionPolicy)(nil), nil) 16 | cdc.RegisterConcrete(&ThresholdDecisionPolicy{}, "cosmos-sdk/ThresholdDecisionPolicy", nil) 17 | cdc.RegisterConcrete(&MsgCreateGroupRequest{}, "cosmos-sdk/MsgCreateGroup", nil) 18 | cdc.RegisterConcrete(&MsgUpdateGroupMembersRequest{}, "cosmos-sdk/MsgUpdateGroupMembers", nil) 19 | cdc.RegisterConcrete(&MsgUpdateGroupAdminRequest{}, "cosmos-sdk/MsgUpdateGroupAdmin", nil) 20 | cdc.RegisterConcrete(&MsgUpdateGroupMetadataRequest{}, "cosmos-sdk/MsgUpdateGroupMetadata", nil) 21 | cdc.RegisterConcrete(&MsgCreateGroupAccountRequest{}, "cosmos-sdk/MsgCreateGroupAccount", nil) 22 | cdc.RegisterConcrete(&MsgUpdateGroupAccountAdminRequest{}, "cosmos-sdk/MsgUpdateGroupAccountAdmin", nil) 23 | cdc.RegisterConcrete(&MsgUpdateGroupAccountDecisionPolicyRequest{}, "cosmos-sdk/MsgUpdateGroupAccountDecisionPolicy", nil) 24 | cdc.RegisterConcrete(&MsgUpdateGroupAccountMetadataRequest{}, "cosmos-sdk/MsgUpdateGroupAccountMetadata", nil) 25 | cdc.RegisterConcrete(&MsgCreateProposalRequest{}, "cosmos-sdk/group/MsgCreateProposal", nil) 26 | cdc.RegisterConcrete(&MsgVoteRequest{}, "cosmos-sdk/group/MsgVote", nil) 27 | cdc.RegisterConcrete(&MsgExecRequest{}, "cosmos-sdk/group/MsgExec", nil) 28 | } 29 | 30 | func RegisterTypes(registry cdctypes.InterfaceRegistry) { 31 | registry.RegisterImplementations((*sdk.Msg)(nil), 32 | &MsgCreateGroupRequest{}, 33 | &MsgUpdateGroupMembersRequest{}, 34 | &MsgUpdateGroupAdminRequest{}, 35 | &MsgUpdateGroupMetadataRequest{}, 36 | &MsgCreateGroupAccountRequest{}, 37 | &MsgUpdateGroupAccountAdminRequest{}, 38 | &MsgUpdateGroupAccountDecisionPolicyRequest{}, 39 | &MsgUpdateGroupAccountMetadataRequest{}, 40 | &MsgCreateProposalRequest{}, 41 | &MsgVoteRequest{}, 42 | &MsgExecRequest{}, 43 | ) 44 | 45 | msgservice.RegisterMsgServiceDesc(registry, &Msg_ServiceDesc) 46 | 47 | registry.RegisterInterface( 48 | "regen.group.v1alpha1.DecisionPolicy", 49 | (*DecisionPolicy)(nil), 50 | &ThresholdDecisionPolicy{}, 51 | ) 52 | } 53 | 54 | var ( 55 | amino = codec.NewLegacyAmino() 56 | ModuleCdc = codec.NewAminoCodec(amino) 57 | ) 58 | 59 | func init() { 60 | RegisterLegacyAminoCodec(amino) 61 | cryptocodec.RegisterCrypto(amino) 62 | } 63 | -------------------------------------------------------------------------------- /x/group/errors.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import ( 4 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | var ( 8 | ErrEmpty = sdkerrors.Register(ModuleName, 202, "value is empty") 9 | ErrDuplicate = sdkerrors.Register(ModuleName, 203, "duplicate value") 10 | ErrMaxLimit = sdkerrors.Register(ModuleName, 204, "limit exceeded") 11 | ErrType = sdkerrors.Register(ModuleName, 205, "invalid type") 12 | ErrInvalid = sdkerrors.Register(ModuleName, 206, "invalid value") 13 | ErrUnauthorized = sdkerrors.Register(ModuleName, 207, "unauthorized") 14 | ErrModified = sdkerrors.Register(ModuleName, 208, "modified") 15 | ErrExpired = sdkerrors.Register(ModuleName, 209, "expired") 16 | ) 17 | -------------------------------------------------------------------------------- /x/group/exported/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package exported 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 6 | ) 7 | 8 | type AccountKeeper interface { 9 | // Return a new account with the next account number. Does not save the new account to the store. 10 | NewAccount(sdk.Context, authtypes.AccountI) authtypes.AccountI 11 | 12 | // Retrieve an account from the store. 13 | GetAccount(sdk.Context, sdk.AccAddress) authtypes.AccountI 14 | 15 | // Set an account in the store. 16 | SetAccount(sdk.Context, authtypes.AccountI) 17 | } 18 | 19 | // BankKeeper defines the expected interface needed to retrieve account balances. 20 | type BankKeeper interface { 21 | SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins 22 | } 23 | -------------------------------------------------------------------------------- /x/group/genesis.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import "github.com/cosmos/cosmos-sdk/codec/types" 4 | 5 | // NewGenesisState creates a new genesis state with default values. 6 | func NewGenesisState() *GenesisState { 7 | return &GenesisState{} 8 | } 9 | 10 | func (s GenesisState) Validate() error { 11 | return nil 12 | } 13 | 14 | // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 15 | func (s GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { 16 | for _, g := range s.GroupAccounts { 17 | err := g.UnpackInterfaces(unpacker) 18 | if err != nil { 19 | return err 20 | } 21 | } 22 | for _, p := range s.Proposals { 23 | err := p.UnpackInterfaces(unpacker) 24 | if err != nil { 25 | return err 26 | } 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /x/group/keys.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | const ( 4 | // ModuleName is the module name constant used in many places 5 | ModuleName = "group" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | DefaultParamspace = ModuleName 11 | 12 | // RouterKey defines the module's message routing key 13 | RouterKey = ModuleName 14 | ) 15 | -------------------------------------------------------------------------------- /x/group/module/module.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/rand" 7 | 8 | "github.com/gorilla/mux" 9 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 10 | "github.com/spf13/cobra" 11 | 12 | sdkclient "github.com/cosmos/cosmos-sdk/client" 13 | "github.com/cosmos/cosmos-sdk/codec" 14 | "github.com/cosmos/cosmos-sdk/codec/types" 15 | sdk "github.com/cosmos/cosmos-sdk/types" 16 | "github.com/cosmos/cosmos-sdk/types/module" 17 | 18 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 19 | "github.com/interchainberlin/metachain/x/group" 20 | "github.com/interchainberlin/metachain/x/group/client" 21 | climodule "github.com/regen-network/regen-ledger/types/module/client/cli" 22 | 23 | "github.com/interchainberlin/metachain/x/group/exported" 24 | "github.com/interchainberlin/metachain/x/group/server" 25 | "github.com/interchainberlin/metachain/x/group/simulation" 26 | 27 | servermodule "github.com/regen-network/regen-ledger/types/module/server" 28 | ) 29 | 30 | type Module struct { 31 | Registry types.InterfaceRegistry 32 | BankKeeper exported.BankKeeper 33 | AccountKeeper exported.AccountKeeper 34 | } 35 | 36 | var _ module.AppModuleBasic = Module{} 37 | var _ module.AppModuleSimulation = Module{} 38 | var _ servermodule.Module = Module{} 39 | var _ climodule.Module = Module{} 40 | var _ servermodule.LegacyRouteModule = Module{} 41 | 42 | func (a Module) Name() string { 43 | return group.ModuleName 44 | } 45 | 46 | // RegisterInterfaces registers module concrete types into protobuf Any. 47 | func (a Module) RegisterInterfaces(registry types.InterfaceRegistry) { 48 | group.RegisterTypes(registry) 49 | } 50 | 51 | func (a Module) RegisterServices(configurator servermodule.Configurator) { 52 | server.RegisterServices(configurator, a.AccountKeeper, a.BankKeeper) 53 | } 54 | 55 | func (a Module) DefaultGenesis(marshaler codec.JSONMarshaler) json.RawMessage { 56 | return marshaler.MustMarshalJSON(group.NewGenesisState()) 57 | } 58 | 59 | func (a Module) ValidateGenesis(cdc codec.JSONMarshaler, config sdkclient.TxEncodingConfig, bz json.RawMessage) error { 60 | var data group.GenesisState 61 | if err := cdc.UnmarshalJSON(bz, &data); err != nil { 62 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", group.ModuleName, err) 63 | } 64 | return data.Validate() 65 | } 66 | 67 | func (a Module) RegisterGRPCGatewayRoutes(sdkclient.Context, *runtime.ServeMux) {} 68 | 69 | func (a Module) GetTxCmd() *cobra.Command { 70 | return client.TxCmd(a.Name()) 71 | } 72 | 73 | func (a Module) GetQueryCmd() *cobra.Command { 74 | return client.QueryCmd(a.Name()) 75 | } 76 | 77 | /**** DEPRECATED ****/ 78 | func (a Module) RegisterRESTRoutes(sdkclient.Context, *mux.Router) {} 79 | func (a Module) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 80 | group.RegisterLegacyAminoCodec(cdc) 81 | } 82 | 83 | func (a Module) Route(configurator servermodule.Configurator) sdk.Route { 84 | return sdk.NewRoute(group.RouterKey, server.NewHandler(configurator, a.AccountKeeper, a.BankKeeper)) 85 | } 86 | 87 | // AppModuleSimulation functions 88 | 89 | // GenerateGenesisState creates a randomized GenesisState of the group module. 90 | func (Module) GenerateGenesisState(simState *module.SimulationState) { 91 | simulation.RandomizedGenState(simState) 92 | } 93 | 94 | // ProposalContents returns all the group content functions used to 95 | // simulate proposals. 96 | func (Module) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { 97 | return nil 98 | } 99 | 100 | // RandomizedParams creates randomized group param changes for the simulator. 101 | func (Module) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { 102 | return nil 103 | } 104 | 105 | // RegisterStoreDecoder registers a decoder for group module's types 106 | func (Module) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 107 | } 108 | 109 | // WeightedOperations returns all the group module operations with their respective weights. 110 | // NOTE: This is no longer needed for the modules which uses ADR-33, group module `WeightedOperations` 111 | // registered in the `x/group/server` package. 112 | func (Module) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /x/group/proposal.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import ( 4 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | "github.com/regen-network/regen-ledger/orm" 8 | ) 9 | 10 | func (p *Proposal) GetMsgs() []sdk.Msg { 11 | msgs := make([]sdk.Msg, len(p.Msgs)) 12 | for i, any := range p.Msgs { 13 | msg, ok := any.GetCachedValue().(sdk.Msg) 14 | if !ok { 15 | return nil 16 | } 17 | msgs[i] = msg 18 | } 19 | return msgs 20 | } 21 | 22 | func (p *Proposal) SetMsgs(new []sdk.Msg) error { 23 | p.Msgs = make([]*codectypes.Any, len(new)) 24 | for i := range new { 25 | if new[i] == nil { 26 | return sdkerrors.Wrap(ErrInvalid, "msg must not be nil") 27 | } 28 | any, err := codectypes.NewAnyWithValue(new[i]) 29 | if err != nil { 30 | return err 31 | } 32 | p.Msgs[i] = any 33 | } 34 | return nil 35 | } 36 | 37 | func (p Proposal) ValidateBasic() error { 38 | _, err := sdk.AccAddressFromBech32(p.Address) 39 | if err != nil { 40 | return sdkerrors.Wrap(err, "group account") 41 | } 42 | 43 | if len(p.Proposers) == 0 { 44 | return sdkerrors.Wrap(ErrEmpty, "proposers") 45 | } 46 | addrs := make([]sdk.AccAddress, len(p.Proposers)) 47 | for i, proposer := range p.Proposers { 48 | addr, err := sdk.AccAddressFromBech32(proposer) 49 | if err != nil { 50 | return sdkerrors.Wrap(err, "proposers") 51 | } 52 | addrs[i] = addr 53 | } 54 | if err := AccAddresses(addrs).ValidateBasic(); err != nil { 55 | return sdkerrors.Wrap(err, "proposers") 56 | } 57 | 58 | if p.SubmittedAt.Seconds == 0 && p.SubmittedAt.Nanos == 0 { 59 | return sdkerrors.Wrap(ErrEmpty, "submitted at") 60 | } 61 | if p.GroupVersion == 0 { 62 | return sdkerrors.Wrap(ErrEmpty, "group version") 63 | } 64 | if p.GroupAccountVersion == 0 { 65 | return sdkerrors.Wrap(ErrEmpty, "group account version") 66 | } 67 | if p.Status == ProposalStatusInvalid { 68 | return sdkerrors.Wrap(ErrEmpty, "status") 69 | } 70 | if _, ok := Proposal_Status_name[int32(p.Status)]; !ok { 71 | return sdkerrors.Wrap(ErrInvalid, "status") 72 | } 73 | if p.Result == ProposalResultInvalid { 74 | return sdkerrors.Wrap(ErrEmpty, "result") 75 | } 76 | if _, ok := Proposal_Result_name[int32(p.Result)]; !ok { 77 | return sdkerrors.Wrap(ErrInvalid, "result") 78 | } 79 | if p.ExecutorResult == ProposalExecutorResultInvalid { 80 | return sdkerrors.Wrap(ErrEmpty, "executor result") 81 | } 82 | if _, ok := Proposal_ExecutorResult_name[int32(p.ExecutorResult)]; !ok { 83 | return sdkerrors.Wrap(ErrInvalid, "executor result") 84 | } 85 | if err := p.VoteState.ValidateBasic(); err != nil { 86 | return sdkerrors.Wrap(err, "vote state") 87 | } 88 | if p.Timeout.Seconds == 0 && p.Timeout.Nanos == 0 { 89 | return sdkerrors.Wrap(ErrEmpty, "timeout") 90 | } 91 | msgs := p.GetMsgs() 92 | for i, msg := range msgs { 93 | if err := msg.ValidateBasic(); err != nil { 94 | return sdkerrors.Wrapf(err, "message %d", i) 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 101 | func (p Proposal) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { 102 | for _, any := range p.Msgs { 103 | var msg sdk.Msg 104 | err := unpacker.UnpackAny(any, &msg) 105 | if err != nil { 106 | return err 107 | } 108 | } 109 | 110 | return nil 111 | } 112 | 113 | func (p Proposal) PrimaryKey() []byte { 114 | return orm.EncodeSequence(p.ProposalId) 115 | } 116 | -------------------------------------------------------------------------------- /x/group/server/genesis.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | "github.com/cosmos/cosmos-sdk/types/errors" 8 | abci "github.com/tendermint/tendermint/abci/types" 9 | 10 | "github.com/regen-network/regen-ledger/orm" 11 | "github.com/regen-network/regen-ledger/types" 12 | "github.com/interchainberlin/metachain/x/group" 13 | ) 14 | 15 | func (s serverImpl) InitGenesis(ctx types.Context, cdc codec.JSONMarshaler, data json.RawMessage) ([]abci.ValidatorUpdate, error) { 16 | var genesisState group.GenesisState 17 | cdc.MustUnmarshalJSON(data, &genesisState) 18 | 19 | if err := orm.ImportTableData(ctx, s.groupTable, genesisState.Groups, 0); err != nil { 20 | return nil, errors.Wrap(err, "groups") 21 | } 22 | if err := s.groupSeq.InitVal(ctx, genesisState.GroupSeq); err != nil { 23 | return nil, errors.Wrap(err, "group seq") 24 | } 25 | 26 | if err := orm.ImportTableData(ctx, s.groupMemberTable, genesisState.GroupMembers, 0); err != nil { 27 | return nil, errors.Wrap(err, "group members") 28 | } 29 | 30 | if err := orm.ImportTableData(ctx, s.groupAccountTable, genesisState.GroupAccounts, 0); err != nil { 31 | return nil, errors.Wrap(err, "group accounts") 32 | } 33 | if err := s.groupAccountSeq.InitVal(ctx, genesisState.GroupAccountSeq); err != nil { 34 | return nil, errors.Wrap(err, "group account seq") 35 | } 36 | 37 | if err := orm.ImportTableData(ctx, s.proposalTable, genesisState.Proposals, genesisState.ProposalSeq); err != nil { 38 | return nil, errors.Wrap(err, "proposals") 39 | } 40 | 41 | if err := orm.ImportTableData(ctx, s.voteTable, genesisState.Votes, 0); err != nil { 42 | return nil, errors.Wrap(err, "votes") 43 | } 44 | 45 | return []abci.ValidatorUpdate{}, nil 46 | } 47 | 48 | func (s serverImpl) ExportGenesis(ctx types.Context, cdc codec.JSONMarshaler) (json.RawMessage, error) { 49 | genesisState := group.NewGenesisState() 50 | 51 | var groups []*group.GroupInfo 52 | _, err := orm.ExportTableData(ctx, s.groupTable, &groups) 53 | if err != nil { 54 | return nil, errors.Wrap(err, "groups") 55 | } 56 | genesisState.Groups = groups 57 | genesisState.GroupSeq = s.groupSeq.CurVal(ctx) 58 | 59 | var groupMembers []*group.GroupMember 60 | _, err = orm.ExportTableData(ctx, s.groupMemberTable, &groupMembers) 61 | if err != nil { 62 | return nil, errors.Wrap(err, "group members") 63 | } 64 | genesisState.GroupMembers = groupMembers 65 | 66 | var groupAccounts []*group.GroupAccountInfo 67 | _, err = orm.ExportTableData(ctx, s.groupAccountTable, &groupAccounts) 68 | if err != nil { 69 | return nil, errors.Wrap(err, "group accounts") 70 | } 71 | genesisState.GroupAccounts = groupAccounts 72 | genesisState.GroupAccountSeq = s.groupAccountSeq.CurVal(ctx) 73 | 74 | var proposals []*group.Proposal 75 | proposalSeq, err := orm.ExportTableData(ctx, s.proposalTable, &proposals) 76 | if err != nil { 77 | return nil, errors.Wrap(err, "proposals") 78 | } 79 | genesisState.Proposals = proposals 80 | genesisState.ProposalSeq = proposalSeq 81 | 82 | var votes []*group.Vote 83 | _, err = orm.ExportTableData(ctx, s.voteTable, &votes) 84 | if err != nil { 85 | return nil, errors.Wrap(err, "votes") 86 | } 87 | genesisState.Votes = votes 88 | 89 | genesisBytes := cdc.MustMarshalJSON(genesisState) 90 | return genesisBytes, nil 91 | } 92 | -------------------------------------------------------------------------------- /x/group/server/handler.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/cosmos-sdk/types/errors" 6 | "github.com/regen-network/regen-ledger/types" 7 | servermodule "github.com/regen-network/regen-ledger/types/module/server" 8 | "github.com/interchainberlin/metachain/x/group" 9 | "github.com/interchainberlin/metachain/x/group/exported" 10 | ) 11 | 12 | // NewHandler creates an sdk.Handler for all the group type messages. 13 | // This is needed for supporting amino-json signing. 14 | func NewHandler(configurator servermodule.Configurator, accountKeeper exported.AccountKeeper, bankKeeper exported.BankKeeper) sdk.Handler { 15 | impl := newServer(configurator.ModuleKey(), configurator.Router(), accountKeeper, bankKeeper, configurator.Marshaler()) 16 | 17 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 18 | ctx = ctx.WithEventManager(sdk.NewEventManager()) 19 | regenCtx := types.Context{Context: ctx} 20 | 21 | switch msg := msg.(type) { 22 | case *group.MsgCreateGroupRequest: 23 | res, err := impl.CreateGroup(regenCtx, msg) 24 | return sdk.WrapServiceResult(ctx, res, err) 25 | 26 | case *group.MsgUpdateGroupMembersRequest: 27 | res, err := impl.UpdateGroupMembers(regenCtx, msg) 28 | return sdk.WrapServiceResult(ctx, res, err) 29 | 30 | case *group.MsgUpdateGroupAdminRequest: 31 | res, err := impl.UpdateGroupAdmin(regenCtx, msg) 32 | return sdk.WrapServiceResult(ctx, res, err) 33 | 34 | case *group.MsgUpdateGroupMetadataRequest: 35 | res, err := impl.UpdateGroupMetadata(regenCtx, msg) 36 | return sdk.WrapServiceResult(ctx, res, err) 37 | 38 | case *group.MsgCreateGroupAccountRequest: 39 | res, err := impl.CreateGroupAccount(regenCtx, msg) 40 | return sdk.WrapServiceResult(ctx, res, err) 41 | 42 | case *group.MsgUpdateGroupAccountAdminRequest: 43 | res, err := impl.UpdateGroupAccountAdmin(regenCtx, msg) 44 | return sdk.WrapServiceResult(ctx, res, err) 45 | 46 | case *group.MsgUpdateGroupAccountDecisionPolicyRequest: 47 | res, err := impl.UpdateGroupAccountDecisionPolicy(regenCtx, msg) 48 | return sdk.WrapServiceResult(ctx, res, err) 49 | 50 | case *group.MsgUpdateGroupAccountMetadataRequest: 51 | res, err := impl.UpdateGroupAccountMetadata(regenCtx, msg) 52 | return sdk.WrapServiceResult(ctx, res, err) 53 | 54 | case *group.MsgCreateProposalRequest: 55 | res, err := impl.CreateProposal(regenCtx, msg) 56 | return sdk.WrapServiceResult(ctx, res, err) 57 | 58 | case *group.MsgVoteRequest: 59 | res, err := impl.Vote(regenCtx, msg) 60 | return sdk.WrapServiceResult(ctx, res, err) 61 | 62 | case *group.MsgExecRequest: 63 | res, err := impl.Exec(regenCtx, msg) 64 | return sdk.WrapServiceResult(ctx, res, err) 65 | 66 | default: 67 | return nil, errors.Wrapf(errors.ErrUnknownRequest, "unrecognized %s message type: %T", group.ModuleName, msg) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /x/group/server/invariants.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/cockroachdb/apd/v2" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | 9 | "github.com/regen-network/regen-ledger/orm" 10 | regenMath "github.com/regen-network/regen-ledger/types/math" 11 | "github.com/interchainberlin/metachain/x/group" 12 | ) 13 | 14 | const ( 15 | votesInvariant = "Tally-Votes" 16 | weightInvariant = "Group-TotalWeight" 17 | ) 18 | 19 | func (s serverImpl) RegisterInvariants(ir sdk.InvariantRegistry) { 20 | ir.RegisterRoute(group.ModuleName, votesInvariant, s.tallyVotesInvariant()) 21 | ir.RegisterRoute(group.ModuleName, weightInvariant, s.groupTotalWeightInvariant()) 22 | } 23 | 24 | func (s serverImpl) tallyVotesInvariant() sdk.Invariant { 25 | return func(ctx sdk.Context) (string, bool) { 26 | if ctx.BlockHeight()-1 < 0 { 27 | return sdk.FormatInvariant(group.ModuleName, votesInvariant, "Not enough blocks to perform TallyVotesInvariant"), false 28 | } 29 | prevCtx, _ := ctx.CacheContext() 30 | prevCtx = prevCtx.WithBlockHeight(ctx.BlockHeight() - 1) 31 | msg, broken, err := tallyVotesInvariant(ctx, prevCtx, s.proposalTable) 32 | if err != nil { 33 | panic(err) 34 | } 35 | return sdk.FormatInvariant(group.ModuleName, votesInvariant, msg), broken 36 | } 37 | } 38 | 39 | func (s serverImpl) groupTotalWeightInvariant() sdk.Invariant { 40 | return func(ctx sdk.Context) (string, bool) { 41 | msg, broken, err := groupTotalWeightInvariant(ctx, s.groupTable, s.groupMemberByGroupIndex) 42 | if err != nil { 43 | panic(err) 44 | } 45 | return sdk.FormatInvariant(group.ModuleName, weightInvariant, msg), broken 46 | } 47 | } 48 | 49 | func tallyVotesInvariant(ctx sdk.Context, prevCtx sdk.Context, proposalTable orm.AutoUInt64Table) (string, bool, error) { 50 | 51 | var msg string 52 | var broken bool 53 | 54 | prevIt, err := proposalTable.PrefixScan(prevCtx, 1, math.MaxUint64) 55 | if err != nil { 56 | return msg, broken, err 57 | } 58 | curIt, err := proposalTable.PrefixScan(ctx, 1, math.MaxUint64) 59 | if err != nil { 60 | return msg, broken, err 61 | } 62 | 63 | var curProposals []*group.Proposal 64 | _, err = orm.ReadAll(curIt, &curProposals) 65 | if err != nil { 66 | return msg, broken, err 67 | } 68 | 69 | var prevProposals []*group.Proposal 70 | _, err = orm.ReadAll(prevIt, &prevProposals) 71 | if err != nil { 72 | return msg, broken, err 73 | } 74 | 75 | for i := 0; i < len(prevProposals); i++ { 76 | if prevProposals[i].ProposalId == curProposals[i].ProposalId { 77 | prevYesCount, err := prevProposals[i].VoteState.GetYesCount() 78 | if err != nil { 79 | return msg, broken, err 80 | } 81 | curYesCount, err := curProposals[i].VoteState.GetYesCount() 82 | if err != nil { 83 | return msg, broken, err 84 | } 85 | prevNoCount, err := prevProposals[i].VoteState.GetNoCount() 86 | if err != nil { 87 | return msg, broken, err 88 | } 89 | curNoCount, err := curProposals[i].VoteState.GetNoCount() 90 | if err != nil { 91 | return msg, broken, err 92 | } 93 | prevAbstainCount, err := prevProposals[i].VoteState.GetAbstainCount() 94 | if err != nil { 95 | return msg, broken, err 96 | } 97 | curAbstainCount, err := curProposals[i].VoteState.GetAbstainCount() 98 | if err != nil { 99 | return msg, broken, err 100 | } 101 | prevVetoCount, err := prevProposals[i].VoteState.GetVetoCount() 102 | if err != nil { 103 | return msg, broken, err 104 | } 105 | curVetoCount, err := curProposals[i].VoteState.GetVetoCount() 106 | if err != nil { 107 | return msg, broken, err 108 | } 109 | if (curYesCount.Cmp(prevYesCount) == -1) || (curNoCount.Cmp(prevNoCount) == -1) || (curAbstainCount.Cmp(prevAbstainCount) == -1) || (curVetoCount.Cmp(prevVetoCount) == -1) { 110 | broken = true 111 | msg += "vote tally sums must never have less than the block before\n" 112 | return msg, broken, err 113 | } 114 | } 115 | } 116 | return msg, broken, err 117 | } 118 | 119 | func groupTotalWeightInvariant(ctx sdk.Context, groupTable orm.Table, groupMemberByGroupIndex orm.UInt64Index) (string, bool, error) { 120 | 121 | var msg string 122 | var broken bool 123 | 124 | var groupInfo group.GroupInfo 125 | var groupMember group.GroupMember 126 | 127 | groupIt, err := groupTable.PrefixScan(ctx, nil, nil) 128 | if err != nil { 129 | return msg, broken, err 130 | } 131 | defer groupIt.Close() 132 | 133 | for { 134 | membersWeight := apd.New(0, 0) 135 | _, err := groupIt.LoadNext(&groupInfo) 136 | if orm.ErrIteratorDone.Is(err) { 137 | break 138 | } 139 | memIt, err := groupMemberByGroupIndex.Get(ctx, groupInfo.GroupId) 140 | if err != nil { 141 | return msg, broken, err 142 | } 143 | defer memIt.Close() 144 | 145 | for { 146 | _, err = memIt.LoadNext(&groupMember) 147 | if orm.ErrIteratorDone.Is(err) { 148 | break 149 | } 150 | curMemWeight, err := regenMath.ParseNonNegativeDecimal(groupMember.GetMember().GetWeight()) 151 | if err != nil { 152 | return msg, broken, err 153 | } 154 | err = regenMath.Add(membersWeight, membersWeight, curMemWeight) 155 | if err != nil { 156 | return msg, broken, err 157 | } 158 | } 159 | groupWeight, err := regenMath.ParseNonNegativeDecimal(groupInfo.GetTotalWeight()) 160 | if err != nil { 161 | return msg, broken, err 162 | } 163 | 164 | if groupWeight.Cmp(membersWeight) != 0 { 165 | broken = true 166 | msg += "group's TotalWeight must be equal to the sum of its members' weights\n" 167 | break 168 | } 169 | } 170 | return msg, broken, err 171 | } 172 | -------------------------------------------------------------------------------- /x/group/server/operations.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/types/module" 5 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 6 | "github.com/interchainberlin/metachain/x/group" 7 | "github.com/interchainberlin/metachain/x/group/simulation" 8 | ) 9 | 10 | // WeightedOperations returns all the group module operations with their respective weights. 11 | func (s serverImpl) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 12 | 13 | queryClient := group.NewQueryClient(s.key) 14 | return simulation.WeightedOperations( 15 | simState.AppParams, simState.Cdc, 16 | s.accKeeper, s.bankKeeper, queryClient, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /x/group/server/proposal_executor.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/cosmos-sdk/types/errors" 6 | "github.com/interchainberlin/metachain/x/group" 7 | ) 8 | 9 | // ensureMsgAuthZ checks that if a message requires signers that all of them are equal to the given group account. 10 | func ensureMsgAuthZ(msgs []sdk.Msg, groupAccount sdk.AccAddress) error { 11 | for i := range msgs { 12 | for _, acct := range msgs[i].GetSigners() { 13 | if !groupAccount.Equals(acct) { 14 | return errors.Wrap(errors.ErrUnauthorized, "msg does not have group account authorization") 15 | } 16 | } 17 | } 18 | return nil 19 | } 20 | 21 | // DoExecuteMsgs routes the messages to the registered handlers. Messages are limited to those that require no authZ or 22 | // by the group account only. Otherwise this gives access to other peoples accounts as the sdk ant handler is bypassed 23 | func DoExecuteMsgs(ctx sdk.Context, router sdk.Router, groupAccount sdk.AccAddress, msgs []sdk.Msg) ([]sdk.Result, error) { 24 | results := make([]sdk.Result, len(msgs)) 25 | if err := ensureMsgAuthZ(msgs, groupAccount); err != nil { 26 | return nil, err 27 | } 28 | for i, msg := range msgs { 29 | handler := router.Route(ctx, msg.Route()) 30 | if handler == nil { 31 | return nil, errors.Wrapf(group.ErrInvalid, "no message handler found for %q", msg.Route()) 32 | } 33 | r, err := handler(ctx, msg) 34 | if err != nil { 35 | return nil, errors.Wrapf(err, "message %q at position %d", msg.Type(), i) 36 | } 37 | if r != nil { 38 | results[i] = *r 39 | } 40 | } 41 | return results, nil 42 | } 43 | -------------------------------------------------------------------------------- /x/group/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | 7 | "github.com/interchainberlin/metachain/x/group" 8 | "github.com/interchainberlin/metachain/x/group/exported" 9 | "github.com/regen-network/regen-ledger/orm" 10 | servermodule "github.com/regen-network/regen-ledger/types/module/server" 11 | ) 12 | 13 | const ( 14 | // Group Table 15 | GroupTablePrefix byte = 0x0 16 | GroupTableSeqPrefix byte = 0x1 17 | GroupByAdminIndexPrefix byte = 0x2 18 | 19 | // Group Member Table 20 | GroupMemberTablePrefix byte = 0x10 21 | GroupMemberByGroupIndexPrefix byte = 0x11 22 | GroupMemberByMemberIndexPrefix byte = 0x12 23 | 24 | // Group Account Table 25 | GroupAccountTablePrefix byte = 0x20 26 | GroupAccountTableSeqPrefix byte = 0x21 27 | GroupAccountByGroupIndexPrefix byte = 0x22 28 | GroupAccountByAdminIndexPrefix byte = 0x23 29 | 30 | // Proposal Table 31 | ProposalTablePrefix byte = 0x30 32 | ProposalTableSeqPrefix byte = 0x31 33 | ProposalByGroupAccountIndexPrefix byte = 0x32 34 | ProposalByProposerIndexPrefix byte = 0x33 35 | 36 | // Vote Table 37 | VoteTablePrefix byte = 0x40 38 | VoteByProposalIndexPrefix byte = 0x41 39 | VoteByVoterIndexPrefix byte = 0x42 40 | ) 41 | 42 | type serverImpl struct { 43 | key servermodule.RootModuleKey 44 | router sdk.Router 45 | 46 | accKeeper exported.AccountKeeper 47 | bankKeeper exported.BankKeeper 48 | 49 | // Group Table 50 | groupSeq orm.Sequence 51 | groupTable orm.Table 52 | groupByAdminIndex orm.Index 53 | 54 | // Group Member Table 55 | groupMemberTable orm.PrimaryKeyTable 56 | groupMemberByGroupIndex orm.UInt64Index 57 | groupMemberByMemberIndex orm.Index 58 | 59 | // Group Account Table 60 | groupAccountSeq orm.Sequence 61 | groupAccountTable orm.PrimaryKeyTable 62 | groupAccountByGroupIndex orm.UInt64Index 63 | groupAccountByAdminIndex orm.Index 64 | 65 | // Proposal Table 66 | proposalTable orm.AutoUInt64Table 67 | proposalByGroupAccountIndex orm.Index 68 | proposalByProposerIndex orm.Index 69 | 70 | // Vote Table 71 | voteTable orm.PrimaryKeyTable 72 | voteByProposalIndex orm.UInt64Index 73 | voteByVoterIndex orm.Index 74 | } 75 | 76 | func newServer(storeKey servermodule.RootModuleKey, router sdk.Router, accKeeper exported.AccountKeeper, bankKeeper exported.BankKeeper, cdc codec.Marshaler) serverImpl { 77 | s := serverImpl{key: storeKey, router: router, accKeeper: accKeeper, bankKeeper: bankKeeper} 78 | 79 | // Group Table 80 | groupTableBuilder := orm.NewTableBuilder(GroupTablePrefix, storeKey, &group.GroupInfo{}, orm.FixLengthIndexKeys(orm.EncodedSeqLength), cdc) 81 | s.groupSeq = orm.NewSequence(storeKey, GroupTableSeqPrefix) 82 | s.groupByAdminIndex = orm.NewIndex(groupTableBuilder, GroupByAdminIndexPrefix, func(val interface{}) ([]orm.RowID, error) { 83 | addr, err := sdk.AccAddressFromBech32(val.(*group.GroupInfo).Admin) 84 | if err != nil { 85 | return nil, err 86 | } 87 | return []orm.RowID{addr.Bytes()}, nil 88 | }) 89 | s.groupTable = groupTableBuilder.Build() 90 | 91 | // Group Member Table 92 | groupMemberTableBuilder := orm.NewPrimaryKeyTableBuilder(GroupMemberTablePrefix, storeKey, &group.GroupMember{}, orm.Max255DynamicLengthIndexKeyCodec{}, cdc) 93 | s.groupMemberByGroupIndex = orm.NewUInt64Index(groupMemberTableBuilder, GroupMemberByGroupIndexPrefix, func(val interface{}) ([]uint64, error) { 94 | group := val.(*group.GroupMember).GroupId 95 | return []uint64{group}, nil 96 | }) 97 | s.groupMemberByMemberIndex = orm.NewIndex(groupMemberTableBuilder, GroupMemberByMemberIndexPrefix, func(val interface{}) ([]orm.RowID, error) { 98 | memberAddr := val.(*group.GroupMember).Member.Address 99 | addr, err := sdk.AccAddressFromBech32(memberAddr) 100 | if err != nil { 101 | return nil, err 102 | } 103 | return []orm.RowID{addr.Bytes()}, nil 104 | }) 105 | s.groupMemberTable = groupMemberTableBuilder.Build() 106 | 107 | // Group Account Table 108 | s.groupAccountSeq = orm.NewSequence(storeKey, GroupAccountTableSeqPrefix) 109 | groupAccountTableBuilder := orm.NewPrimaryKeyTableBuilder(GroupAccountTablePrefix, storeKey, &group.GroupAccountInfo{}, orm.Max255DynamicLengthIndexKeyCodec{}, cdc) 110 | s.groupAccountByGroupIndex = orm.NewUInt64Index(groupAccountTableBuilder, GroupAccountByGroupIndexPrefix, func(value interface{}) ([]uint64, error) { 111 | group := value.(*group.GroupAccountInfo).GroupId 112 | return []uint64{group}, nil 113 | }) 114 | s.groupAccountByAdminIndex = orm.NewIndex(groupAccountTableBuilder, GroupAccountByAdminIndexPrefix, func(value interface{}) ([]orm.RowID, error) { 115 | admin := value.(*group.GroupAccountInfo).Admin 116 | addr, err := sdk.AccAddressFromBech32(admin) 117 | if err != nil { 118 | return nil, err 119 | } 120 | return []orm.RowID{addr.Bytes()}, nil 121 | }) 122 | s.groupAccountTable = groupAccountTableBuilder.Build() 123 | 124 | // Proposal Table 125 | proposalTableBuilder := orm.NewAutoUInt64TableBuilder(ProposalTablePrefix, ProposalTableSeqPrefix, storeKey, &group.Proposal{}, cdc) 126 | s.proposalByGroupAccountIndex = orm.NewIndex(proposalTableBuilder, ProposalByGroupAccountIndexPrefix, func(value interface{}) ([]orm.RowID, error) { 127 | account := value.(*group.Proposal).Address 128 | addr, err := sdk.AccAddressFromBech32(account) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return []orm.RowID{addr.Bytes()}, nil 133 | }) 134 | s.proposalByProposerIndex = orm.NewIndex(proposalTableBuilder, ProposalByProposerIndexPrefix, func(value interface{}) ([]orm.RowID, error) { 135 | proposers := value.(*group.Proposal).Proposers 136 | r := make([]orm.RowID, len(proposers)) 137 | for i := range proposers { 138 | addr, err := sdk.AccAddressFromBech32(proposers[i]) 139 | if err != nil { 140 | return nil, err 141 | } 142 | r[i] = addr.Bytes() 143 | } 144 | return r, nil 145 | }) 146 | s.proposalTable = proposalTableBuilder.Build() 147 | 148 | // Vote Table 149 | voteTableBuilder := orm.NewPrimaryKeyTableBuilder(VoteTablePrefix, storeKey, &group.Vote{}, orm.Max255DynamicLengthIndexKeyCodec{}, cdc) 150 | s.voteByProposalIndex = orm.NewUInt64Index(voteTableBuilder, VoteByProposalIndexPrefix, func(value interface{}) ([]uint64, error) { 151 | return []uint64{value.(*group.Vote).ProposalId}, nil 152 | }) 153 | s.voteByVoterIndex = orm.NewIndex(voteTableBuilder, VoteByVoterIndexPrefix, func(value interface{}) ([]orm.RowID, error) { 154 | addr, err := sdk.AccAddressFromBech32(value.(*group.Vote).Voter) 155 | if err != nil { 156 | return nil, err 157 | } 158 | return []orm.RowID{addr.Bytes()}, nil 159 | }) 160 | s.voteTable = voteTableBuilder.Build() 161 | 162 | return s 163 | } 164 | 165 | func RegisterServices(configurator servermodule.Configurator, accountKeeper exported.AccountKeeper, bankKeeper exported.BankKeeper) { 166 | impl := newServer(configurator.ModuleKey(), configurator.Router(), accountKeeper, bankKeeper, configurator.Marshaler()) 167 | group.RegisterMsgServer(configurator.MsgServer(), impl) 168 | group.RegisterQueryServer(configurator.QueryServer(), impl) 169 | configurator.RegisterInvariantsHandler(impl.RegisterInvariants) 170 | configurator.RegisterGenesisHandlers(impl.InitGenesis, impl.ExportGenesis) 171 | configurator.RegisterWeightedOperationsHandler(impl.WeightedOperations) 172 | } 173 | -------------------------------------------------------------------------------- /x/group/server/server_test.go: -------------------------------------------------------------------------------- 1 | package server_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" 9 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 10 | "github.com/cosmos/cosmos-sdk/x/bank" 11 | bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 12 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 13 | paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" 14 | "github.com/stretchr/testify/suite" 15 | 16 | groupmodule "github.com/interchainberlin/metachain/x/group/module" 17 | "github.com/regen-network/regen-ledger/types/module" 18 | "github.com/regen-network/regen-ledger/types/module/server" 19 | "github.com/interchainberlin/metachain/x/group/server/testsuite" 20 | ) 21 | 22 | func TestServer(t *testing.T) { 23 | ff := server.NewFixtureFactory(t, 6) 24 | cdc := ff.Codec() 25 | // Setting up bank keeper 26 | banktypes.RegisterInterfaces(cdc.InterfaceRegistry()) 27 | authtypes.RegisterInterfaces(cdc.InterfaceRegistry()) 28 | 29 | paramsKey := sdk.NewKVStoreKey(paramstypes.StoreKey) 30 | authKey := sdk.NewKVStoreKey(authtypes.StoreKey) 31 | bankKey := sdk.NewKVStoreKey(banktypes.StoreKey) 32 | tkey := sdk.NewTransientStoreKey(paramstypes.TStoreKey) 33 | amino := codec.NewLegacyAmino() 34 | 35 | authSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, authtypes.ModuleName) 36 | bankSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, banktypes.ModuleName) 37 | 38 | accountKeeper := authkeeper.NewAccountKeeper( 39 | cdc, authKey, authSubspace, authtypes.ProtoBaseAccount, map[string][]string{}, 40 | ) 41 | 42 | bankKeeper := bankkeeper.NewBaseKeeper( 43 | cdc, bankKey, accountKeeper, bankSubspace, map[string]bool{}, 44 | ) 45 | 46 | baseApp := ff.BaseApp() 47 | 48 | baseApp.Router().AddRoute(sdk.NewRoute(banktypes.ModuleName, bank.NewHandler(bankKeeper))) 49 | baseApp.MountStore(tkey, sdk.StoreTypeTransient) 50 | baseApp.MountStore(paramsKey, sdk.StoreTypeIAVL) 51 | baseApp.MountStore(authKey, sdk.StoreTypeIAVL) 52 | baseApp.MountStore(bankKey, sdk.StoreTypeIAVL) 53 | 54 | ff.SetModules([]module.Module{groupmodule.Module{AccountKeeper: accountKeeper}}) 55 | 56 | s := testsuite.NewIntegrationTestSuite(ff, accountKeeper, bankKeeper) 57 | 58 | suite.Run(t, s) 59 | } 60 | -------------------------------------------------------------------------------- /x/group/server/testsuite/genesis.go: -------------------------------------------------------------------------------- 1 | package testsuite 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 9 | proto "github.com/gogo/protobuf/types" 10 | "github.com/interchainberlin/metachain/x/group" 11 | ) 12 | 13 | func (s *IntegrationTestSuite) TestInitExportGenesis() { 14 | require := s.Require() 15 | ctx := s.genesisCtx 16 | cdc := s.fixture.Codec() 17 | 18 | now := time.Now() 19 | submittedAt, err := proto.TimestampProto(now) 20 | require.NoError(err) 21 | timeout, err := proto.TimestampProto(now.Add(time.Second * 1)) 22 | require.NoError(err) 23 | 24 | groupAccount := &group.GroupAccountInfo{ 25 | Address: s.groupAccountAddr.String(), 26 | GroupId: 1, 27 | Admin: s.addr1.String(), 28 | Version: 1, 29 | Metadata: []byte("account metadata"), 30 | } 31 | err = groupAccount.SetDecisionPolicy(&group.ThresholdDecisionPolicy{ 32 | Threshold: "1", 33 | Timeout: proto.Duration{Seconds: 1}, 34 | }) 35 | require.NoError(err) 36 | 37 | proposal := &group.Proposal{ 38 | ProposalId: 1, 39 | Address: s.groupAccountAddr.String(), 40 | Metadata: []byte("proposal metadata"), 41 | GroupVersion: 1, 42 | GroupAccountVersion: 1, 43 | Proposers: []string{ 44 | s.addr1.String(), 45 | }, 46 | SubmittedAt: *submittedAt, 47 | Status: group.ProposalStatusClosed, 48 | Result: group.ProposalResultAccepted, 49 | VoteState: group.Tally{ 50 | YesCount: "1", 51 | NoCount: "0", 52 | AbstainCount: "0", 53 | VetoCount: "0", 54 | }, 55 | Timeout: *timeout, 56 | ExecutorResult: group.ProposalExecutorResultSuccess, 57 | } 58 | err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ 59 | FromAddress: s.groupAccountAddr.String(), 60 | ToAddress: s.addr2.String(), 61 | Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)}, 62 | }}) 63 | require.NoError(err) 64 | 65 | genesisState := &group.GenesisState{ 66 | GroupSeq: 2, 67 | Groups: []*group.GroupInfo{{GroupId: 1, Admin: s.addr1.String(), Metadata: []byte("1"), Version: 1, TotalWeight: "1"}, {GroupId: 2, Admin: s.addr2.String(), Metadata: []byte("2"), Version: 2, TotalWeight: "2"}}, 68 | GroupMembers: []*group.GroupMember{{GroupId: 1, Member: &group.Member{Address: s.addr1.String(), Weight: "1", Metadata: []byte("member metadata")}}, {GroupId: 2, Member: &group.Member{Address: s.addr1.String(), Weight: "2", Metadata: []byte("member metadata")}}}, 69 | GroupAccountSeq: 1, 70 | GroupAccounts: []*group.GroupAccountInfo{groupAccount}, 71 | ProposalSeq: 1, 72 | Proposals: []*group.Proposal{proposal}, 73 | Votes: []*group.Vote{{ProposalId: proposal.ProposalId, Voter: s.addr1.String(), SubmittedAt: *submittedAt, Choice: group.Choice_CHOICE_YES}}, 74 | } 75 | 76 | genesisBytes, err := cdc.MarshalJSON(genesisState) 77 | require.NoError(err) 78 | 79 | genesisData := map[string]json.RawMessage{group.ModuleName: genesisBytes} 80 | _, err = s.fixture.InitGenesis(ctx.Context, genesisData) 81 | require.NoError(err) 82 | 83 | for i, g := range genesisState.Groups { 84 | res, err := s.queryClient.GroupInfo(ctx, &group.QueryGroupInfoRequest{ 85 | GroupId: g.GroupId, 86 | }) 87 | require.NoError(err) 88 | require.Equal(g, res.Info) 89 | 90 | membersRes, err := s.queryClient.GroupMembers(ctx, &group.QueryGroupMembersRequest{ 91 | GroupId: g.GroupId, 92 | }) 93 | require.NoError(err) 94 | require.Equal(len(membersRes.Members), 1) 95 | require.Equal(membersRes.Members[0], genesisState.GroupMembers[i]) 96 | } 97 | 98 | for _, g := range genesisState.GroupAccounts { 99 | res, err := s.queryClient.GroupAccountInfo(ctx, &group.QueryGroupAccountInfoRequest{ 100 | Address: g.Address, 101 | }) 102 | require.NoError(err) 103 | s.assertGroupAccountsEqual(g, res.Info) 104 | } 105 | 106 | for _, g := range genesisState.Proposals { 107 | res, err := s.queryClient.Proposal(ctx, &group.QueryProposalRequest{ 108 | ProposalId: g.ProposalId, 109 | }) 110 | require.NoError(err) 111 | s.assertProposalsEqual(g, res.Proposal) 112 | 113 | votesRes, err := s.queryClient.VotesByProposal(ctx, &group.QueryVotesByProposalRequest{ 114 | ProposalId: g.ProposalId, 115 | }) 116 | require.NoError(err) 117 | require.Equal(len(votesRes.Votes), 1) 118 | require.Equal(votesRes.Votes[0], genesisState.Votes[0]) 119 | } 120 | 121 | exported, err := s.fixture.ExportGenesis(ctx.Context) 122 | require.NoError(err) 123 | 124 | var exportedGenesisState group.GenesisState 125 | err = cdc.UnmarshalJSON(exported[group.ModuleName], &exportedGenesisState) 126 | require.NoError(err) 127 | 128 | require.Equal(genesisState.Groups, exportedGenesisState.Groups) 129 | require.Equal(genesisState.GroupMembers, exportedGenesisState.GroupMembers) 130 | 131 | require.Equal(len(genesisState.GroupAccounts), len(exportedGenesisState.GroupAccounts)) 132 | for i, g := range genesisState.GroupAccounts { 133 | res := exportedGenesisState.GroupAccounts[i] 134 | require.NoError(err) 135 | s.assertGroupAccountsEqual(g, res) 136 | } 137 | 138 | require.Equal(len(genesisState.Proposals), len(exportedGenesisState.Proposals)) 139 | for i, g := range genesisState.Proposals { 140 | res := exportedGenesisState.Proposals[i] 141 | require.NoError(err) 142 | s.assertProposalsEqual(g, res) 143 | } 144 | require.Equal(genesisState.Votes, exportedGenesisState.Votes) 145 | 146 | require.Equal(genesisState.GroupSeq, exportedGenesisState.GroupSeq) 147 | require.Equal(genesisState.GroupAccountSeq, exportedGenesisState.GroupAccountSeq) 148 | require.Equal(genesisState.ProposalSeq, exportedGenesisState.ProposalSeq) 149 | 150 | } 151 | 152 | func (s *IntegrationTestSuite) assertGroupAccountsEqual(g *group.GroupAccountInfo, other *group.GroupAccountInfo) { 153 | require := s.Require() 154 | require.Equal(g.Address, other.Address) 155 | require.Equal(g.GroupId, other.GroupId) 156 | require.Equal(g.Admin, other.Admin) 157 | require.Equal(g.Metadata, other.Metadata) 158 | require.Equal(g.Version, other.Version) 159 | require.Equal(g.GetDecisionPolicy(), other.GetDecisionPolicy()) 160 | } 161 | 162 | func (s *IntegrationTestSuite) assertProposalsEqual(g *group.Proposal, other *group.Proposal) { 163 | require := s.Require() 164 | require.Equal(g.ProposalId, other.ProposalId) 165 | require.Equal(g.Address, other.Address) 166 | require.Equal(g.Metadata, other.Metadata) 167 | require.Equal(g.Proposers, other.Proposers) 168 | require.Equal(g.SubmittedAt, other.SubmittedAt) 169 | require.Equal(g.GroupVersion, other.GroupVersion) 170 | require.Equal(g.GroupAccountVersion, other.GroupAccountVersion) 171 | require.Equal(g.Status, other.Status) 172 | require.Equal(g.Result, other.Result) 173 | require.Equal(g.VoteState, other.VoteState) 174 | require.Equal(g.Timeout, other.Timeout) 175 | require.Equal(g.ExecutorResult, other.ExecutorResult) 176 | require.Equal(g.GetMsgs(), other.GetMsgs()) 177 | } 178 | -------------------------------------------------------------------------------- /x/group/simulation/genesis.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "math/rand" 5 | 6 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/types/module" 9 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 10 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 11 | gogotypes "github.com/gogo/protobuf/types" 12 | 13 | "github.com/interchainberlin/metachain/x/group" 14 | ) 15 | 16 | const ( 17 | GroupInfo = "group-info" 18 | GroupMembers = "group-members" 19 | GroupAccountInfo = "group-accout-info" 20 | GroupProposals = "group-proposals" 21 | GroupVote = "group-vote" 22 | ) 23 | 24 | func getGroups(r *rand.Rand, accounts []simtypes.Account) []*group.GroupInfo { 25 | groups := make([]*group.GroupInfo, 3) 26 | for i := 0; i < 3; i++ { 27 | acc, _ := simtypes.RandomAcc(r, accounts) 28 | groups[i] = &group.GroupInfo{ 29 | GroupId: uint64(i + 1), 30 | Admin: acc.Address.String(), 31 | Metadata: []byte(simtypes.RandStringOfLength(r, 10)), 32 | Version: 1, 33 | TotalWeight: "10", 34 | } 35 | } 36 | return groups 37 | } 38 | 39 | func getGroupMembers(r *rand.Rand, accounts []simtypes.Account) []*group.GroupMember { 40 | groupMembers := make([]*group.GroupMember, 3) 41 | for i := 0; i < 3; i++ { 42 | acc, _ := simtypes.RandomAcc(r, accounts) 43 | groupMembers[i] = &group.GroupMember{ 44 | GroupId: uint64(i + 1), 45 | Member: &group.Member{ 46 | Address: acc.Address.String(), 47 | Weight: "10", 48 | Metadata: []byte(simtypes.RandStringOfLength(r, 10)), 49 | }, 50 | } 51 | } 52 | return groupMembers 53 | } 54 | 55 | func getGroupAccounts(r *rand.Rand, simState *module.SimulationState) []*group.GroupAccountInfo { 56 | groupMembers := make([]*group.GroupAccountInfo, 3) 57 | for i := 0; i < 3; i++ { 58 | acc, _ := simtypes.RandomAcc(r, simState.Accounts) 59 | any, err := codectypes.NewAnyWithValue(group.NewThresholdDecisionPolicy("10", gogotypes.Duration{Seconds: 1})) 60 | if err != nil { 61 | panic(err) 62 | } 63 | groupMembers[i] = &group.GroupAccountInfo{ 64 | GroupId: uint64(i + 1), 65 | Admin: acc.Address.String(), 66 | Address: acc.Address.String(), 67 | Version: 1, 68 | DecisionPolicy: any, 69 | Metadata: []byte(simtypes.RandStringOfLength(r, 10)), 70 | } 71 | } 72 | return groupMembers 73 | } 74 | 75 | func getProposals(r *rand.Rand, simState *module.SimulationState) []*group.Proposal { 76 | proposals := make([]*group.Proposal, 3) 77 | proposers := []string{simState.Accounts[0].Address.String(), simState.Accounts[1].Address.String()} 78 | for i := 0; i < 3; i++ { 79 | from, _ := simtypes.RandomAcc(r, simState.Accounts) 80 | to, _ := simtypes.RandomAcc(r, simState.Accounts) 81 | fromAddr := from.Address.String() 82 | 83 | proposal := &group.Proposal{ 84 | ProposalId: uint64(i + 1), 85 | Proposers: proposers, 86 | Address: fromAddr, 87 | GroupVersion: uint64(i + 1), 88 | GroupAccountVersion: uint64(i + 1), 89 | Status: group.ProposalStatusSubmitted, 90 | Result: group.ProposalResultAccepted, 91 | VoteState: group.Tally{ 92 | YesCount: "1", 93 | NoCount: "1", 94 | AbstainCount: "1", 95 | VetoCount: "0", 96 | }, 97 | ExecutorResult: group.ProposalExecutorResultNotRun, 98 | Metadata: []byte(simtypes.RandStringOfLength(r, 50)), 99 | SubmittedAt: gogotypes.Timestamp{Seconds: 1}, 100 | Timeout: gogotypes.Timestamp{Seconds: 1000}, 101 | } 102 | err := proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ 103 | FromAddress: fromAddr, 104 | ToAddress: to.Address.String(), 105 | Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)), 106 | }}) 107 | if err != nil { 108 | panic(err) 109 | } 110 | 111 | proposals[i] = proposal 112 | } 113 | 114 | return proposals 115 | } 116 | 117 | func getVotes(r *rand.Rand, simState *module.SimulationState) []*group.Vote { 118 | votes := make([]*group.Vote, 3) 119 | 120 | for i := 0; i < 3; i++ { 121 | votes[i] = &group.Vote{ 122 | ProposalId: uint64(i + 1), 123 | Voter: simState.Accounts[i].Address.String(), 124 | Choice: getVoteChoice(i), 125 | Metadata: []byte(simtypes.RandStringOfLength(r, 50)), 126 | SubmittedAt: gogotypes.Timestamp{Seconds: 10}, 127 | } 128 | } 129 | 130 | return votes 131 | } 132 | 133 | func getVoteChoice(index int) group.Choice { 134 | switch index { 135 | case 0: 136 | return group.Choice_CHOICE_YES 137 | case 1: 138 | return group.Choice_CHOICE_NO 139 | case 2: 140 | return group.Choice_CHOICE_ABSTAIN 141 | default: 142 | return group.Choice_CHOICE_VETO 143 | } 144 | } 145 | 146 | // RandomizedGenState generates a random GenesisState for the group module. 147 | func RandomizedGenState(simState *module.SimulationState) { 148 | 149 | // groups 150 | var groups []*group.GroupInfo 151 | simState.AppParams.GetOrGenerate( 152 | simState.Cdc, GroupInfo, &groups, simState.Rand, 153 | func(r *rand.Rand) { groups = getGroups(r, simState.Accounts) }, 154 | ) 155 | 156 | // group members 157 | var members []*group.GroupMember 158 | simState.AppParams.GetOrGenerate( 159 | simState.Cdc, GroupMembers, &members, simState.Rand, 160 | func(r *rand.Rand) { members = getGroupMembers(r, simState.Accounts) }, 161 | ) 162 | 163 | // group accounts 164 | var groupAccounts []*group.GroupAccountInfo 165 | simState.AppParams.GetOrGenerate( 166 | simState.Cdc, GroupAccountInfo, &groupAccounts, simState.Rand, 167 | func(r *rand.Rand) { groupAccounts = getGroupAccounts(r, simState) }, 168 | ) 169 | 170 | // proposals 171 | var proposals []*group.Proposal 172 | simState.AppParams.GetOrGenerate( 173 | simState.Cdc, GroupProposals, &proposals, simState.Rand, 174 | func(r *rand.Rand) { proposals = getProposals(r, simState) }, 175 | ) 176 | 177 | // votes 178 | var votes []*group.Vote 179 | simState.AppParams.GetOrGenerate( 180 | simState.Cdc, GroupVote, &votes, simState.Rand, 181 | func(r *rand.Rand) { votes = getVotes(r, simState) }, 182 | ) 183 | 184 | groupGenesis := group.GenesisState{ 185 | GroupSeq: 3, 186 | Groups: groups, 187 | GroupMembers: members, 188 | GroupAccountSeq: 3, 189 | GroupAccounts: groupAccounts, 190 | ProposalSeq: 3, 191 | Proposals: proposals, 192 | Votes: votes, 193 | } 194 | 195 | simState.GenState[group.ModuleName] = simState.Cdc.MustMarshalJSON(&groupGenesis) 196 | } 197 | -------------------------------------------------------------------------------- /x/group/spec/00_diff.md: -------------------------------------------------------------------------------- 1 | # Diff 2 | 3 | This page captures the changes made between the current 4 | [groups](https://github.com/regen-network/regen-ledger/tree/v1.0.0/x/group/spec) 5 | module and what is proposed. 6 | 7 | ## Transfer Decision Policy, Voting and Proposals across to the DAO module. 8 | 9 | The types and messages that are concerned with the governance of the group: 10 | `DecisionPolicy`, `Vote`, `Proposal` have been moved to the `dao` module. 11 | 12 | The idea behind the change is to scope the `group` module just to the notion of 13 | a weighted group of members and sets of associated accounts. Thus `group` can 14 | change set membership, the admin and create many accounts per group but it does 15 | not concern itself with the actions that the group decides upon. 16 | 17 | This decoupling means that groups wishing to use the `dao` module for governance 18 | need to set the admin to the `dao` account and register the group with the dao 19 | (along setting a decision policy and other governance parameters). This is seen as 20 | the standard pairing but this is not always the case. The `admin` field sets who 21 | has authority of the group. This allows for other cases such as off-chain 22 | governance (where the account of the off-chain service is used instead). It can 23 | also be the address of another group of another module or any other account for 24 | that matter. 25 | 26 | ## Add Differential Groups 27 | 28 | A differential group extends a regular group by using slices. It structures 29 | the group membership and membership changes in such a way as to efficiently 30 | persist multiple states. This is beneficial when requiring group snapshots 31 | which may be needed for proposals where weight is counted at the start of the 32 | proposal. 33 | -------------------------------------------------------------------------------- /x/group/spec/01_concepts.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Concepts 6 | 7 | ## Group 8 | 9 | A group is simply an aggregation of accounts with associated weights. It is not 10 | an account and doesn't have a balance. It doesn't in and of itself have any 11 | sort of voting or decision weight. It does have an "administrator" which has 12 | the ability to add, remove and update members in the group. An administrator of 13 | a group could be a single user, another group, a module such as `dao` or some 14 | equivalent that offers governance capabilities. It could even be an account 15 | connected with off-chain governance. 16 | 17 | Groups can have different implementations largely depending on the tradeoffs 18 | between frequency of weight changes and membership changes, the size of the 19 | group, and the complexity of the decision policies. To unify these tradeoffs 20 | groups can be represented as the following interface: 21 | 22 | ```golang 23 | type Group interface { 24 | // Get's the current memberset and height. This also signals to the 25 | // group that a process requires the memberset and may need it at a later point. 26 | GetMemberSet() (MemberSet, uint64) 27 | 28 | // Get's a member at the current height 29 | GetMember(member string) (Member, uint64) 30 | 31 | // Returns the weight of a specific member at the specified height. 32 | GetMemberAtHeight(member string, height uint64) (Member, error) 33 | 34 | // Returns the member set at that height 35 | GetMemberSetAtHeight(height uint64) (MemberSet, error) 36 | 37 | // Returns all the heights that the group has knowledge of the MemberSet 38 | GetAllMemberSetHeights() []uint64 39 | 40 | // Signals that the MemberSet at that height is no longer needed by a process 41 | ReleaseMemberSet(height uint64) 42 | } 43 | 44 | type MemberSet []Member 45 | 46 | type Member struct { 47 | address string 48 | weight string 49 | meta []byte 50 | } 51 | ``` 52 | Note that this interface only pertains to reading the `Group`. Writes to group 53 | membership are subject to the individual concrete types. 54 | 55 | ### Static Group 56 | 57 | A static group is the simplest implementation. It doesn't retain any temporal 58 | knowledge rather only holds the memberset at the current height. This is 59 | suitable for groups whose membership doesn't change too often or for groups that 60 | employ a governance mechanism which only requires the memberset at a single 61 | height (i.e. when votes are counted at the end of a proposal) 62 | 63 | ### Differential Group 64 | 65 | A differential group extends a regular group by using slices. It structures 66 | the group membership and membership changes in such a way as to efficiently 67 | persist multiple states. This is beneficial when requiring group snapshots 68 | which may be needed for certain proposals. 69 | 70 | As an example, say the current height was 10 and we had a snapshot of the group 71 | at height 5. We would save the slice as representing the changes made between 5 72 | and 10. 73 | 74 | Group at height 10: 75 | 76 | `a` = 10, `b` = 15, `c` = 20 77 | 78 | Group at height 5: 79 | 80 | `a` = 10, `b` = 10, `d` = 10 81 | 82 | Slice: 83 | 84 | `b` = 10, `c` = 0, `d` = 10 85 | 86 | With the group at height 10 and the slice we could work backwards and obtain the 87 | group at height 5. When a membership update happens, we thus update the group at 88 | height 10 and sometimes the slice in between (Note: that all slices apart from the 89 | most recent stay the same, hence this is quite efficient): 90 | 91 | I.e. for a change in `b`'s weight from 15 -> 20 92 | 93 | Group at height 11: 94 | 95 | `a` = 10, `b` = 20, `c` = 20 96 | 97 | Slice (stays the same): 98 | 99 | `b` = 10, `c` = 0, `d` = 10 100 | 101 | For a new member `e` = 10 102 | 103 | Group at height 12: 104 | 105 | `a` = 10, `b` = 20, `c` = 20, `e` = 10 106 | 107 | Slice (adds `e`): 108 | 109 | `b` = 10, `c` = 0, `d` = 10, `e` = 0, 110 | 111 | 112 | ### Epoch Group 113 | 114 | For groups that update extremely often (like token groups) and can have many 115 | simulatenous proposals, computation in updating the slice and computation in 116 | generating the group at a specified height can be too high. In this case we have 117 | an epoch group. Epoch groups keep the current member set and one prior snapshot. 118 | This snashot has the characteristic of being updated every x heights - hence 119 | defining an epoch. Proposals within an epoch all use the snapshot made at the 120 | beginning of the epoch as a reference for the member set. Users of such a group 121 | should be wary of proposals that cross over multiple epochs and use an 122 | appropriate governance mechanism. 123 | 124 | ## Group Account 125 | 126 | A group account is a group as well as an account. Group accounts are abstracted 127 | from groups because a single group may have multiple accounts designed for 128 | different purposes and having different governance mechanisms. Managing group 129 | membership separately from decision policies results in the least overhead 130 | and keeps membership consistent across different policies. The pattern that 131 | is recommended is to have a single master group account for a given group, 132 | and then to create separate group accounts with different decision policies 133 | and delegate the desired permissions from the master account to 134 | those "sub-accounts" using the `x/authz` module. A group account has an 135 | administrator which could be set up in the same ways as a group administrator 136 | 137 | ```proto 138 | message GroupAccount { 139 | // the group account address 140 | string address = 1; 141 | 142 | // the address of the admin of the account 143 | string admin = 2; 144 | 145 | // the group_id the account is associated with 146 | uint64 group_id = 3; 147 | 148 | // metadata is any arbitrary metadata attached to the group account. 149 | bytes metadata = 4; 150 | } 151 | ``` -------------------------------------------------------------------------------- /x/group/spec/02_state.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # State 6 | 7 | The `group` module uses the `orm` package which provides table storage with support for 8 | primary keys and secondary indexes. `orm` also defines `Sequence` which is a persistent unique key generator based on a counter that can be used along with `Table`s. 9 | 10 | Here's the list of tables and associated sequences and indexes stored as part of the `group` module. 11 | 12 | ## Group Table 13 | 14 | The `groupTable` stores `GroupInfo`: `0x0 | []byte(GroupId) -> ProtocolBuffer(GroupInfo)`. 15 | 16 | ### groupSeq 17 | 18 | The value of `groupSeq` is incremented when creating a new group and corresponds to the new `GroupId`: `0x1 | 0x1 -> BigEndian`. 19 | 20 | The second `0x1` corresponds to the ORM `sequenceStorageKey`. 21 | 22 | ### groupByAdminIndex 23 | 24 | `groupByAdminIndex` allows to retrieve groups by admin address: 25 | `0x2 | []byte(group.Admin) | []byte(GroupId) -> []byte()`. 26 | 27 | ## Group Member Table 28 | 29 | The `groupMemberTable` stores `GroupMember`s: `0x10 | []byte(GroupId) | []byte(member.Address) -> ProtocolBuffer(GroupMember)`. 30 | 31 | The `groupMemberTable` is a primary key table and its `PrimaryKey` is given by 32 | `[]byte(GroupId) | []byte(member.Address)` which is used by the following indexes. 33 | 34 | ### groupMemberByGroupIndex 35 | 36 | `groupMemberByGroupIndex` allows to retrieve group members by group id: 37 | `0x11 | []byte(GroupId) | PrimaryKey | byte(len(PrimaryKey)) -> []byte()`. 38 | 39 | ### groupMemberByMemberIndex 40 | 41 | `groupMemberByMemberIndex` allows to retrieve group members by member address: 42 | `0x12 | []byte(member.Address) | PrimaryKey | byte(len(PrimaryKey)) -> 43 | []byte()`. 44 | 45 | ## Group Differential Table 46 | 47 | A `groupDifferentialTable` stores the differences in group membership between states 48 | or heights: `0x30 | []byte(GroupId) | []byte(height) -> ProtocolBuffer(Members) 49 | 50 | 51 | ## Group Account Table 52 | 53 | The `groupAccountTable` stores `GroupAccountInfo`: `0x20 | []byte(Address) -> ProtocolBuffer(GroupAccountInfo)`. 54 | 55 | The `groupAccountTable` is a primary key table and its `PrimaryKey` is given by 56 | `[]byte(Address)` which is used by the following indexes. 57 | 58 | ### groupAccountSeq 59 | 60 | The value of `groupAccountSeq` is incremented when creating a new group account and is used to generate the new group account `Address`: 61 | `0x21 | 0x1 -> BigEndian`. 62 | 63 | The second `0x1` corresponds to the ORM `sequenceStorageKey`. 64 | 65 | ### groupAccountByGroupIndex 66 | 67 | `groupAccountByGroupIndex` allows to retrieve group accounts by group id: 68 | `0x22 | []byte(GroupId) | PrimaryKey | byte(len(PrimaryKey)) -> []byte()`. 69 | 70 | ### groupAccountByAdminIndex 71 | 72 | `groupAccountByAdminIndex` allows to retrieve group accounts by admin address: 73 | `0x23 | []byte(Address) | PrimaryKey | byte(len(PrimaryKey)) -> []byte()`. 74 | -------------------------------------------------------------------------------- /x/group/spec/03_messages.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Msg Service 6 | 7 | ## MsgCreateGroup 8 | 9 | A new group can be created with the `MsgCreateGroupRequest`, which has an admin address, a list of members and some optional metadata bytes. 10 | 11 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L53-L64 12 | 13 | It's expecting to fail if metadata length is greater than some `MaxMetadataLength`. 14 | 15 | ## MsgUpdateGroupMembers 16 | 17 | Group members can be updated with the `UpdateGroupMembersRequest`. 18 | 19 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L73-L85 20 | 21 | In the list of `MemberUpdates`, an existing member can be removed by setting its weight to 0. 22 | 23 | It's expecting to fail if the signer is not the admin of the group. 24 | 25 | ## MsgUpdateGroupAdmin 26 | 27 | The `UpdateGroupAdminRequest` can be used to update a group admin. 28 | 29 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L90-L101 30 | 31 | It's expecting to fail if the signer is not the admin of the group. 32 | 33 | ## MsgUpdateGroupMetadata 34 | 35 | The `UpdateGroupMetadataRequest` can be used to update a group metadata. 36 | 37 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L106-L117 38 | 39 | It's expecting to fail if: 40 | - new metadata length is greater than some `MaxMetadataLength`. 41 | - the signer is not the admin of the group. 42 | 43 | ## MsgCreateGroupAccount 44 | 45 | A new group account can be created with the `MsgCreateGroupAccountRequest`, which has an admin address, a group id, a decision policy and some optional metadata bytes. 46 | 47 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L126-L141 48 | 49 | It's expecting to fail if metadata length is greater than some `MaxMetadataLength`. 50 | 51 | ## MsgUpdateGroupAccountAdmin 52 | 53 | The `UpdateGroupAccountAdminRequest` can be used to update a group account admin. 54 | 55 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L150-L161 56 | 57 | It's expecting to fail if the signer is not the admin of the group account. 58 | 59 | ## MsgUpdateGroupAccountMetadata 60 | 61 | The `UpdateGroupAccountMetadataRequest` can be used to update a group account metadata. 62 | 63 | +++ https://github.com/regen-network/regen-ledger/blob/24290a0f2b219ac831482006952cb7a11d173ecf/proto/regen/group/v1alpha1/tx.proto#L183-L194 64 | 65 | It's expecting to fail if: 66 | - new metadata length is greater than some `MaxMetadataLength`. 67 | - the signer is not the admin of the group. 68 | -------------------------------------------------------------------------------- /x/group/spec/04_events.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Events 6 | 7 | The group module emits the following events: 8 | 9 | ## EventCreateGroup 10 | 11 | | Type | Attribute Key | Attribute Value | 12 | |---------------------------------------|---------------|---------------------------------------| 13 | | message | action | /regen.group.v1alpha1.Msg/CreateGroup | 14 | | regen.group.v1alpha1.EventCreateGroup | group_id | {groupId} | 15 | 16 | ## EventUpdateGroup 17 | 18 | | Type | Attribute Key | Attribute Value | 19 | |---------------------------------------|---------------|-----------------------------------------------------------------| 20 | | message | action | /regen.group.v1alpha1.Msg/UpdateGroup{Admin\|Metadata\|Members} | 21 | | regen.group.v1alpha1.EventUpdateGroup | group_id | {groupId} | 22 | 23 | ## EventCreateGroupAccount 24 | 25 | | Type | Attribute Key | Attribute Value | 26 | |----------------------------------------------|---------------|----------------------------------------------| 27 | | message | action | /regen.group.v1alpha1.Msg/CreateGroupAccount | 28 | | regen.group.v1alpha1.EventCreateGroupAccount | address | {groupAccountAddress} | 29 | 30 | ## EventUpdateGroupAccount 31 | 32 | | Type | Attribute Key | Attribute Value | 33 | |----------------------------------------------|---------------|-------------------------------------------------------------------------------| 34 | | message | action | /regen.group.v1alpha1.Msg/UpdateGroupAccount{Admin\|Metadata\|DecisionPolicy} | 35 | | regen.group.v1alpha1.EventUpdateGroupAccount | address | {groupAccountAddress} | -------------------------------------------------------------------------------- /x/group/spec/README.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | ## Group 9 | 10 | The following specification is a modified version. The original can be found 11 | [here](https://github.com/regen-network/regen-ledger/tree/v1.0.0/x/group/spec). 12 | 13 | To view the diff, click [here](./00_diff.md) 14 | 15 | This module allows the creation and management of on-chain multisig accounts and enables voting for message execution based on configurable decision policies. 16 | 17 | ## Contents 18 | 19 | 1. **[Concepts](01_concepts.md)** 20 | - [Group](01_concepts.md#group) 21 | - [Group Account](01_concepts.md#group-account) 22 | - [Decision Policy](01_concepts.md#decision-policy) 23 | - [Proposal](01_concepts.md#proposal) 24 | - [Voting](01_concepts.md#voting) 25 | - [Executing Proposals](01_concepts.md#executing-proposals) 26 | 2. **[State](02_state.md)** 27 | - [Group Table](02_state.md#group-table) 28 | - [Group Member Table](02_state.md#group-member-table) 29 | - [Group Account Table](02_state.md#group-account-table) 30 | - [Proposal](02_state.md#proposal-table) 31 | - [Vote Table](02_state.md#vote-table) 32 | 3. **[Msg Service](03_messages.md)** 33 | - [Msg/CreateGroup](03_messages.md#msgcreategroup) 34 | - [Msg/UpdateGroupMembers](03_messages.md#msgupdategroupmembers) 35 | - [Msg/UpdateGroupAdmin](03_messages.md#msgupdategroupadmin) 36 | - [Msg/UpdateGroupMetadata](03_messages.md#msgupdategroupmetadata) 37 | - [Msg/CreateGroupAccount](03_messages.md#msgcreategroupaccount) 38 | - [Msg/UpdateGroupAccountAdmin](03_messages.md#msgupdategroupaccountadmin) 39 | - [Msg/UpdateGroupAccountDecisionPolicy](03_messages.md#msgupdategroupaccountdecisionpolicy) 40 | - [Msg/UpdateGroupAccountMetadata](03_messages.md#msgupdategroupaccountmetadata) 41 | - [Msg/CreateProposal](03_messages.md#msgcreateproposal) 42 | - [Msg/Vote](03_messages.md#msgvote) 43 | - [Msg/Exec](03_messages.md#msgexec) 44 | 4. **[Events](04_events.md)** 45 | - [EventCreateGroup](04_events.md#eventcreategroup) 46 | - [EventUpdateGroup](04_events.md#eventupdategroup) 47 | - [EventCreateGroupAccount](04_events.md#eventcreategroupaccount) 48 | - [EventUpdateGroupAccount](04_events.md#eventupdategroupaccount) 49 | - [EventCreateProposal](04_events.md#eventcreateproposal) 50 | - [EventVote](04_events.md#eventvote) 51 | - [EventExec](04_events.md#eventexec) 52 | 53 | -------------------------------------------------------------------------------- /x/group/testdata/tx.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "bytes" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/gogo/protobuf/jsonpb" 8 | ) 9 | 10 | var _ sdk.Msg = &MsgAuthenticated{} 11 | 12 | func (m MsgAuthenticated) Route() string { return "MsgAuthenticated" } 13 | 14 | func (m MsgAuthenticated) Type() string { return "Msg Authenticated" } 15 | 16 | // GetSignBytes returns the bytes for the message signer to sign on 17 | func (m MsgAuthenticated) GetSignBytes() []byte { 18 | var buf bytes.Buffer 19 | enc := jsonpb.Marshaler{} 20 | if err := enc.Marshal(&buf, &m); err != nil { 21 | panic(err) 22 | } 23 | return sdk.MustSortJSON(buf.Bytes()) 24 | } 25 | 26 | // ValidateBasic does a sanity check on the provided data 27 | func (m MsgAuthenticated) ValidateBasic() error { 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /x/group/testdata/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package testdata; 4 | 5 | import "gogoproto/gogo.proto"; 6 | 7 | option go_package = "github.com/interchainberlin/metachain/x/group/testdata"; 8 | 9 | message MsgAuthenticated { 10 | repeated bytes signers = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; 11 | } 12 | -------------------------------------------------------------------------------- /x/group/typesupport.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | func (ms Members) ValidateBasic() error { 9 | index := make(map[string]struct{}, len(ms.Members)) 10 | for i := range ms.Members { 11 | member := ms.Members[i] 12 | if err := member.ValidateBasic(); err != nil { 13 | return err 14 | } 15 | addr := member.Address 16 | if _, exists := index[addr]; exists { 17 | return sdkerrors.Wrapf(ErrDuplicate, "address: %s", addr) 18 | } 19 | index[addr] = struct{}{} 20 | } 21 | return nil 22 | } 23 | 24 | type AccAddresses []sdk.AccAddress 25 | 26 | // ValidateBasic verifies that there's no duplicate address. 27 | // Individual account address validation has to be done separately. 28 | func (a AccAddresses) ValidateBasic() error { 29 | index := make(map[string]struct{}, len(a)) 30 | for i := range a { 31 | accAddr := a[i] 32 | addr := string(accAddr) 33 | if _, exists := index[addr]; exists { 34 | return sdkerrors.Wrapf(ErrDuplicate, "address: %s", accAddr.String()) 35 | } 36 | index[addr] = struct{}{} 37 | } 38 | return nil 39 | } 40 | --------------------------------------------------------------------------------