diff --git a/docs/general/GCP Feature Deployments.md b/docs/general/GCP Feature Deployments.md new file mode 100644 index 0000000000..2f0bf5eba0 --- /dev/null +++ b/docs/general/GCP Feature Deployments.md @@ -0,0 +1,70 @@ +# Sifnode Feature Deployments on GCP + +## Push + +Each feature branch will deploy a single Sifnode node to a testnet GKE cluster on Google Cloud, with each push. + +### Requirements + +If you wish to interact with your branch's namespace, you will need to install the following: + +* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl) +* [gcloud SDK](https://cloud.google.com/sdk/docs/install) + +### Namespaces + +Each feature branch will have its own [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces). + +The namespace is derived from the branch name, however all slashes (`/`) will be converted to hyphens (`-`) and it will be converted to lowercase. + +For example: `feature/Some-Branch` will have a namespace of `feature-some-branch`. + +### kubectl + +In order to interact with the cluster and your namespace, you'll need to ensure that the dependencies above have been installed. + +Because the node endpoint is not exposed as part of the Github actions workflow, there are a few commands you'll need to run to obtain this. You'll require this when wanting to interact with your newly deployed node. + +1. Configure gcloud: + +``` +gcloud init +``` + +*Follow the on-screen instructions to authenticate and configure the SDK.* + +2. Create a new `kubeconfig`: + +``` +gcloud container clusters get-credentials --zone --project +``` + +3. Get your node's IP/s: + +``` +kubectl get svc -n +``` + +where `` is your namespace name, as above. + +The command will display those running services in the provided namespace: + +``` +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +sifnoded LoadBalancer 10.0.0.1 35.232.1.1 1317:30336/TCP 20h +sifnoded LoadBalancer 10.0.0.2 35.232.1.2 26656-26657:30840-30841/TCP 20h +``` + +You can then use the `EXTERNAL-IP` address to interact with the services, on the standard RPC/P2P (26656/26657) and rest (1317) ports. + +#### Troubleshooting + +If you're having trouble with your namespace, you can always query all available namespaces on the cluster: + +``` +kubectl get svc --all-namespaces +``` + +## Merging into Develop + +When your feature branch is merged, the namespace will be destroyed. diff --git a/gcp/gcp.go b/gcp/gcp.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/x/clp/keeper/keeper.go b/x/clp/keeper/keeper.go index e617f44b99..135fcd8353 100644 --- a/x/clp/keeper/keeper.go +++ b/x/clp/keeper/keeper.go @@ -25,6 +25,7 @@ type Keeper struct { mintKeeper mintkeeper.Keeper getMarginKeeper func() margintypes.Keeper paramstore paramtypes.Subspace + PmtpKeeper types.PmtpKeeper } // NewKeeper creates a clp keeper @@ -45,6 +46,11 @@ func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, bankkeeper types.BankKee getMarginKeeper: getMarginKeeper, paramstore: ps, } + + + keeper.PmtpKeeper = keeper + + return keeper } diff --git a/x/clp/keeper/proposal_handler.go b/x/clp/keeper/proposal_handler.go new file mode 100644 index 0000000000..2a5f719f74 --- /dev/null +++ b/x/clp/keeper/proposal_handler.go @@ -0,0 +1,69 @@ + + +package keeper + +import ( + "fmt" + + "github.com/Sifchain/sifnode/x/clp/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + + +type ClpProposalHandler struct { + keeper Keeper +} + +func NewClpProposalHandler(keeper Keeper) ClpProposalHandler { + return ClpProposalHandler{ + keeper: keeper, + } +} + +func (h ClpProposalHandler) HandleProposal(ctx sdk.Context, proposal govtypes.Proposal) error { + switch proposal := proposal.(type) { + case *types.UpdatePmtpParamsProposal: + return HandleUpdatePmtpParamsProposal(ctx, h.keeper, proposal) + default: + return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized clp proposal type: %T", proposal) + } +} + + +) + +// HandleUpdatePmtpParamsProposal is a handler for updating pmtp params +func HandleUpdatePmtpParamsProposal(ctx sdk.Context, k Keeper, proposal *types.UpdatePmtpParamsProposal) error { + proposer, err := sdk.AccAddressFromBech32(proposal.RunAs) + if err != nil { + return err + } + if !k.adminKeeper.IsAdminAccount(ctx, types.AdminType_CLP, proposer) { + return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "proposer is not a recognized admin account") + } + + pmtpParams := proposal.PmtpParams + if pmtpParams == nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "pmtp params cannot be nil") + } + + k.SetPmtpParams(ctx, pmtpParams) + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUpdatePmtpParams, + sdk.NewAttribute(types.AttributeKeyPmtpPeriodStartBlock, fmt.Sprintf("%d", pmtpParams.PmtpPeriodStartBlock)), + sdk.NewAttribute(types.AttributeKeyPmtpPeriodEndBlock, fmt.Sprintf("%d", pmtpParams.PmtpPeriodEndBlock)), + sdk.NewAttribute(types.AttributeKeyPmtpBlockRate, pmtpParams.PmtpBlockRate.String()), + sdk.NewAttribute(types.AttributeKeyPmtpCurrentRunningRate, pmtpParams.PmtpCurrentRunningRate.String()), + sdk.NewAttribute(types.AttributeKeyPmtpInterimAsset, pmtpParams.PmtpInterimAsset.String()), + sdk.NewAttribute(types.AttributeKeyPmtpCurrentDepth, pmtpParams.PmtpCurrentDepth.String()), + sdk.NewAttribute(types.AttributeKeyPmtpFarmingFeeRate, pmtpParams.PmtpFarmingFeeRate.String()), + ), + ) + + return nil +} + diff --git a/x/clp/module.go b/x/clp/module.go index 7af7c65cfa..259a9294e2 100644 --- a/x/clp/module.go +++ b/x/clp/module.go @@ -19,6 +19,7 @@ import ( "github.com/Sifchain/sifnode/x/clp/client/rest" "github.com/Sifchain/sifnode/x/clp/keeper" "github.com/Sifchain/sifnode/x/clp/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) // Type check to ensure the interface is properly implemented @@ -43,8 +44,11 @@ func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { //nolin // RegisterInterfaces registers the module's interface types func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { types.RegisterInterfaces(registry) -} + +func (b AppModuleBasic) RegisterProposalType(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&types.UpdatePmtpParamsProposal{}, "sifnode/UpdatePmtpParamsProposal", nil) +} // DefaultGenesis returns default genesis state as raw bytes for the clp // module. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { @@ -92,14 +96,16 @@ type AppModule struct { keeper keeper.Keeper bankKeeper types.BankKeeper + govRouter govtypes.Router } // NewAppModule creates a new AppModule object -func NewAppModule(k keeper.Keeper, bankKeeper types.BankKeeper) AppModule { +func NewAppModule(k keeper.Keeper, bankKeeper types.BankKeeper, govRouter govtypes.Router) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: k, bankKeeper: bankKeeper, + govRouter: govRouter, } } @@ -158,6 +164,8 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json. var genesisState types.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) + am.govRouter.AddRoute(types.RouterKey, keeper.NewClpProposalHandler(am.keeper)) + return InitGenesis(ctx, am.keeper, genesisState) } diff --git a/x/clp/types/codec.go b/x/clp/types/codec.go index 6fa3e3fd03..1223a84502 100644 --- a/x/clp/types/codec.go +++ b/x/clp/types/codec.go @@ -18,7 +18,9 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { //nolint cdc.RegisterConcrete(&MsgDecommissionPool{}, "clp/DecommissionPool", nil) cdc.RegisterConcrete(&MsgUnlockLiquidityRequest{}, "clp/UnlockLiquidity", nil) cdc.RegisterConcrete(&MsgAddLiquidityToRewardsBucketRequest{}, "clp/AddLiquidityToRewardsBucket", nil) -} + + cdc.RegisterConcrete(&UpdatePmtpParamsProposal{}, "clp/UpdatePmtpParamsProposal", nil) + var ( amino = codec.NewLegacyAmino() diff --git a/x/clp/types/events.go b/x/clp/types/events.go index 4a6650e54d..4f010cf9a3 100644 --- a/x/clp/types/events.go +++ b/x/clp/types/events.go @@ -7,6 +7,12 @@ const ( EventTypeDecommissionPool = "decommission_pool" EventTypeAddNewPmtpPolicy = "pmtp_new_policy" EventTypeEndPmtpPolicy = "pmtp_end_policy" + + + EventTypeUpdatePmtpParams = "pmtp_update_params" + + + EventTypeCreateLiquidityProvider = "created_new_liquidity_provider" EventTypeAddLiquidity = "added_liquidity" EventTypeRemoveLiquidity = "removed_liquidity" @@ -26,11 +32,16 @@ const ( AttributeKeyThreshold = "min_threshold" AttributeKeySwapAmount = "swap_amount" AttributeKeyLiquidityFee = "liquidity_fee" - AttributeKeyPriceImpact = "price_impact" - AttributeKeyInPool = "in_pool" - AttributeKeyOutPool = "out_pool" - AttributePmtpBlockRate = "pmtp_block_rate" - AttributePmtpCurrentRunningRate = "pmtp_current_running_rate" + + + + AttributeKeyPmtpPeriodStartBlock = "pmtp_period_start_block" + AttributeKeyPmtpPeriodEndBlock = "pmtp_period_end_block" + AttributeKeyPmtpBlockRate = "pmtp_block_rate" + AttributeKeyPmtpCurrentRunningRate = "pmtp_current_running_rate" + AttributeKeyPmtpInterimAsset = "pmtp_interim_asset" + AttributeKeyPmtpCurrentDepth = "pmtp_current_depth" + AttributeKeyPmtpFarmingFeeRate = "pmtp_farming_fee_rate" AttributeKeyPool = "pool" AttributeKeyHeight = "height" AttributeKeyLiquidityProvider = "liquidity_provider" diff --git a/x/clp/types/expected_keepers.go b/x/clp/types/expected_keepers.go index dff7911f86..a4d51d86c8 100644 --- a/x/clp/types/expected_keepers.go +++ b/x/clp/types/expected_keepers.go @@ -41,3 +41,10 @@ type TokenRegistryKeeper interface { type AdminKeeper interface { IsAdminAccount(ctx sdk.Context, moduleName admintypes.AdminType, adminAccount sdk.AccAddress) bool } + + +type PmtpKeeper interface { + SetPmtpParams(ctx sdk.Context, params *PmtpParams) + GetPmtpParams(ctx sdk.Context) *PmtpParams +} + diff --git a/x/clp/types/proposals.go b/x/clp/types/proposals.go new file mode 100644 index 0000000000..d7fbd38b9c --- /dev/null +++ b/x/clp/types/proposals.go @@ -0,0 +1,73 @@ + +package types + +import ( + "fmt" + "strings" + + yaml "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +const ( + // ProposalTypeUpdatePmtpParams defines the type for a UpdatePmtpParamsProposal + ProposalTypeUpdatePmtpParams = "UpdatePmtpParams" +) + +// Assert UpdatePmtpParamsProposal implements govtypes.Content at compile time +var _ govtypes.Content = &UpdatePmtpParamsProposal{} + +func init() { + govtypes.RegisterProposalType(ProposalTypeUpdatePmtpParams) + govtypes.RegisterProposalTypeCodec(&UpdatePmtpParamsProposal{}, "sifnode/UpdatePmtpParamsProposal") +} + +// UpdatePmtpParamsProposal defines a proposal to update pmtp parameters. +type UpdatePmtpParamsProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + RunAs string `json:"run_as" yaml:"run_as"` + PmtpParams *PmtpParams `json:"pmtp_params" yaml:"pmtp_params"` +} + +// NewUpdatePmtpParamsProposal creates a new UpdatePmtpParamsProposal. +func NewUpdatePmtpParamsProposal(title, description, runAs string, pmtpParams *PmtpParams) *UpdatePmtpParamsProposal { + return &UpdatePmtpParamsProposal{ + Title: title, + Description: description, + RunAs: runAs, + PmtpParams: pmtpParams, + } +} + +// GetTitle returns the title of a community pool spend proposal. +func (p *UpdatePmtpParamsProposal) GetTitle() string { return p.Title } + +// GetDescription returns the description of a community pool spend proposal. +func (p *UpdatePmtpParamsProposal) GetDescription() string { return p.Description } + +// ProposalRoute returns the routing key of a community pool spend proposal. +func (p *UpdatePmtpParamsProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a community pool spend proposal. +func (p *UpdatePmtpParamsProposal) ProposalType() string { return ProposalTypeUpdatePmtpParams } + +// ValidateBasic runs basic governance proposal checks and checks that the proposal details are valid. +func (p *UpdatePmtpParamsProposal) ValidateBasic() error { + err := govtypes.ValidateAbstract(p) + if err != nil { + return err + } + if p.PmtpParams == nil { + return ErrInvalidPmtpParams + } + return p.PmtpParams.ValidateBasic() +} + +// String implements the Stringer interface. +func (p *UpdatePmtpParamsProposal) String() string { + out, _ := yaml.Marshal(p) + return string(out) +}