mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-10-31 21:47:27 +00:00 
			
		
		
		
	Committee migration (#665)
* draft: kava-3 to kava-4 committee migrations * migration: add busd, xrpb, btcb bep3 and cdp params to stability committee * add new pricefeed markets to safety committee * add harvest to committee
This commit is contained in:
		
							parent
							
								
									00f2068d1b
								
							
						
					
					
						commit
						8f69dcf960
					
				| @ -18,6 +18,8 @@ import ( | ||||
| 	v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9" | ||||
| 	v0_11cdp "github.com/kava-labs/kava/x/cdp" | ||||
| 	v0_9cdp "github.com/kava-labs/kava/x/cdp/legacy/v0_9" | ||||
| 	v0_11committee "github.com/kava-labs/kava/x/committee" | ||||
| 	v0_9committee "github.com/kava-labs/kava/x/committee/legacy/v0_9" | ||||
| 	v0_11harvest "github.com/kava-labs/kava/x/harvest" | ||||
| 	v0_11incentive "github.com/kava-labs/kava/x/incentive" | ||||
| 	v0_9incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_9" | ||||
| @ -164,6 +166,200 @@ func MigrateBep3(oldGenState v0_9bep3.GenesisState) v0_11bep3.GenesisState { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MigrateCommittee migrates from a v0.9 (or v0.10) committee genesis state to a v0.11 committee genesis state
 | ||||
| func MigrateCommittee(oldGenState v0_9committee.GenesisState) v0_11committee.GenesisState { | ||||
| 	var newCommittees []v0_11committee.Committee | ||||
| 	var newStabilityCommittee v0_11committee.Committee | ||||
| 	var newSafetyCommittee v0_11committee.Committee | ||||
| 	var newProposals []v0_11committee.Proposal | ||||
| 	var newVotes []v0_11committee.Vote | ||||
| 
 | ||||
| 	for _, committee := range oldGenState.Committees { | ||||
| 		if committee.ID == 1 { | ||||
| 			newStabilityCommittee.Description = committee.Description | ||||
| 			newStabilityCommittee.ID = committee.ID | ||||
| 			newStabilityCommittee.Members = committee.Members | ||||
| 			newStabilityCommittee.VoteThreshold = committee.VoteThreshold | ||||
| 			newStabilityCommittee.ProposalDuration = committee.ProposalDuration | ||||
| 			var newStabilityPermissions []v0_11committee.Permission | ||||
| 			var newStabilitySubParamPermissions v0_11committee.SubParamChangePermission | ||||
| 			for _, permission := range committee.Permissions { | ||||
| 				subPermission, ok := permission.(v0_9committee.SubParamChangePermission) | ||||
| 				if ok { | ||||
| 					oldCollateralParam := subPermission.AllowedCollateralParams[0] | ||||
| 					newCollateralParam := v0_11committee.AllowedCollateralParam{ | ||||
| 						Type:                "bnb-a", | ||||
| 						Denom:               false, | ||||
| 						AuctionSize:         oldCollateralParam.AuctionSize, | ||||
| 						ConversionFactor:    oldCollateralParam.ConversionFactor, | ||||
| 						DebtLimit:           oldCollateralParam.DebtLimit, | ||||
| 						LiquidationMarketID: oldCollateralParam.LiquidationMarketID, | ||||
| 						SpotMarketID:        oldCollateralParam.SpotMarketID, | ||||
| 						LiquidationPenalty:  oldCollateralParam.LiquidationPenalty, | ||||
| 						LiquidationRatio:    oldCollateralParam.LiquidationRatio, | ||||
| 						Prefix:              oldCollateralParam.Prefix, | ||||
| 						StabilityFee:        oldCollateralParam.StabilityFee, | ||||
| 					} | ||||
| 					oldDebtParam := subPermission.AllowedDebtParam | ||||
| 					newDebtParam := v0_11committee.AllowedDebtParam{ | ||||
| 						ConversionFactor: oldDebtParam.ConversionFactor, | ||||
| 						DebtFloor:        oldDebtParam.DebtFloor, | ||||
| 						Denom:            oldDebtParam.Denom, | ||||
| 						ReferenceAsset:   oldDebtParam.ReferenceAsset, | ||||
| 						SavingsRate:      oldDebtParam.SavingsRate, | ||||
| 					} | ||||
| 					oldAssetParam := subPermission.AllowedAssetParams[0] | ||||
| 					newAssetParam := v0_11committee.AllowedAssetParam{ | ||||
| 						Active:        oldAssetParam.Active, | ||||
| 						CoinID:        oldAssetParam.CoinID, | ||||
| 						Denom:         oldAssetParam.Denom, | ||||
| 						Limit:         oldAssetParam.Limit, | ||||
| 						MaxSwapAmount: true, | ||||
| 						MinBlockLock:  true, | ||||
| 					} | ||||
| 					oldMarketParams := subPermission.AllowedMarkets | ||||
| 					var newMarketParams v0_11committee.AllowedMarkets | ||||
| 					for _, oldMarketParam := range oldMarketParams { | ||||
| 						newMarketParam := v0_11committee.AllowedMarket(oldMarketParam) | ||||
| 						newMarketParams = append(newMarketParams, newMarketParam) | ||||
| 					} | ||||
| 					// add btc, xrp, busd markets to committee
 | ||||
| 					btcMarketParam := v0_11committee.AllowedMarket{ | ||||
| 						MarketID:   "btc:usd", | ||||
| 						BaseAsset:  false, | ||||
| 						QuoteAsset: false, | ||||
| 						Oracles:    false, | ||||
| 						Active:     true, | ||||
| 					} | ||||
| 					btc30MarketParam := v0_11committee.AllowedMarket{ | ||||
| 						MarketID:   "btc:usd:30", | ||||
| 						BaseAsset:  false, | ||||
| 						QuoteAsset: false, | ||||
| 						Oracles:    false, | ||||
| 						Active:     true, | ||||
| 					} | ||||
| 					xrpMarketParam := v0_11committee.AllowedMarket{ | ||||
| 						MarketID:   "xrp:usd", | ||||
| 						BaseAsset:  false, | ||||
| 						QuoteAsset: false, | ||||
| 						Oracles:    false, | ||||
| 						Active:     true, | ||||
| 					} | ||||
| 					xrp30MarketParam := v0_11committee.AllowedMarket{ | ||||
| 						MarketID:   "xrp:usd:30", | ||||
| 						BaseAsset:  false, | ||||
| 						QuoteAsset: false, | ||||
| 						Oracles:    false, | ||||
| 						Active:     true, | ||||
| 					} | ||||
| 					busdMarketParam := v0_11committee.AllowedMarket{ | ||||
| 						MarketID:   "busd:usd", | ||||
| 						BaseAsset:  false, | ||||
| 						QuoteAsset: false, | ||||
| 						Oracles:    false, | ||||
| 						Active:     true, | ||||
| 					} | ||||
| 					busd30MarketParam := v0_11committee.AllowedMarket{ | ||||
| 						MarketID:   "busd:usd:30", | ||||
| 						BaseAsset:  false, | ||||
| 						QuoteAsset: false, | ||||
| 						Oracles:    false, | ||||
| 						Active:     true, | ||||
| 					} | ||||
| 					newMarketParams = append(newMarketParams, btcMarketParam, btc30MarketParam, xrpMarketParam, xrp30MarketParam, busdMarketParam, busd30MarketParam) | ||||
| 					oldAllowedParams := subPermission.AllowedParams | ||||
| 					var newAllowedParams v0_11committee.AllowedParams | ||||
| 					for _, oldAllowedParam := range oldAllowedParams { | ||||
| 						newAllowedParam := v0_11committee.AllowedParam(oldAllowedParam) | ||||
| 						if oldAllowedParam.Subspace == "bep3" && oldAllowedParam.Key == "SupportedAssets" { | ||||
| 							newAllowedParam.Key = "AssetParams" | ||||
| 						} | ||||
| 						harvestParam := v0_11committee.AllowedParam{Subspace: "harvest", Key: "Active"} | ||||
| 
 | ||||
| 						newAllowedParams = append(newAllowedParams, newAllowedParam, harvestParam) | ||||
| 					} | ||||
| 
 | ||||
| 					// --------------- ADD BUSD, XRP-B, BTC-B BEP3 parameters to Stability Committee Permissions
 | ||||
| 					busdAllowedAssetParam := v0_11committee.AllowedAssetParam{ | ||||
| 						Active:        true, | ||||
| 						CoinID:        true, // allow busd coinID to be updated in case it gets its own slip-44
 | ||||
| 						Denom:         "busd", | ||||
| 						Limit:         true, | ||||
| 						MaxSwapAmount: true, | ||||
| 						MinBlockLock:  true, | ||||
| 					} | ||||
| 					xrpbAllowedAssetParam := v0_11committee.AllowedAssetParam{ | ||||
| 						Active:        true, | ||||
| 						CoinID:        false, | ||||
| 						Denom:         "xrpb", | ||||
| 						Limit:         true, | ||||
| 						MaxSwapAmount: true, | ||||
| 						MinBlockLock:  true, | ||||
| 					} | ||||
| 					btcbAllowedAssetParam := v0_11committee.AllowedAssetParam{ | ||||
| 						Active:        true, | ||||
| 						CoinID:        false, | ||||
| 						Denom:         "btcb", | ||||
| 						Limit:         true, | ||||
| 						MaxSwapAmount: true, | ||||
| 						MinBlockLock:  true, | ||||
| 					} | ||||
| 					// --------- ADD BTC-B, XRP-B, BUSD(a), BUSD(b) cdp collateral params to stability committee
 | ||||
| 					busdaAllowedCollateralParam := v0_11committee.NewAllowedCollateralParam( | ||||
| 						"busd-a", false, false, true, true, true, false, false, false, false, false, | ||||
| 					) | ||||
| 					busdbAllowedCollateralParam := v0_11committee.NewAllowedCollateralParam( | ||||
| 						"busd-b", false, false, true, true, true, false, false, false, false, false, | ||||
| 					) | ||||
| 					btcbAllowedCollateralParam := v0_11committee.NewAllowedCollateralParam( | ||||
| 						"btcb-a", false, false, true, true, true, false, false, false, false, false, | ||||
| 					) | ||||
| 					xrpbAllowedCollateralParam := v0_11committee.NewAllowedCollateralParam( | ||||
| 						"xrpb-a", false, false, true, true, true, false, false, false, false, false, | ||||
| 					) | ||||
| 
 | ||||
| 					newStabilitySubParamPermissions.AllowedAssetParams = v0_11committee.AllowedAssetParams{ | ||||
| 						newAssetParam, busdAllowedAssetParam, btcbAllowedAssetParam, xrpbAllowedAssetParam} | ||||
| 					newStabilitySubParamPermissions.AllowedCollateralParams = v0_11committee.AllowedCollateralParams{ | ||||
| 						newCollateralParam, busdaAllowedCollateralParam, busdbAllowedCollateralParam, btcbAllowedCollateralParam, xrpbAllowedCollateralParam} | ||||
| 					newStabilitySubParamPermissions.AllowedDebtParam = newDebtParam | ||||
| 					newStabilitySubParamPermissions.AllowedMarkets = newMarketParams | ||||
| 					newStabilitySubParamPermissions.AllowedParams = newAllowedParams | ||||
| 					newStabilityPermissions = append(newStabilityPermissions, newStabilitySubParamPermissions) | ||||
| 				} | ||||
| 			} | ||||
| 			newStabilityPermissions = append(newStabilityPermissions, v0_11committee.TextPermission{}) | ||||
| 			newStabilityCommittee.Permissions = newStabilityPermissions | ||||
| 			newCommittees = append(newCommittees, newStabilityCommittee) | ||||
| 		} else { | ||||
| 			newSafetyCommittee.ID = committee.ID | ||||
| 			newSafetyCommittee.Description = committee.Description | ||||
| 			newSafetyCommittee.Members = committee.Members | ||||
| 			newSafetyCommittee.Permissions = []v0_11committee.Permission{v0_11committee.SoftwareUpgradePermission{}} | ||||
| 			newSafetyCommittee.VoteThreshold = committee.VoteThreshold | ||||
| 			newSafetyCommittee.ProposalDuration = committee.ProposalDuration | ||||
| 			newCommittees = append(newCommittees, newSafetyCommittee) | ||||
| 		} | ||||
| 	} | ||||
| 	for _, oldProp := range oldGenState.Proposals { | ||||
| 		newPubProposal := v0_11committee.PubProposal(oldProp.PubProposal) | ||||
| 		newProp := v0_11committee.NewProposal(newPubProposal, oldProp.ID, oldProp.CommitteeID, oldProp.Deadline) | ||||
| 		newProposals = append(newProposals, newProp) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, oldVote := range oldGenState.Votes { | ||||
| 		newVote := v0_11committee.NewVote(oldVote.ProposalID, oldVote.Voter) | ||||
| 		newVotes = append(newVotes, newVote) | ||||
| 	} | ||||
| 
 | ||||
| 	return v0_11committee.GenesisState{ | ||||
| 		NextProposalID: oldGenState.NextProposalID, | ||||
| 		Committees:     newCommittees, | ||||
| 		Proposals:      newProposals, | ||||
| 		Votes:          newVotes, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MigrateAuth migrates from a v0.38.5 auth genesis state to a v0.39.1 auth genesis state
 | ||||
| func MigrateAuth(oldGenState v38_5auth.GenesisState) v39_1auth.GenesisState { | ||||
| 	var newAccounts v39_1authexported.GenesisAccounts | ||||
|  | ||||
| @ -6,9 +6,9 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/codec" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/codec" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 
 | ||||
| 	v39_1auth "github.com/cosmos/cosmos-sdk/x/auth" | ||||
| @ -20,6 +20,7 @@ import ( | ||||
| 	v38_5supply "github.com/kava-labs/kava/migrate/v0_11/legacy/cosmos-sdk/v0.38.5/supply" | ||||
| 	v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9" | ||||
| 	v0_9cdp "github.com/kava-labs/kava/x/cdp/legacy/v0_9" | ||||
| 	v0_9committee "github.com/kava-labs/kava/x/committee/legacy/v0_9" | ||||
| 	v0_9incentive "github.com/kava-labs/kava/x/incentive/legacy/v0_9" | ||||
| 	v0_9pricefeed "github.com/kava-labs/kava/x/pricefeed/legacy/v0_9" | ||||
| 	v0_11validator_vesting "github.com/kava-labs/kava/x/validator-vesting" | ||||
| @ -48,6 +49,22 @@ func TestMigrateBep3(t *testing.T) { | ||||
| 	require.NoError(t, err) | ||||
| } | ||||
| 
 | ||||
| func TestMigrateCommittee(t *testing.T) { | ||||
| 	bz, err := ioutil.ReadFile(filepath.Join("testdata", "committee-v09.json")) | ||||
| 	require.NoError(t, err) | ||||
| 	var oldGenState v0_9committee.GenesisState | ||||
| 	cdc := codec.New() | ||||
| 	sdk.RegisterCodec(cdc) | ||||
| 	v0_9committee.RegisterCodec(cdc) | ||||
| 
 | ||||
| 	require.NotPanics(t, func() { | ||||
| 		cdc.MustUnmarshalJSON(bz, &oldGenState) | ||||
| 	}) | ||||
| 
 | ||||
| 	newGenState := MigrateCommittee(oldGenState) | ||||
| 	err = newGenState.Validate() | ||||
| 	require.NoError(t, err) | ||||
| } | ||||
| func TestMigrateAuth(t *testing.T) { | ||||
| 	bz, err := ioutil.ReadFile(filepath.Join("testdata", "auth-v09.json")) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
							
								
								
									
										157
									
								
								migrate/v0_11/testdata/committee-v09.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								migrate/v0_11/testdata/committee-v09.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,157 @@ | ||||
| { | ||||
|   "committees": [ | ||||
|     { | ||||
|       "description": "Kava Stability Committee", | ||||
|       "id": "1", | ||||
|       "members": [ | ||||
|         "kava1gru35up50ql2wxhegr880qy6ynl63ujlv8gum2", | ||||
|         "kava1sc3mh3pkas5e7xd269am4xm5mp6zweyzmhjagj", | ||||
|         "kava1c9ye54e3pzwm3e0zpdlel6pnavrj9qqv6e8r4h", | ||||
|         "kava1m7p6sjqrz6mylz776ct48wj6lpnpcd0z82209d", | ||||
|         "kava1a9pmkzk570egv3sflu3uwdf3gejl7qfy9hghzl" | ||||
|       ], | ||||
|       "permissions": [ | ||||
|         { | ||||
|           "type": "kava/SubParamChangePermission", | ||||
|           "value": { | ||||
|             "allowed_asset_params": [ | ||||
|               { | ||||
|                 "active": true, | ||||
|                 "coin_id": false, | ||||
|                 "denom": "bnb", | ||||
|                 "limit": true | ||||
|               } | ||||
|             ], | ||||
|             "allowed_collateral_params": [ | ||||
|               { | ||||
|                 "auction_size": true, | ||||
|                 "conversion_factor": false, | ||||
|                 "debt_limit": true, | ||||
|                 "denom": "bnb", | ||||
|                 "liquidation_market_id": false, | ||||
|                 "liquidation_penalty": false, | ||||
|                 "liquidation_ratio": false, | ||||
|                 "prefix": false, | ||||
|                 "spot_market_id": false, | ||||
|                 "stability_fee": true | ||||
|               } | ||||
|             ], | ||||
|             "allowed_debt_param": { | ||||
|               "conversion_factor": false, | ||||
|               "debt_floor": true, | ||||
|               "denom": false, | ||||
|               "reference_asset": false, | ||||
|               "savings_rate": true | ||||
|             }, | ||||
|             "allowed_markets": [ | ||||
|               { | ||||
|                 "active": true, | ||||
|                 "base_asset": false, | ||||
|                 "market_id": "bnb:usd", | ||||
|                 "oracles": false, | ||||
|                 "quote_asset": false | ||||
|               }, | ||||
|               { | ||||
|                 "active": true, | ||||
|                 "base_asset": false, | ||||
|                 "market_id": "bnb:usd:30", | ||||
|                 "oracles": false, | ||||
|                 "quote_asset": false | ||||
|               } | ||||
|             ], | ||||
|             "allowed_params": [ | ||||
|               { | ||||
|                 "key": "BidDuration", | ||||
|                 "subspace": "auction" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "IncrementSurplus", | ||||
|                 "subspace": "auction" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "IncrementDebt", | ||||
|                 "subspace": "auction" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "IncrementCollateral", | ||||
|                 "subspace": "auction" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "SupportedAssets", | ||||
|                 "subspace": "bep3" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "GlobalDebtLimit", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "SurplusThreshold", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "SurplusLot", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "DebtThreshold", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "DebtLot", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "DistributionFrequency", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "CollateralParams", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "DebtParam", | ||||
|                 "subspace": "cdp" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "Active", | ||||
|                 "subspace": "incentive" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "Active", | ||||
|                 "subspace": "kavadist" | ||||
|               }, | ||||
|               { | ||||
|                 "key": "Markets", | ||||
|                 "subspace": "pricefeed" | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "type": "kava/TextPermission", | ||||
|           "value": {} | ||||
|         } | ||||
|       ], | ||||
|       "proposal_duration": "604800000000000", | ||||
|       "vote_threshold": "0.500000000000000000" | ||||
|     }, | ||||
|     { | ||||
|       "description": "Kava Safety Committee", | ||||
|       "id": "2", | ||||
|       "members": [ | ||||
|         "kava1e0agyg6eug9r62fly9sls77ycjgw8ax6xk73es" | ||||
|       ], | ||||
|       "permissions": [ | ||||
|         { | ||||
|           "type": "kava/SoftwareUpgradePermission", | ||||
|           "value": {} | ||||
|         } | ||||
|       ], | ||||
|       "proposal_duration": "604800000000000", | ||||
|       "vote_threshold": "0.500000000000000000" | ||||
|     } | ||||
|   ], | ||||
|   "next_proposal_id": "27", | ||||
|   "proposals": [], | ||||
|   "votes": [] | ||||
| } | ||||
| @ -29,8 +29,10 @@ const ( | ||||
| 	QuerierRoute                    = types.QuerierRoute | ||||
| 	QueryCommittee                  = types.QueryCommittee | ||||
| 	QueryCommittees                 = types.QueryCommittees | ||||
| 	QueryNextProposalID             = types.QueryNextProposalID | ||||
| 	QueryProposal                   = types.QueryProposal | ||||
| 	QueryProposals                  = types.QueryProposals | ||||
| 	QueryRawParams                  = types.QueryRawParams | ||||
| 	QueryTally                      = types.QueryTally | ||||
| 	QueryVote                       = types.QueryVote | ||||
| 	QueryVotes                      = types.QueryVotes | ||||
| @ -51,6 +53,7 @@ var ( | ||||
| 	DefaultGenesisState         = types.DefaultGenesisState | ||||
| 	GetKeyFromID                = types.GetKeyFromID | ||||
| 	GetVoteKey                  = types.GetVoteKey | ||||
| 	NewAllowedCollateralParam   = types.NewAllowedCollateralParam | ||||
| 	NewCommittee                = types.NewCommittee | ||||
| 	NewCommitteeChangeProposal  = types.NewCommitteeChangeProposal | ||||
| 	NewCommitteeDeleteProposal  = types.NewCommitteeDeleteProposal | ||||
| @ -60,6 +63,7 @@ var ( | ||||
| 	NewProposal                 = types.NewProposal | ||||
| 	NewQueryCommitteeParams     = types.NewQueryCommitteeParams | ||||
| 	NewQueryProposalParams      = types.NewQueryProposalParams | ||||
| 	NewQueryRawParamsParams     = types.NewQueryRawParamsParams | ||||
| 	NewQueryVoteParams          = types.NewQueryVoteParams | ||||
| 	NewVote                     = types.NewVote | ||||
| 	RegisterCodec               = types.RegisterCodec | ||||
| @ -77,6 +81,7 @@ var ( | ||||
| 	ErrProposalExpired         = types.ErrProposalExpired | ||||
| 	ErrUnknownCommittee        = types.ErrUnknownCommittee | ||||
| 	ErrUnknownProposal         = types.ErrUnknownProposal | ||||
| 	ErrUnknownSubspace         = types.ErrUnknownSubspace | ||||
| 	ErrUnknownVote             = types.ErrUnknownVote | ||||
| 	ModuleCdc                  = types.ModuleCdc | ||||
| 	NextProposalIDKey          = types.NextProposalIDKey | ||||
| @ -108,6 +113,7 @@ type ( | ||||
| 	PubProposal                 = types.PubProposal | ||||
| 	QueryCommitteeParams        = types.QueryCommitteeParams | ||||
| 	QueryProposalParams         = types.QueryProposalParams | ||||
| 	QueryRawParamsParams        = types.QueryRawParamsParams | ||||
| 	QueryVoteParams             = types.QueryVoteParams | ||||
| 	SimpleParamChangePermission = types.SimpleParamChangePermission | ||||
| 	SoftwareUpgradePermission   = types.SoftwareUpgradePermission | ||||
|  | ||||
							
								
								
									
										798
									
								
								x/committee/legacy/v0_11/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										798
									
								
								x/committee/legacy/v0_11/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,798 @@ | ||||
| package v0_11 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	yaml "gopkg.in/yaml.v2" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/codec" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" | ||||
| 	"github.com/cosmos/cosmos-sdk/x/params" | ||||
| 	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" | ||||
| 	upgrade "github.com/cosmos/cosmos-sdk/x/upgrade" | ||||
| 
 | ||||
| 	bep3types "github.com/kava-labs/kava/x/bep3/types" | ||||
| 	cdptypes "github.com/kava-labs/kava/x/cdp/types" | ||||
| 	"github.com/kava-labs/kava/x/pricefeed" | ||||
| 	pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	MaxCommitteeDescriptionLength int = 512 | ||||
| ) | ||||
| 
 | ||||
| // Permission is anything with a method that validates whether a proposal is allowed by it or not.
 | ||||
| type Permission interface { | ||||
| 	Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool | ||||
| } | ||||
| 
 | ||||
| type ParamKeeper interface { | ||||
| 	GetSubspace(string) (params.Subspace, bool) | ||||
| } | ||||
| 
 | ||||
| // A Committee is a collection of addresses that are allowed to vote and enact any governance proposal that passes their permissions.
 | ||||
| type Committee struct { | ||||
| 	ID               uint64           `json:"id" yaml:"id"` | ||||
| 	Description      string           `json:"description" yaml:"description"` | ||||
| 	Members          []sdk.AccAddress `json:"members" yaml:"members"` | ||||
| 	Permissions      []Permission     `json:"permissions" yaml:"permissions"` | ||||
| 	VoteThreshold    sdk.Dec          `json:"vote_threshold" yaml:"vote_threshold"`       // Smallest percentage of members that must vote for a proposal to pass.
 | ||||
| 	ProposalDuration time.Duration    `json:"proposal_duration" yaml:"proposal_duration"` // The length of time a proposal remains active for. Proposals will close earlier if they get enough votes.
 | ||||
| } | ||||
| 
 | ||||
| func NewCommittee(id uint64, description string, members []sdk.AccAddress, permissions []Permission, threshold sdk.Dec, duration time.Duration) Committee { | ||||
| 	return Committee{ | ||||
| 		ID:               id, | ||||
| 		Description:      description, | ||||
| 		Members:          members, | ||||
| 		Permissions:      permissions, | ||||
| 		VoteThreshold:    threshold, | ||||
| 		ProposalDuration: duration, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c Committee) HasMember(addr sdk.AccAddress) bool { | ||||
| 	for _, m := range c.Members { | ||||
| 		if m.Equals(addr) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // HasPermissionsFor returns whether the committee is authorized to enact a proposal.
 | ||||
| // As long as one permission allows the proposal then it goes through. Its the OR of all permissions.
 | ||||
| func (c Committee) HasPermissionsFor(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, proposal PubProposal) bool { | ||||
| 	for _, p := range c.Permissions { | ||||
| 		if p.Allows(ctx, appCdc, pk, proposal) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (c Committee) Validate() error { | ||||
| 
 | ||||
| 	addressMap := make(map[string]bool, len(c.Members)) | ||||
| 	for _, m := range c.Members { | ||||
| 		// check there are no duplicate members
 | ||||
| 		if _, ok := addressMap[m.String()]; ok { | ||||
| 			return fmt.Errorf("committe cannot have duplicate members, %s", m) | ||||
| 		} | ||||
| 		// check for valid addresses
 | ||||
| 		if m.Empty() { | ||||
| 			return fmt.Errorf("committee cannot have empty member address") | ||||
| 		} | ||||
| 		addressMap[m.String()] = true | ||||
| 	} | ||||
| 
 | ||||
| 	if len(c.Members) == 0 { | ||||
| 		return fmt.Errorf("committee cannot have zero members") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(c.Description) > MaxCommitteeDescriptionLength { | ||||
| 		return fmt.Errorf("description length %d longer than max allowed %d", len(c.Description), MaxCommitteeDescriptionLength) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, p := range c.Permissions { | ||||
| 		if p == nil { | ||||
| 			return fmt.Errorf("committee cannot have a nil permission") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// threshold must be in the range (0,1]
 | ||||
| 	if c.VoteThreshold.IsNil() || c.VoteThreshold.LTE(sdk.ZeroDec()) || c.VoteThreshold.GT(sdk.NewDec(1)) { | ||||
| 		return fmt.Errorf("invalid threshold: %s", c.VoteThreshold) | ||||
| 	} | ||||
| 
 | ||||
| 	if c.ProposalDuration < 0 { | ||||
| 		return fmt.Errorf("invalid proposal duration: %s", c.ProposalDuration) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				Proposals
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // PubProposal is the interface that all proposals must fulfill to be submitted to a committee.
 | ||||
| // Proposal types can be created external to this module. For example a ParamChangeProposal, or CommunityPoolSpendProposal.
 | ||||
| // It is pinned to the equivalent type in the gov module to create compatibility between proposal types.
 | ||||
| type PubProposal govtypes.Content | ||||
| 
 | ||||
| // Proposal is an internal record of a governance proposal submitted to a committee.
 | ||||
| type Proposal struct { | ||||
| 	PubProposal `json:"pub_proposal" yaml:"pub_proposal"` | ||||
| 	ID          uint64    `json:"id" yaml:"id"` | ||||
| 	CommitteeID uint64    `json:"committee_id" yaml:"committee_id"` | ||||
| 	Deadline    time.Time `json:"deadline" yaml:"deadline"` | ||||
| } | ||||
| 
 | ||||
| func NewProposal(pubProposal PubProposal, id uint64, committeeID uint64, deadline time.Time) Proposal { | ||||
| 	return Proposal{ | ||||
| 		PubProposal: pubProposal, | ||||
| 		ID:          id, | ||||
| 		CommitteeID: committeeID, | ||||
| 		Deadline:    deadline, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // HasExpiredBy calculates if the proposal will have expired by a certain time.
 | ||||
| // All votes must be cast before deadline, those cast at time == deadline are not valid
 | ||||
| func (p Proposal) HasExpiredBy(time time.Time) bool { | ||||
| 	return !time.Before(p.Deadline) | ||||
| } | ||||
| 
 | ||||
| // String implements the fmt.Stringer interface, and importantly overrides the String methods inherited from the embedded PubProposal type.
 | ||||
| func (p Proposal) String() string { | ||||
| 	bz, _ := yaml.Marshal(p) | ||||
| 	return string(bz) | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				Votes
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| type Vote struct { | ||||
| 	ProposalID uint64         `json:"proposal_id" yaml:"proposal_id"` | ||||
| 	Voter      sdk.AccAddress `json:"voter" yaml:"voter"` | ||||
| } | ||||
| 
 | ||||
| func NewVote(proposalID uint64, voter sdk.AccAddress) Vote { | ||||
| 	return Vote{ | ||||
| 		ProposalID: proposalID, | ||||
| 		Voter:      voter, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (v Vote) Validate() error { | ||||
| 	if v.Voter.Empty() { | ||||
| 		return fmt.Errorf("voter address cannot be empty") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				GodPermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // GodPermission allows any governance proposal. It is used mainly for testing.
 | ||||
| type GodPermission struct{} | ||||
| 
 | ||||
| var _ Permission = GodPermission{} | ||||
| 
 | ||||
| func (GodPermission) Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool { return true } | ||||
| 
 | ||||
| func (GodPermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type string `yaml:"type"` | ||||
| 	}{ | ||||
| 		Type: "god_permission", | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				SimpleParamChangePermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // SimpleParamChangePermission only allows changes to certain params
 | ||||
| type SimpleParamChangePermission struct { | ||||
| 	AllowedParams AllowedParams `json:"allowed_params" yaml:"allowed_params"` | ||||
| } | ||||
| 
 | ||||
| var _ Permission = SimpleParamChangePermission{} | ||||
| 
 | ||||
| func (perm SimpleParamChangePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { | ||||
| 	proposal, ok := p.(paramstypes.ParameterChangeProposal) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !perm.AllowedParams.Contains(change) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (perm SimpleParamChangePermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type          string        `yaml:"type"` | ||||
| 		AllowedParams AllowedParams `yaml:"allowed_params"` | ||||
| 	}{ | ||||
| 		Type:          "param_change_permission", | ||||
| 		AllowedParams: perm.AllowedParams, | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| type AllowedParam struct { | ||||
| 	Subspace string `json:"subspace" yaml:"subspace"` | ||||
| 	Key      string `json:"key" yaml:"key"` | ||||
| } | ||||
| type AllowedParams []AllowedParam | ||||
| 
 | ||||
| func (allowed AllowedParams) Contains(paramChange paramstypes.ParamChange) bool { | ||||
| 	for _, p := range allowed { | ||||
| 		if paramChange.Subspace == p.Subspace && paramChange.Key == p.Key { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				TextPermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // TextPermission allows any text governance proposal.
 | ||||
| type TextPermission struct{} | ||||
| 
 | ||||
| var _ Permission = TextPermission{} | ||||
| 
 | ||||
| func (TextPermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { | ||||
| 	_, ok := p.(govtypes.TextProposal) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (TextPermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type string `yaml:"type"` | ||||
| 	}{ | ||||
| 		Type: "text_permission", | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				SoftwareUpgradePermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| type SoftwareUpgradePermission struct{} | ||||
| 
 | ||||
| var _ Permission = SoftwareUpgradePermission{} | ||||
| 
 | ||||
| func (SoftwareUpgradePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { | ||||
| 	_, ok := p.(upgrade.SoftwareUpgradeProposal) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (SoftwareUpgradePermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type string `yaml:"type"` | ||||
| 	}{ | ||||
| 		Type: "software_upgrade_permission", | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				SubParamChangePermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // ParamChangeProposal only allows changes to certain params
 | ||||
| type SubParamChangePermission struct { | ||||
| 	AllowedParams           AllowedParams           `json:"allowed_params" yaml:"allowed_params"` | ||||
| 	AllowedCollateralParams AllowedCollateralParams `json:"allowed_collateral_params" yaml:"allowed_collateral_params"` | ||||
| 	AllowedDebtParam        AllowedDebtParam        `json:"allowed_debt_param" yaml:"allowed_debt_param"` | ||||
| 	AllowedAssetParams      AllowedAssetParams      `json:"allowed_asset_params" yaml:"allowed_asset_params"` | ||||
| 	AllowedMarkets          AllowedMarkets          `json:"allowed_markets" yaml:"allowed_markets"` | ||||
| } | ||||
| 
 | ||||
| var _ Permission = SubParamChangePermission{} | ||||
| 
 | ||||
| func (perm SubParamChangePermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type                    string                  `yaml:"type"` | ||||
| 		AllowedParams           AllowedParams           `yaml:"allowed_params"` | ||||
| 		AllowedCollateralParams AllowedCollateralParams `yaml:"allowed_collateral_params"` | ||||
| 		AllowedDebtParam        AllowedDebtParam        `yaml:"allowed_debt_param"` | ||||
| 		AllowedAssetParams      AllowedAssetParams      `yaml:"allowed_asset_params"` | ||||
| 		AllowedMarkets          AllowedMarkets          `yaml:"allowed_markets"` | ||||
| 	}{ | ||||
| 		Type:                    "param_change_permission", | ||||
| 		AllowedParams:           perm.AllowedParams, | ||||
| 		AllowedCollateralParams: perm.AllowedCollateralParams, | ||||
| 		AllowedDebtParam:        perm.AllowedDebtParam, | ||||
| 		AllowedAssetParams:      perm.AllowedAssetParams, | ||||
| 		AllowedMarkets:          perm.AllowedMarkets, | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, p PubProposal) bool { | ||||
| 	// Check pubproposal has correct type
 | ||||
| 	proposal, ok := p.(paramstypes.ParameterChangeProposal) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	// Check the param changes match the allowed keys
 | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !perm.AllowedParams.Contains(change) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	// Check any CollateralParam changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming CollaterParams value
 | ||||
| 	var foundIncomingCP bool | ||||
| 	var incomingCP cdptypes.CollateralParams | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == cdptypes.ModuleName && change.Key == string(cdptypes.KeyCollateralParams)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingCP = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingCP); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingCP { | ||||
| 		// Get the current value of the CollateralParams
 | ||||
| 		cdpSubspace, found := pk.GetSubspace(cdptypes.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentCP cdptypes.CollateralParams | ||||
| 		cdpSubspace.Get(ctx, cdptypes.KeyCollateralParams, ¤tCP) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check all the incoming changes in the CollateralParams are allowed
 | ||||
| 		collateralParamChangesAllowed := perm.AllowedCollateralParams.Allows(currentCP, incomingCP) | ||||
| 		if !collateralParamChangesAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check any DebtParam changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming DebtParam value
 | ||||
| 	var foundIncomingDP bool | ||||
| 	var incomingDP cdptypes.DebtParam | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == cdptypes.ModuleName && change.Key == string(cdptypes.KeyDebtParam)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingDP = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingDP); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingDP { | ||||
| 		// Get the current value of the DebtParams
 | ||||
| 		cdpSubspace, found := pk.GetSubspace(cdptypes.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentDP cdptypes.DebtParam | ||||
| 		cdpSubspace.Get(ctx, cdptypes.KeyDebtParam, ¤tDP) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check the incoming changes in the DebtParam are allowed
 | ||||
| 		debtParamChangeAllowed := perm.AllowedDebtParam.Allows(currentDP, incomingDP) | ||||
| 		if !debtParamChangeAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check any AssetParams changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming AssetParams value
 | ||||
| 	var foundIncomingAPs bool | ||||
| 	var incomingAPs bep3types.AssetParams | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == bep3types.ModuleName && change.Key == string(bep3types.KeyAssetParams)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingAPs = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingAPs); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingAPs { | ||||
| 		// Get the current value of the SupportedAssets
 | ||||
| 		subspace, found := pk.GetSubspace(bep3types.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentAPs bep3types.AssetParams | ||||
| 		subspace.Get(ctx, bep3types.KeyAssetParams, ¤tAPs) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check all the incoming changes in the CollateralParams are allowed
 | ||||
| 		assetParamsChangesAllowed := perm.AllowedAssetParams.Allows(currentAPs, incomingAPs) | ||||
| 		if !assetParamsChangesAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check any Markets changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming Markets value
 | ||||
| 	var foundIncomingMs bool | ||||
| 	var incomingMs pricefeedtypes.Markets | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == pricefeedtypes.ModuleName && change.Key == string(pricefeedtypes.KeyMarkets)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingMs = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingMs); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingMs { | ||||
| 		// Get the current value of the Markets
 | ||||
| 		subspace, found := pk.GetSubspace(pricefeedtypes.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentMs pricefeedtypes.Markets | ||||
| 		subspace.Get(ctx, pricefeedtypes.KeyMarkets, ¤tMs) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check all the incoming changes in the Markets are allowed
 | ||||
| 		marketsChangesAllowed := perm.AllowedMarkets.Allows(currentMs, incomingMs) | ||||
| 		if !marketsChangesAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // AllowedCollateralParam cdp.CollateralParam fields that can be subject to committee governance
 | ||||
| type AllowedCollateralParam struct { | ||||
| 	Type                string `json:"type" yaml:"type"` | ||||
| 	Denom               bool   `json:"denom" yaml:"denom"` | ||||
| 	LiquidationRatio    bool   `json:"liquidation_ratio" yaml:"liquidation_ratio"` | ||||
| 	DebtLimit           bool   `json:"debt_limit" yaml:"debt_limit"` | ||||
| 	StabilityFee        bool   `json:"stability_fee" yaml:"stability_fee"` | ||||
| 	AuctionSize         bool   `json:"auction_size" yaml:"auction_size"` | ||||
| 	LiquidationPenalty  bool   `json:"liquidation_penalty" yaml:"liquidation_penalty"` | ||||
| 	Prefix              bool   `json:"prefix" yaml:"prefix"` | ||||
| 	SpotMarketID        bool   `json:"spot_market_id" yaml:"spot_market_id"` | ||||
| 	LiquidationMarketID bool   `json:"liquidation_market_id" yaml:"liquidation_market_id"` | ||||
| 	ConversionFactor    bool   `json:"conversion_factor" yaml:"conversion_factor"` | ||||
| } | ||||
| 
 | ||||
| type AllowedCollateralParams []AllowedCollateralParam | ||||
| 
 | ||||
| func (acps AllowedCollateralParams) Allows(current, incoming cdptypes.CollateralParams) bool { | ||||
| 	allAllowed := true | ||||
| 
 | ||||
| 	// do not allow CollateralParams to be added or removed
 | ||||
| 	// this checks both lists are the same size, then below checks each incoming matches a current
 | ||||
| 	if len(incoming) != len(current) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// for each param struct, check it is allowed, and if it is not, check the value has not changed
 | ||||
| 	for _, incomingCP := range incoming { | ||||
| 		// 1) check incoming cp is in list of allowed cps
 | ||||
| 		var foundAllowedCP bool | ||||
| 		var allowedCP AllowedCollateralParam | ||||
| 		for _, p := range acps { | ||||
| 			if p.Type != incomingCP.Type { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundAllowedCP = true | ||||
| 			allowedCP = p | ||||
| 		} | ||||
| 		if !foundAllowedCP { | ||||
| 			// incoming had a CollateralParam that wasn't in the list of allowed ones
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// 2) Check incoming changes are individually allowed
 | ||||
| 		// find existing CollateralParam
 | ||||
| 		var foundCurrentCP bool | ||||
| 		var currentCP cdptypes.CollateralParam | ||||
| 		for _, p := range current { | ||||
| 			if p.Denom != incomingCP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundCurrentCP = true | ||||
| 			currentCP = p | ||||
| 		} | ||||
| 		if !foundCurrentCP { | ||||
| 			return false // not allowed to add param to list
 | ||||
| 		} | ||||
| 		// check changed values are all allowed
 | ||||
| 		allowed := allowedCP.Allows(currentCP, incomingCP) | ||||
| 
 | ||||
| 		allAllowed = allAllowed && allowed | ||||
| 	} | ||||
| 	return allAllowed | ||||
| } | ||||
| 
 | ||||
| func (acp AllowedCollateralParam) Allows(current, incoming cdptypes.CollateralParam) bool { | ||||
| 	allowed := ((acp.Type == current.Type) && (acp.Type == incoming.Type)) && // require collatreral types to be all equal
 | ||||
| 		(current.Denom == incoming.Denom || acp.Denom) && | ||||
| 		(current.LiquidationRatio.Equal(incoming.LiquidationRatio) || acp.LiquidationRatio) && | ||||
| 		(current.DebtLimit.IsEqual(incoming.DebtLimit) || acp.DebtLimit) && | ||||
| 		(current.StabilityFee.Equal(incoming.StabilityFee) || acp.StabilityFee) && | ||||
| 		(current.AuctionSize.Equal(incoming.AuctionSize) || acp.AuctionSize) && | ||||
| 		(current.LiquidationPenalty.Equal(incoming.LiquidationPenalty) || acp.LiquidationPenalty) && | ||||
| 		((current.Prefix == incoming.Prefix) || acp.Prefix) && | ||||
| 		((current.SpotMarketID == incoming.SpotMarketID) || acp.SpotMarketID) && | ||||
| 		((current.LiquidationMarketID == incoming.LiquidationMarketID) || acp.LiquidationMarketID) && | ||||
| 		(current.ConversionFactor.Equal(incoming.ConversionFactor) || acp.ConversionFactor) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| type AllowedDebtParam struct { | ||||
| 	Denom            bool `json:"denom" yaml:"denom"` | ||||
| 	ReferenceAsset   bool `json:"reference_asset" yaml:"reference_asset"` | ||||
| 	ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"` | ||||
| 	DebtFloor        bool `json:"debt_floor" yaml:"debt_floor"` | ||||
| 	SavingsRate      bool `json:"savings_rate" yaml:"savings_rate"` | ||||
| } | ||||
| 
 | ||||
| func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool { | ||||
| 	allowed := ((current.Denom == incoming.Denom) || adp.Denom) && | ||||
| 		((current.ReferenceAsset == incoming.ReferenceAsset) || adp.ReferenceAsset) && | ||||
| 		(current.ConversionFactor.Equal(incoming.ConversionFactor) || adp.ConversionFactor) && | ||||
| 		(current.DebtFloor.Equal(incoming.DebtFloor) || adp.DebtFloor) && | ||||
| 		(current.SavingsRate.Equal(incoming.SavingsRate) || adp.SavingsRate) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| type AllowedAssetParams []AllowedAssetParam | ||||
| 
 | ||||
| func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) bool { | ||||
| 	allAllowed := true | ||||
| 
 | ||||
| 	// do not allow AssetParams to be added or removed
 | ||||
| 	// this checks both lists are the same size, then below checks each incoming matches a current
 | ||||
| 	if len(incoming) != len(current) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// for each asset struct, check it is allowed, and if it is not, check the value has not changed
 | ||||
| 	for _, incomingAP := range incoming { | ||||
| 		// 1) check incoming ap is in list of allowed aps
 | ||||
| 		var foundAllowedAP bool | ||||
| 		var allowedAP AllowedAssetParam | ||||
| 		for _, p := range aaps { | ||||
| 			if p.Denom != incomingAP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundAllowedAP = true | ||||
| 			allowedAP = p | ||||
| 		} | ||||
| 		if !foundAllowedAP { | ||||
| 			// incoming had a AssetParam that wasn't in the list of allowed ones
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// 2) Check incoming changes are individually allowed
 | ||||
| 		// find existing SupportedAsset
 | ||||
| 		var foundCurrentAP bool | ||||
| 		var currentAP bep3types.AssetParam | ||||
| 		for _, p := range current { | ||||
| 			if p.Denom != incomingAP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundCurrentAP = true | ||||
| 			currentAP = p | ||||
| 		} | ||||
| 		if !foundCurrentAP { | ||||
| 			return false // not allowed to add asset to list
 | ||||
| 		} | ||||
| 		// check changed values are all allowed
 | ||||
| 		allowed := allowedAP.Allows(currentAP, incomingAP) | ||||
| 
 | ||||
| 		allAllowed = allAllowed && allowed | ||||
| 	} | ||||
| 	return allAllowed | ||||
| } | ||||
| 
 | ||||
| type AllowedAssetParam struct { | ||||
| 	Denom  string `json:"denom" yaml:"denom"` | ||||
| 	CoinID bool   `json:"coin_id" yaml:"coin_id"` | ||||
| 	Limit  bool   `json:"limit" yaml:"limit"` | ||||
| 	Active bool   `json:"active" yaml:"active"` | ||||
| } | ||||
| 
 | ||||
| func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool { | ||||
| 
 | ||||
| 	allowed := ((aap.Denom == current.Denom) && (aap.Denom == incoming.Denom)) && // require denoms to be all equal
 | ||||
| 		((current.CoinID == incoming.CoinID) || aap.CoinID) && | ||||
| 		(current.SupplyLimit.Equals(incoming.SupplyLimit) || aap.Limit) && | ||||
| 		((current.Active == incoming.Active) || aap.Active) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| type AllowedMarkets []AllowedMarket | ||||
| 
 | ||||
| func (ams AllowedMarkets) Allows(current, incoming pricefeedtypes.Markets) bool { | ||||
| 	allAllowed := true | ||||
| 
 | ||||
| 	// do not allow Markets to be added or removed
 | ||||
| 	// this checks both lists are the same size, then below checks each incoming matches a current
 | ||||
| 	if len(incoming) != len(current) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// for each market struct, check it is allowed, and if it is not, check the value has not changed
 | ||||
| 	for _, incomingM := range incoming { | ||||
| 		// 1) check incoming market is in list of allowed markets
 | ||||
| 		var foundAllowedM bool | ||||
| 		var allowedM AllowedMarket | ||||
| 		for _, p := range ams { | ||||
| 			if p.MarketID != incomingM.MarketID { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundAllowedM = true | ||||
| 			allowedM = p | ||||
| 		} | ||||
| 		if !foundAllowedM { | ||||
| 			// incoming had a Market that wasn't in the list of allowed ones
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// 2) Check incoming changes are individually allowed
 | ||||
| 		// find existing SupportedAsset
 | ||||
| 		var foundCurrentM bool | ||||
| 		var currentM pricefeed.Market | ||||
| 		for _, p := range current { | ||||
| 			if p.MarketID != incomingM.MarketID { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundCurrentM = true | ||||
| 			currentM = p | ||||
| 		} | ||||
| 		if !foundCurrentM { | ||||
| 			return false // not allowed to add market to list
 | ||||
| 		} | ||||
| 		// check changed values are all allowed
 | ||||
| 		allowed := allowedM.Allows(currentM, incomingM) | ||||
| 
 | ||||
| 		allAllowed = allAllowed && allowed | ||||
| 	} | ||||
| 	return allAllowed | ||||
| } | ||||
| 
 | ||||
| type AllowedMarket struct { | ||||
| 	MarketID   string `json:"market_id" yaml:"market_id"` | ||||
| 	BaseAsset  bool   `json:"base_asset" yaml:"base_asset"` | ||||
| 	QuoteAsset bool   `json:"quote_asset" yaml:"quote_asset"` | ||||
| 	Oracles    bool   `json:"oracles" yaml:"oracles"` | ||||
| 	Active     bool   `json:"active" yaml:"active"` | ||||
| } | ||||
| 
 | ||||
| func (am AllowedMarket) Allows(current, incoming pricefeedtypes.Market) bool { | ||||
| 	allowed := ((am.MarketID == current.MarketID) && (am.MarketID == incoming.MarketID)) && // require denoms to be all equal
 | ||||
| 		((current.BaseAsset == incoming.BaseAsset) || am.BaseAsset) && | ||||
| 		((current.QuoteAsset == incoming.QuoteAsset) || am.QuoteAsset) && | ||||
| 		(addressesEqual(current.Oracles, incoming.Oracles) || am.Oracles) && | ||||
| 		((current.Active == incoming.Active) || am.Active) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| // addressesEqual check if slices of addresses are equal, the order matters
 | ||||
| func addressesEqual(addrs1, addrs2 []sdk.AccAddress) bool { | ||||
| 	if len(addrs1) != len(addrs2) { | ||||
| 		return false | ||||
| 	} | ||||
| 	areEqual := true | ||||
| 	for i := range addrs1 { | ||||
| 		areEqual = areEqual && addrs1[i].Equals(addrs2[i]) | ||||
| 	} | ||||
| 	return areEqual | ||||
| } | ||||
| 
 | ||||
| // DefaultNextProposalID is the starting poiint for proposal IDs.
 | ||||
| const DefaultNextProposalID uint64 = 1 | ||||
| 
 | ||||
| // GenesisState is state that must be provided at chain genesis.
 | ||||
| type GenesisState struct { | ||||
| 	NextProposalID uint64      `json:"next_proposal_id" yaml:"next_proposal_id"` | ||||
| 	Committees     []Committee `json:"committees" yaml:"committees"` | ||||
| 	Proposals      []Proposal  `json:"proposals" yaml:"proposals"` | ||||
| 	Votes          []Vote      `json:"votes" yaml:"votes"` | ||||
| } | ||||
| 
 | ||||
| // NewGenesisState returns a new genesis state object for the module.
 | ||||
| func NewGenesisState(nextProposalID uint64, committees []Committee, proposals []Proposal, votes []Vote) GenesisState { | ||||
| 	return GenesisState{ | ||||
| 		NextProposalID: nextProposalID, | ||||
| 		Committees:     committees, | ||||
| 		Proposals:      proposals, | ||||
| 		Votes:          votes, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DefaultGenesisState returns the default genesis state for the module.
 | ||||
| func DefaultGenesisState() GenesisState { | ||||
| 	return NewGenesisState( | ||||
| 		DefaultNextProposalID, | ||||
| 		[]Committee{}, | ||||
| 		[]Proposal{}, | ||||
| 		[]Vote{}, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| // Validate performs basic validation of genesis data.
 | ||||
| func (gs GenesisState) Validate() error { | ||||
| 	// validate committees
 | ||||
| 	committeeMap := make(map[uint64]bool, len(gs.Committees)) | ||||
| 	for _, com := range gs.Committees { | ||||
| 		// check there are no duplicate IDs
 | ||||
| 		if _, ok := committeeMap[com.ID]; ok { | ||||
| 			return fmt.Errorf("duplicate committee ID found in genesis state; id: %d", com.ID) | ||||
| 		} | ||||
| 		committeeMap[com.ID] = true | ||||
| 
 | ||||
| 		// validate committee
 | ||||
| 		if err := com.Validate(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// validate proposals
 | ||||
| 	proposalMap := make(map[uint64]bool, len(gs.Proposals)) | ||||
| 	for _, p := range gs.Proposals { | ||||
| 		// check there are no duplicate IDs
 | ||||
| 		if _, ok := proposalMap[p.ID]; ok { | ||||
| 			return fmt.Errorf("duplicate proposal ID found in genesis state; id: %d", p.ID) | ||||
| 		} | ||||
| 		proposalMap[p.ID] = true | ||||
| 
 | ||||
| 		// validate next proposal ID
 | ||||
| 		if p.ID >= gs.NextProposalID { | ||||
| 			return fmt.Errorf("NextProposalID is not greater than all proposal IDs; id: %d", p.ID) | ||||
| 		} | ||||
| 
 | ||||
| 		// check committee exists
 | ||||
| 		if !committeeMap[p.CommitteeID] { | ||||
| 			return fmt.Errorf("proposal refers to non existent committee; proposal: %+v", p) | ||||
| 		} | ||||
| 
 | ||||
| 		// validate pubProposal
 | ||||
| 		if err := p.PubProposal.ValidateBasic(); err != nil { | ||||
| 			return fmt.Errorf("proposal %d invalid: %w", p.ID, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// validate votes
 | ||||
| 	for _, v := range gs.Votes { | ||||
| 		// validate committee
 | ||||
| 		if err := v.Validate(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		// check proposal exists
 | ||||
| 		if !proposalMap[v.ProposalID] { | ||||
| 			return fmt.Errorf("vote refers to non existent proposal; vote: %+v", v) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										810
									
								
								x/committee/legacy/v0_9/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										810
									
								
								x/committee/legacy/v0_9/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,810 @@ | ||||
| package v0_9 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	yaml "gopkg.in/yaml.v2" | ||||
| 
 | ||||
| 	"github.com/cosmos/cosmos-sdk/codec" | ||||
| 	sdk "github.com/cosmos/cosmos-sdk/types" | ||||
| 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" | ||||
| 	"github.com/cosmos/cosmos-sdk/x/params" | ||||
| 	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" | ||||
| 	upgrade "github.com/cosmos/cosmos-sdk/x/upgrade" | ||||
| 
 | ||||
| 	bep3types "github.com/kava-labs/kava/x/bep3/types" | ||||
| 	cdptypes "github.com/kava-labs/kava/x/cdp/types" | ||||
| 	"github.com/kava-labs/kava/x/pricefeed" | ||||
| 	pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	MaxCommitteeDescriptionLength int = 512 | ||||
| ) | ||||
| 
 | ||||
| // Permission is anything with a method that validates whether a proposal is allowed by it or not.
 | ||||
| type Permission interface { | ||||
| 	Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool | ||||
| } | ||||
| 
 | ||||
| type ParamKeeper interface { | ||||
| 	GetSubspace(string) (params.Subspace, bool) | ||||
| } | ||||
| 
 | ||||
| // A Committee is a collection of addresses that are allowed to vote and enact any governance proposal that passes their permissions.
 | ||||
| type Committee struct { | ||||
| 	ID               uint64           `json:"id" yaml:"id"` | ||||
| 	Description      string           `json:"description" yaml:"description"` | ||||
| 	Members          []sdk.AccAddress `json:"members" yaml:"members"` | ||||
| 	Permissions      []Permission     `json:"permissions" yaml:"permissions"` | ||||
| 	VoteThreshold    sdk.Dec          `json:"vote_threshold" yaml:"vote_threshold"`       // Smallest percentage of members that must vote for a proposal to pass.
 | ||||
| 	ProposalDuration time.Duration    `json:"proposal_duration" yaml:"proposal_duration"` // The length of time a proposal remains active for. Proposals will close earlier if they get enough votes.
 | ||||
| } | ||||
| 
 | ||||
| func NewCommittee(id uint64, description string, members []sdk.AccAddress, permissions []Permission, threshold sdk.Dec, duration time.Duration) Committee { | ||||
| 	return Committee{ | ||||
| 		ID:               id, | ||||
| 		Description:      description, | ||||
| 		Members:          members, | ||||
| 		Permissions:      permissions, | ||||
| 		VoteThreshold:    threshold, | ||||
| 		ProposalDuration: duration, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c Committee) HasMember(addr sdk.AccAddress) bool { | ||||
| 	for _, m := range c.Members { | ||||
| 		if m.Equals(addr) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // HasPermissionsFor returns whether the committee is authorized to enact a proposal.
 | ||||
| // As long as one permission allows the proposal then it goes through. Its the OR of all permissions.
 | ||||
| func (c Committee) HasPermissionsFor(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, proposal PubProposal) bool { | ||||
| 	for _, p := range c.Permissions { | ||||
| 		if p.Allows(ctx, appCdc, pk, proposal) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (c Committee) Validate() error { | ||||
| 
 | ||||
| 	addressMap := make(map[string]bool, len(c.Members)) | ||||
| 	for _, m := range c.Members { | ||||
| 		// check there are no duplicate members
 | ||||
| 		if _, ok := addressMap[m.String()]; ok { | ||||
| 			return fmt.Errorf("committe cannot have duplicate members, %s", m) | ||||
| 		} | ||||
| 		// check for valid addresses
 | ||||
| 		if m.Empty() { | ||||
| 			return fmt.Errorf("committee cannot have empty member address") | ||||
| 		} | ||||
| 		addressMap[m.String()] = true | ||||
| 	} | ||||
| 
 | ||||
| 	if len(c.Members) == 0 { | ||||
| 		return fmt.Errorf("committee cannot have zero members") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(c.Description) > MaxCommitteeDescriptionLength { | ||||
| 		return fmt.Errorf("description length %d longer than max allowed %d", len(c.Description), MaxCommitteeDescriptionLength) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, p := range c.Permissions { | ||||
| 		if p == nil { | ||||
| 			return fmt.Errorf("committee cannot have a nil permission") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// threshold must be in the range (0,1]
 | ||||
| 	if c.VoteThreshold.IsNil() || c.VoteThreshold.LTE(sdk.ZeroDec()) || c.VoteThreshold.GT(sdk.NewDec(1)) { | ||||
| 		return fmt.Errorf("invalid threshold: %s", c.VoteThreshold) | ||||
| 	} | ||||
| 
 | ||||
| 	if c.ProposalDuration < 0 { | ||||
| 		return fmt.Errorf("invalid proposal duration: %s", c.ProposalDuration) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				Proposals
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // PubProposal is the interface that all proposals must fulfill to be submitted to a committee.
 | ||||
| // Proposal types can be created external to this module. For example a ParamChangeProposal, or CommunityPoolSpendProposal.
 | ||||
| // It is pinned to the equivalent type in the gov module to create compatibility between proposal types.
 | ||||
| type PubProposal govtypes.Content | ||||
| 
 | ||||
| // Proposal is an internal record of a governance proposal submitted to a committee.
 | ||||
| type Proposal struct { | ||||
| 	PubProposal `json:"pub_proposal" yaml:"pub_proposal"` | ||||
| 	ID          uint64    `json:"id" yaml:"id"` | ||||
| 	CommitteeID uint64    `json:"committee_id" yaml:"committee_id"` | ||||
| 	Deadline    time.Time `json:"deadline" yaml:"deadline"` | ||||
| } | ||||
| 
 | ||||
| func NewProposal(pubProposal PubProposal, id uint64, committeeID uint64, deadline time.Time) Proposal { | ||||
| 	return Proposal{ | ||||
| 		PubProposal: pubProposal, | ||||
| 		ID:          id, | ||||
| 		CommitteeID: committeeID, | ||||
| 		Deadline:    deadline, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // HasExpiredBy calculates if the proposal will have expired by a certain time.
 | ||||
| // All votes must be cast before deadline, those cast at time == deadline are not valid
 | ||||
| func (p Proposal) HasExpiredBy(time time.Time) bool { | ||||
| 	return !time.Before(p.Deadline) | ||||
| } | ||||
| 
 | ||||
| // String implements the fmt.Stringer interface, and importantly overrides the String methods inherited from the embedded PubProposal type.
 | ||||
| func (p Proposal) String() string { | ||||
| 	bz, _ := yaml.Marshal(p) | ||||
| 	return string(bz) | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				Votes
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| type Vote struct { | ||||
| 	ProposalID uint64         `json:"proposal_id" yaml:"proposal_id"` | ||||
| 	Voter      sdk.AccAddress `json:"voter" yaml:"voter"` | ||||
| } | ||||
| 
 | ||||
| func NewVote(proposalID uint64, voter sdk.AccAddress) Vote { | ||||
| 	return Vote{ | ||||
| 		ProposalID: proposalID, | ||||
| 		Voter:      voter, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (v Vote) Validate() error { | ||||
| 	if v.Voter.Empty() { | ||||
| 		return fmt.Errorf("voter address cannot be empty") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				GodPermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // GodPermission allows any governance proposal. It is used mainly for testing.
 | ||||
| type GodPermission struct{} | ||||
| 
 | ||||
| var _ Permission = GodPermission{} | ||||
| 
 | ||||
| func (GodPermission) Allows(sdk.Context, *codec.Codec, ParamKeeper, PubProposal) bool { return true } | ||||
| 
 | ||||
| func (GodPermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type string `yaml:"type"` | ||||
| 	}{ | ||||
| 		Type: "god_permission", | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				SimpleParamChangePermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // SimpleParamChangePermission only allows changes to certain params
 | ||||
| type SimpleParamChangePermission struct { | ||||
| 	AllowedParams AllowedParams `json:"allowed_params" yaml:"allowed_params"` | ||||
| } | ||||
| 
 | ||||
| var _ Permission = SimpleParamChangePermission{} | ||||
| 
 | ||||
| func (perm SimpleParamChangePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { | ||||
| 	proposal, ok := p.(paramstypes.ParameterChangeProposal) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !perm.AllowedParams.Contains(change) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (perm SimpleParamChangePermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type          string        `yaml:"type"` | ||||
| 		AllowedParams AllowedParams `yaml:"allowed_params"` | ||||
| 	}{ | ||||
| 		Type:          "param_change_permission", | ||||
| 		AllowedParams: perm.AllowedParams, | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| type AllowedParam struct { | ||||
| 	Subspace string `json:"subspace" yaml:"subspace"` | ||||
| 	Key      string `json:"key" yaml:"key"` | ||||
| } | ||||
| type AllowedParams []AllowedParam | ||||
| 
 | ||||
| func (allowed AllowedParams) Contains(paramChange paramstypes.ParamChange) bool { | ||||
| 	for _, p := range allowed { | ||||
| 		if paramChange.Subspace == p.Subspace && paramChange.Key == p.Key { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				TextPermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // TextPermission allows any text governance proposal.
 | ||||
| type TextPermission struct{} | ||||
| 
 | ||||
| var _ Permission = TextPermission{} | ||||
| 
 | ||||
| func (TextPermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { | ||||
| 	_, ok := p.(govtypes.TextProposal) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (TextPermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type string `yaml:"type"` | ||||
| 	}{ | ||||
| 		Type: "text_permission", | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				SoftwareUpgradePermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| type SoftwareUpgradePermission struct{} | ||||
| 
 | ||||
| var _ Permission = SoftwareUpgradePermission{} | ||||
| 
 | ||||
| func (SoftwareUpgradePermission) Allows(_ sdk.Context, _ *codec.Codec, _ ParamKeeper, p PubProposal) bool { | ||||
| 	_, ok := p.(upgrade.SoftwareUpgradeProposal) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func (SoftwareUpgradePermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type string `yaml:"type"` | ||||
| 	}{ | ||||
| 		Type: "software_upgrade_permission", | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| // ------------------------------------------
 | ||||
| //				SubParamChangePermission
 | ||||
| // ------------------------------------------
 | ||||
| 
 | ||||
| // ParamChangeProposal only allows changes to certain params
 | ||||
| type SubParamChangePermission struct { | ||||
| 	AllowedParams           AllowedParams           `json:"allowed_params" yaml:"allowed_params"` | ||||
| 	AllowedCollateralParams AllowedCollateralParams `json:"allowed_collateral_params" yaml:"allowed_collateral_params"` | ||||
| 	AllowedDebtParam        AllowedDebtParam        `json:"allowed_debt_param" yaml:"allowed_debt_param"` | ||||
| 	AllowedAssetParams      AllowedAssetParams      `json:"allowed_asset_params" yaml:"allowed_asset_params"` | ||||
| 	AllowedMarkets          AllowedMarkets          `json:"allowed_markets" yaml:"allowed_markets"` | ||||
| } | ||||
| 
 | ||||
| var _ Permission = SubParamChangePermission{} | ||||
| 
 | ||||
| func (perm SubParamChangePermission) MarshalYAML() (interface{}, error) { | ||||
| 	valueToMarshal := struct { | ||||
| 		Type                    string                  `yaml:"type"` | ||||
| 		AllowedParams           AllowedParams           `yaml:"allowed_params"` | ||||
| 		AllowedCollateralParams AllowedCollateralParams `yaml:"allowed_collateral_params"` | ||||
| 		AllowedDebtParam        AllowedDebtParam        `yaml:"allowed_debt_param"` | ||||
| 		AllowedAssetParams      AllowedAssetParams      `yaml:"allowed_asset_params"` | ||||
| 		AllowedMarkets          AllowedMarkets          `yaml:"allowed_markets"` | ||||
| 	}{ | ||||
| 		Type:                    "param_change_permission", | ||||
| 		AllowedParams:           perm.AllowedParams, | ||||
| 		AllowedCollateralParams: perm.AllowedCollateralParams, | ||||
| 		AllowedDebtParam:        perm.AllowedDebtParam, | ||||
| 		AllowedAssetParams:      perm.AllowedAssetParams, | ||||
| 		AllowedMarkets:          perm.AllowedMarkets, | ||||
| 	} | ||||
| 	return valueToMarshal, nil | ||||
| } | ||||
| 
 | ||||
| func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec, pk ParamKeeper, p PubProposal) bool { | ||||
| 	// Check pubproposal has correct type
 | ||||
| 	proposal, ok := p.(paramstypes.ParameterChangeProposal) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	// Check the param changes match the allowed keys
 | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !perm.AllowedParams.Contains(change) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	// Check any CollateralParam changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming CollaterParams value
 | ||||
| 	var foundIncomingCP bool | ||||
| 	var incomingCP cdptypes.CollateralParams | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == cdptypes.ModuleName && change.Key == string(cdptypes.KeyCollateralParams)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingCP = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingCP); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingCP { | ||||
| 		// Get the current value of the CollateralParams
 | ||||
| 		cdpSubspace, found := pk.GetSubspace(cdptypes.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentCP cdptypes.CollateralParams | ||||
| 		cdpSubspace.Get(ctx, cdptypes.KeyCollateralParams, ¤tCP) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check all the incoming changes in the CollateralParams are allowed
 | ||||
| 		collateralParamChangesAllowed := perm.AllowedCollateralParams.Allows(currentCP, incomingCP) | ||||
| 		if !collateralParamChangesAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check any DebtParam changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming DebtParam value
 | ||||
| 	var foundIncomingDP bool | ||||
| 	var incomingDP cdptypes.DebtParam | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == cdptypes.ModuleName && change.Key == string(cdptypes.KeyDebtParam)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingDP = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingDP); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingDP { | ||||
| 		// Get the current value of the DebtParams
 | ||||
| 		cdpSubspace, found := pk.GetSubspace(cdptypes.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentDP cdptypes.DebtParam | ||||
| 		cdpSubspace.Get(ctx, cdptypes.KeyDebtParam, ¤tDP) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check the incoming changes in the DebtParam are allowed
 | ||||
| 		debtParamChangeAllowed := perm.AllowedDebtParam.Allows(currentDP, incomingDP) | ||||
| 		if !debtParamChangeAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check any AssetParams changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming AssetParams value
 | ||||
| 	var foundIncomingAPs bool | ||||
| 	var incomingAPs bep3types.AssetParams | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == bep3types.ModuleName && change.Key == string(bep3types.KeyAssetParams)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingAPs = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingAPs); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingAPs { | ||||
| 		// Get the current value of the SupportedAssets
 | ||||
| 		subspace, found := pk.GetSubspace(bep3types.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentAPs bep3types.AssetParams | ||||
| 		subspace.Get(ctx, bep3types.KeyAssetParams, ¤tAPs) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check all the incoming changes in the CollateralParams are allowed
 | ||||
| 		assetParamsChangesAllowed := perm.AllowedAssetParams.Allows(currentAPs, incomingAPs) | ||||
| 		if !assetParamsChangesAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check any Markets changes are allowed
 | ||||
| 
 | ||||
| 	// Get the incoming Markets value
 | ||||
| 	var foundIncomingMs bool | ||||
| 	var incomingMs pricefeedtypes.Markets | ||||
| 	for _, change := range proposal.Changes { | ||||
| 		if !(change.Subspace == pricefeedtypes.ModuleName && change.Key == string(pricefeedtypes.KeyMarkets)) { | ||||
| 			continue | ||||
| 		} | ||||
| 		// note: in case of duplicates take the last value
 | ||||
| 		foundIncomingMs = true | ||||
| 		if err := appCdc.UnmarshalJSON([]byte(change.Value), &incomingMs); err != nil { | ||||
| 			return false // invalid json value, so just disallow
 | ||||
| 		} | ||||
| 	} | ||||
| 	// only check if there was a proposed change
 | ||||
| 	if foundIncomingMs { | ||||
| 		// Get the current value of the Markets
 | ||||
| 		subspace, found := pk.GetSubspace(pricefeedtypes.ModuleName) | ||||
| 		if !found { | ||||
| 			return false // not using a panic to help avoid begin blocker panics
 | ||||
| 		} | ||||
| 		var currentMs pricefeedtypes.Markets | ||||
| 		subspace.Get(ctx, pricefeedtypes.KeyMarkets, ¤tMs) // panics if something goes wrong
 | ||||
| 
 | ||||
| 		// Check all the incoming changes in the Markets are allowed
 | ||||
| 		marketsChangesAllowed := perm.AllowedMarkets.Allows(currentMs, incomingMs) | ||||
| 		if !marketsChangesAllowed { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| type AllowedCollateralParams []AllowedCollateralParam | ||||
| 
 | ||||
| func (acps AllowedCollateralParams) Allows(current, incoming cdptypes.CollateralParams) bool { | ||||
| 	allAllowed := true | ||||
| 
 | ||||
| 	// do not allow CollateralParams to be added or removed
 | ||||
| 	// this checks both lists are the same size, then below checks each incoming matches a current
 | ||||
| 	if len(incoming) != len(current) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// for each param struct, check it is allowed, and if it is not, check the value has not changed
 | ||||
| 	for _, incomingCP := range incoming { | ||||
| 		// 1) check incoming cp is in list of allowed cps
 | ||||
| 		var foundAllowedCP bool | ||||
| 		var allowedCP AllowedCollateralParam | ||||
| 		for _, p := range acps { | ||||
| 			if p.Denom != incomingCP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundAllowedCP = true | ||||
| 			allowedCP = p | ||||
| 		} | ||||
| 		if !foundAllowedCP { | ||||
| 			// incoming had a CollateralParam that wasn't in the list of allowed ones
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// 2) Check incoming changes are individually allowed
 | ||||
| 		// find existing CollateralParam
 | ||||
| 		var foundCurrentCP bool | ||||
| 		var currentCP cdptypes.CollateralParam | ||||
| 		for _, p := range current { | ||||
| 			if p.Denom != incomingCP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundCurrentCP = true | ||||
| 			currentCP = p | ||||
| 		} | ||||
| 		if !foundCurrentCP { | ||||
| 			return false // not allowed to add param to list
 | ||||
| 		} | ||||
| 		// check changed values are all allowed
 | ||||
| 		allowed := allowedCP.Allows(currentCP, incomingCP) | ||||
| 
 | ||||
| 		allAllowed = allAllowed && allowed | ||||
| 	} | ||||
| 	return allAllowed | ||||
| } | ||||
| 
 | ||||
| type AllowedCollateralParam struct { | ||||
| 	Denom               string `json:"denom" yaml:"denom"` | ||||
| 	LiquidationRatio    bool   `json:"liquidation_ratio" yaml:"liquidation_ratio"` | ||||
| 	DebtLimit           bool   `json:"debt_limit" yaml:"debt_limit"` | ||||
| 	StabilityFee        bool   `json:"stability_fee" yaml:"stability_fee"` | ||||
| 	AuctionSize         bool   `json:"auction_size" yaml:"auction_size"` | ||||
| 	LiquidationPenalty  bool   `json:"liquidation_penalty" yaml:"liquidation_penalty"` | ||||
| 	Prefix              bool   `json:"prefix" yaml:"prefix"` | ||||
| 	SpotMarketID        bool   `json:"spot_market_id" yaml:"spot_market_id"` | ||||
| 	LiquidationMarketID bool   `json:"liquidation_market_id" yaml:"liquidation_market_id"` | ||||
| 	ConversionFactor    bool   `json:"conversion_factor" yaml:"conversion_factor"` | ||||
| } | ||||
| 
 | ||||
| func (acp AllowedCollateralParam) Allows(current, incoming cdptypes.CollateralParam) bool { | ||||
| 	allowed := ((acp.Denom == current.Denom) && (acp.Denom == incoming.Denom)) && // require denoms to be all equal
 | ||||
| 		(current.LiquidationRatio.Equal(incoming.LiquidationRatio) || acp.LiquidationRatio) && | ||||
| 		(current.DebtLimit.IsEqual(incoming.DebtLimit) || acp.DebtLimit) && | ||||
| 		(current.StabilityFee.Equal(incoming.StabilityFee) || acp.StabilityFee) && | ||||
| 		(current.AuctionSize.Equal(incoming.AuctionSize) || acp.AuctionSize) && | ||||
| 		(current.LiquidationPenalty.Equal(incoming.LiquidationPenalty) || acp.LiquidationPenalty) && | ||||
| 		((current.Prefix == incoming.Prefix) || acp.Prefix) && | ||||
| 		((current.SpotMarketID == incoming.SpotMarketID) || acp.SpotMarketID) && | ||||
| 		((current.LiquidationMarketID == incoming.LiquidationMarketID) || acp.LiquidationMarketID) && | ||||
| 		(current.ConversionFactor.Equal(incoming.ConversionFactor) || acp.ConversionFactor) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| type AllowedDebtParam struct { | ||||
| 	Denom            bool `json:"denom" yaml:"denom"` | ||||
| 	ReferenceAsset   bool `json:"reference_asset" yaml:"reference_asset"` | ||||
| 	ConversionFactor bool `json:"conversion_factor" yaml:"conversion_factor"` | ||||
| 	DebtFloor        bool `json:"debt_floor" yaml:"debt_floor"` | ||||
| 	SavingsRate      bool `json:"savings_rate" yaml:"savings_rate"` | ||||
| } | ||||
| 
 | ||||
| func (adp AllowedDebtParam) Allows(current, incoming cdptypes.DebtParam) bool { | ||||
| 	allowed := ((current.Denom == incoming.Denom) || adp.Denom) && | ||||
| 		((current.ReferenceAsset == incoming.ReferenceAsset) || adp.ReferenceAsset) && | ||||
| 		(current.ConversionFactor.Equal(incoming.ConversionFactor) || adp.ConversionFactor) && | ||||
| 		(current.DebtFloor.Equal(incoming.DebtFloor) || adp.DebtFloor) && | ||||
| 		(current.SavingsRate.Equal(incoming.SavingsRate) || adp.SavingsRate) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| type AllowedAssetParams []AllowedAssetParam | ||||
| 
 | ||||
| func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) bool { | ||||
| 	allAllowed := true | ||||
| 
 | ||||
| 	// do not allow AssetParams to be added or removed
 | ||||
| 	// this checks both lists are the same size, then below checks each incoming matches a current
 | ||||
| 	if len(incoming) != len(current) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// for each asset struct, check it is allowed, and if it is not, check the value has not changed
 | ||||
| 	for _, incomingAP := range incoming { | ||||
| 		// 1) check incoming ap is in list of allowed aps
 | ||||
| 		var foundAllowedAP bool | ||||
| 		var allowedAP AllowedAssetParam | ||||
| 		for _, p := range aaps { | ||||
| 			if p.Denom != incomingAP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundAllowedAP = true | ||||
| 			allowedAP = p | ||||
| 		} | ||||
| 		if !foundAllowedAP { | ||||
| 			// incoming had a AssetParam that wasn't in the list of allowed ones
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// 2) Check incoming changes are individually allowed
 | ||||
| 		// find existing SupportedAsset
 | ||||
| 		var foundCurrentAP bool | ||||
| 		var currentAP bep3types.AssetParam | ||||
| 		for _, p := range current { | ||||
| 			if p.Denom != incomingAP.Denom { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundCurrentAP = true | ||||
| 			currentAP = p | ||||
| 		} | ||||
| 		if !foundCurrentAP { | ||||
| 			return false // not allowed to add asset to list
 | ||||
| 		} | ||||
| 		// check changed values are all allowed
 | ||||
| 		allowed := allowedAP.Allows(currentAP, incomingAP) | ||||
| 
 | ||||
| 		allAllowed = allAllowed && allowed | ||||
| 	} | ||||
| 	return allAllowed | ||||
| } | ||||
| 
 | ||||
| type AllowedAssetParam struct { | ||||
| 	Denom  string `json:"denom" yaml:"denom"` | ||||
| 	CoinID bool   `json:"coin_id" yaml:"coin_id"` | ||||
| 	Limit  bool   `json:"limit" yaml:"limit"` | ||||
| 	Active bool   `json:"active" yaml:"active"` | ||||
| } | ||||
| 
 | ||||
| func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool { | ||||
| 
 | ||||
| 	allowed := ((aap.Denom == current.Denom) && (aap.Denom == incoming.Denom)) && // require denoms to be all equal
 | ||||
| 		((current.CoinID == incoming.CoinID) || aap.CoinID) && | ||||
| 		(current.SupplyLimit.Equals(incoming.SupplyLimit) || aap.Limit) && | ||||
| 		((current.Active == incoming.Active) || aap.Active) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| type AllowedMarkets []AllowedMarket | ||||
| 
 | ||||
| func (ams AllowedMarkets) Allows(current, incoming pricefeedtypes.Markets) bool { | ||||
| 	allAllowed := true | ||||
| 
 | ||||
| 	// do not allow Markets to be added or removed
 | ||||
| 	// this checks both lists are the same size, then below checks each incoming matches a current
 | ||||
| 	if len(incoming) != len(current) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// for each market struct, check it is allowed, and if it is not, check the value has not changed
 | ||||
| 	for _, incomingM := range incoming { | ||||
| 		// 1) check incoming market is in list of allowed markets
 | ||||
| 		var foundAllowedM bool | ||||
| 		var allowedM AllowedMarket | ||||
| 		for _, p := range ams { | ||||
| 			if p.MarketID != incomingM.MarketID { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundAllowedM = true | ||||
| 			allowedM = p | ||||
| 		} | ||||
| 		if !foundAllowedM { | ||||
| 			// incoming had a Market that wasn't in the list of allowed ones
 | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// 2) Check incoming changes are individually allowed
 | ||||
| 		// find existing SupportedAsset
 | ||||
| 		var foundCurrentM bool | ||||
| 		var currentM pricefeed.Market | ||||
| 		for _, p := range current { | ||||
| 			if p.MarketID != incomingM.MarketID { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundCurrentM = true | ||||
| 			currentM = p | ||||
| 		} | ||||
| 		if !foundCurrentM { | ||||
| 			return false // not allowed to add market to list
 | ||||
| 		} | ||||
| 		// check changed values are all allowed
 | ||||
| 		allowed := allowedM.Allows(currentM, incomingM) | ||||
| 
 | ||||
| 		allAllowed = allAllowed && allowed | ||||
| 	} | ||||
| 	return allAllowed | ||||
| } | ||||
| 
 | ||||
| type AllowedMarket struct { | ||||
| 	MarketID   string `json:"market_id" yaml:"market_id"` | ||||
| 	BaseAsset  bool   `json:"base_asset" yaml:"base_asset"` | ||||
| 	QuoteAsset bool   `json:"quote_asset" yaml:"quote_asset"` | ||||
| 	Oracles    bool   `json:"oracles" yaml:"oracles"` | ||||
| 	Active     bool   `json:"active" yaml:"active"` | ||||
| } | ||||
| 
 | ||||
| func (am AllowedMarket) Allows(current, incoming pricefeedtypes.Market) bool { | ||||
| 	allowed := ((am.MarketID == current.MarketID) && (am.MarketID == incoming.MarketID)) && // require denoms to be all equal
 | ||||
| 		((current.BaseAsset == incoming.BaseAsset) || am.BaseAsset) && | ||||
| 		((current.QuoteAsset == incoming.QuoteAsset) || am.QuoteAsset) && | ||||
| 		(addressesEqual(current.Oracles, incoming.Oracles) || am.Oracles) && | ||||
| 		((current.Active == incoming.Active) || am.Active) | ||||
| 	return allowed | ||||
| } | ||||
| 
 | ||||
| // addressesEqual check if slices of addresses are equal, the order matters
 | ||||
| func addressesEqual(addrs1, addrs2 []sdk.AccAddress) bool { | ||||
| 	if len(addrs1) != len(addrs2) { | ||||
| 		return false | ||||
| 	} | ||||
| 	areEqual := true | ||||
| 	for i := range addrs1 { | ||||
| 		areEqual = areEqual && addrs1[i].Equals(addrs2[i]) | ||||
| 	} | ||||
| 	return areEqual | ||||
| } | ||||
| 
 | ||||
| // DefaultNextProposalID is the starting poiint for proposal IDs.
 | ||||
| const DefaultNextProposalID uint64 = 1 | ||||
| 
 | ||||
| // GenesisState is state that must be provided at chain genesis.
 | ||||
| type GenesisState struct { | ||||
| 	NextProposalID uint64      `json:"next_proposal_id" yaml:"next_proposal_id"` | ||||
| 	Committees     []Committee `json:"committees" yaml:"committees"` | ||||
| 	Proposals      []Proposal  `json:"proposals" yaml:"proposals"` | ||||
| 	Votes          []Vote      `json:"votes" yaml:"votes"` | ||||
| } | ||||
| 
 | ||||
| // NewGenesisState returns a new genesis state object for the module.
 | ||||
| func NewGenesisState(nextProposalID uint64, committees []Committee, proposals []Proposal, votes []Vote) GenesisState { | ||||
| 	return GenesisState{ | ||||
| 		NextProposalID: nextProposalID, | ||||
| 		Committees:     committees, | ||||
| 		Proposals:      proposals, | ||||
| 		Votes:          votes, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DefaultGenesisState returns the default genesis state for the module.
 | ||||
| func DefaultGenesisState() GenesisState { | ||||
| 	return NewGenesisState( | ||||
| 		DefaultNextProposalID, | ||||
| 		[]Committee{}, | ||||
| 		[]Proposal{}, | ||||
| 		[]Vote{}, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| // Validate performs basic validation of genesis data.
 | ||||
| func (gs GenesisState) Validate() error { | ||||
| 	// validate committees
 | ||||
| 	committeeMap := make(map[uint64]bool, len(gs.Committees)) | ||||
| 	for _, com := range gs.Committees { | ||||
| 		// check there are no duplicate IDs
 | ||||
| 		if _, ok := committeeMap[com.ID]; ok { | ||||
| 			return fmt.Errorf("duplicate committee ID found in genesis state; id: %d", com.ID) | ||||
| 		} | ||||
| 		committeeMap[com.ID] = true | ||||
| 
 | ||||
| 		// validate committee
 | ||||
| 		if err := com.Validate(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// validate proposals
 | ||||
| 	proposalMap := make(map[uint64]bool, len(gs.Proposals)) | ||||
| 	for _, p := range gs.Proposals { | ||||
| 		// check there are no duplicate IDs
 | ||||
| 		if _, ok := proposalMap[p.ID]; ok { | ||||
| 			return fmt.Errorf("duplicate proposal ID found in genesis state; id: %d", p.ID) | ||||
| 		} | ||||
| 		proposalMap[p.ID] = true | ||||
| 
 | ||||
| 		// validate next proposal ID
 | ||||
| 		if p.ID >= gs.NextProposalID { | ||||
| 			return fmt.Errorf("NextProposalID is not greater than all proposal IDs; id: %d", p.ID) | ||||
| 		} | ||||
| 
 | ||||
| 		// check committee exists
 | ||||
| 		if !committeeMap[p.CommitteeID] { | ||||
| 			return fmt.Errorf("proposal refers to non existent committee; proposal: %+v", p) | ||||
| 		} | ||||
| 
 | ||||
| 		// validate pubProposal
 | ||||
| 		if err := p.PubProposal.ValidateBasic(); err != nil { | ||||
| 			return fmt.Errorf("proposal %d invalid: %w", p.ID, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// validate votes
 | ||||
| 	for _, v := range gs.Votes { | ||||
| 		// validate committee
 | ||||
| 		if err := v.Validate(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		// check proposal exists
 | ||||
| 		if !proposalMap[v.ProposalID] { | ||||
| 			return fmt.Errorf("vote refers to non existent proposal; vote: %+v", v) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RegisterCodec registers the necessary types for the module
 | ||||
| func RegisterCodec(cdc *codec.Codec) { | ||||
| 
 | ||||
| 	// Proposals
 | ||||
| 	cdc.RegisterInterface((*PubProposal)(nil), nil) | ||||
| 
 | ||||
| 	// Permissions
 | ||||
| 	cdc.RegisterInterface((*Permission)(nil), nil) | ||||
| 	cdc.RegisterConcrete(GodPermission{}, "kava/GodPermission", nil) | ||||
| 	cdc.RegisterConcrete(SimpleParamChangePermission{}, "kava/SimpleParamChangePermission", nil) | ||||
| 	cdc.RegisterConcrete(TextPermission{}, "kava/TextPermission", nil) | ||||
| 	cdc.RegisterConcrete(SoftwareUpgradePermission{}, "kava/SoftwareUpgradePermission", nil) | ||||
| 	cdc.RegisterConcrete(SubParamChangePermission{}, "kava/SubParamChangePermission", nil) | ||||
| } | ||||
| @ -385,6 +385,26 @@ type AllowedCollateralParam struct { | ||||
| 	ConversionFactor    bool   `json:"conversion_factor" yaml:"conversion_factor"` | ||||
| } | ||||
| 
 | ||||
| // NewAllowedCollateralParam return a new AllowedCollateralParam
 | ||||
| func NewAllowedCollateralParam( | ||||
| 	ctype string, denom, liqRatio, debtLimit, | ||||
| 	stabilityFee, auctionSize, liquidationPenalty, | ||||
| 	prefix, spotMarket, liquidationMarket, conversionFactor bool) AllowedCollateralParam { | ||||
| 	return AllowedCollateralParam{ | ||||
| 		Type:                ctype, | ||||
| 		Denom:               denom, | ||||
| 		LiquidationRatio:    liqRatio, | ||||
| 		DebtLimit:           debtLimit, | ||||
| 		StabilityFee:        stabilityFee, | ||||
| 		AuctionSize:         auctionSize, | ||||
| 		LiquidationPenalty:  liquidationPenalty, | ||||
| 		Prefix:              prefix, | ||||
| 		SpotMarketID:        spotMarket, | ||||
| 		LiquidationMarketID: liquidationMarket, | ||||
| 		ConversionFactor:    conversionFactor, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (acp AllowedCollateralParam) Allows(current, incoming cdptypes.CollateralParam) bool { | ||||
| 	allowed := ((acp.Type == current.Type) && (acp.Type == incoming.Type)) && // require collateral types to be all equal
 | ||||
| 		(current.Denom == incoming.Denom || acp.Denom) && | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Kevin Davis
						Kevin Davis