diff --git a/aclmapping/dependency_generator.go b/aclmapping/dependency_generator.go index beec9783a8..fe4be3a313 100644 --- a/aclmapping/dependency_generator.go +++ b/aclmapping/dependency_generator.go @@ -4,6 +4,7 @@ import ( aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper" aclbankmapping "github.com/sei-protocol/sei-chain/aclmapping/bank" acldexmapping "github.com/sei-protocol/sei-chain/aclmapping/dex" + aclstakingmapping "github.com/sei-protocol/sei-chain/aclmapping/staking" aclwasmmapping "github.com/sei-protocol/sei-chain/aclmapping/wasm" ) @@ -18,6 +19,7 @@ func (customDepGen CustomDependencyGenerator) GetCustomDependencyGenerators() ac dependencyGeneratorMap.Merge(acldexmapping.GetDexDependencyGenerators()) dependencyGeneratorMap.Merge(aclbankmapping.GetBankDepedencyGenerator()) + dependencyGeneratorMap.Merge(aclstakingmapping.GetStakingDependencyGenerator()) wasmDependencyGenerators := aclwasmmapping.NewWasmDependencyGenerator() dependencyGeneratorMap.Merge(wasmDependencyGenerators.GetWasmDependencyGenerators()) diff --git a/aclmapping/staking/mappings.go b/aclmapping/staking/mappings.go new file mode 100644 index 0000000000..18e8d68223 --- /dev/null +++ b/aclmapping/staking/mappings.go @@ -0,0 +1,260 @@ +package aclstakingmapping + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkacltypes "github.com/cosmos/cosmos-sdk/types/accesscontrol" + aclkeeper "github.com/cosmos/cosmos-sdk/x/accesscontrol/keeper" + acltypes "github.com/cosmos/cosmos-sdk/x/accesscontrol/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + utils "github.com/sei-protocol/sei-chain/aclmapping/utils" +) + +var ErrorInvalidMsgType = fmt.Errorf("invalid message received for staking module") + +func GetStakingDependencyGenerator() aclkeeper.DependencyGeneratorMap { + dependencyGeneratorMap := make(aclkeeper.DependencyGeneratorMap) + + delegateKey := acltypes.GenerateMessageKey(&stakingtypes.MsgDelegate{}) + undelegateKey := acltypes.GenerateMessageKey(&stakingtypes.MsgUndelegate{}) + beginRedelegateKey := acltypes.GenerateMessageKey(&stakingtypes.MsgBeginRedelegate{}) + dependencyGeneratorMap[delegateKey] = MsgDelegateDependencyGenerator + dependencyGeneratorMap[undelegateKey] = MsgUndelegateDependencyGenerator + dependencyGeneratorMap[beginRedelegateKey] = MsgBeginRedelegateDependencyGenerator + + return dependencyGeneratorMap +} + +func MsgDelegateDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sdk.Msg) ([]sdkacltypes.AccessOperation, error) { + msgDelegate, ok := msg.(*stakingtypes.MsgDelegate) + if !ok { + return []sdkacltypes.AccessOperation{}, ErrorInvalidMsgType + } + + accessOperations := []sdkacltypes.AccessOperation{ + // Checks if the delegator exists + // Checks if there is a delegation object that already exists for (delegatorAddr, validatorAddr) + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgDelegate.DelegatorAddress+msgDelegate.ValidatorAddress), + }, + // Store new delegator for (delegator, validator) + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgDelegate.DelegatorAddress+msgDelegate.ValidatorAddress), + }, + + // delegate coins from account validator account + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgDelegate.DelegatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgDelegate.DelegatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgDelegate.ValidatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgDelegate.ValidatorAddress), + }, + + // Checks if the validators exchange rate is valid + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgDelegate.ValidatorAddress), + }, + // Update validator shares and power index + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgDelegate.ValidatorAddress), + }, + + // Last Operation should always be a commit + { + ResourceType: sdkacltypes.ResourceType_ANY, + AccessType: sdkacltypes.AccessType_COMMIT, + IdentifierTemplate: utils.DefaultIDTemplate, + }, + } + + return accessOperations, nil +} + +func MsgUndelegateDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sdk.Msg) ([]sdkacltypes.AccessOperation, error) { + msgUndelegate, ok := msg.(*stakingtypes.MsgUndelegate) + if !ok { + return []sdkacltypes.AccessOperation{}, ErrorInvalidMsgType + } + + accessOperations := []sdkacltypes.AccessOperation{ + // Treat Delegations and Undelegations to have the same ACL since they are highly coupled, no point in finer granularization + + // Get delegation/redelegations and error checking + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgUndelegate.DelegatorAddress+msgUndelegate.ValidatorAddress), + }, + // Update/delete delegation and update redelegation + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgUndelegate.DelegatorAddress+msgUndelegate.ValidatorAddress), + }, + + // Update the delegator and validator account balances + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgUndelegate.DelegatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgUndelegate.DelegatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgUndelegate.ValidatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgUndelegate.ValidatorAddress), + }, + + // Checks if the validators exchange rate is valid + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgUndelegate.ValidatorAddress), + }, + // Update validator shares and power index + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgUndelegate.ValidatorAddress), + }, + + // Last Operation should always be a commit + { + ResourceType: sdkacltypes.ResourceType_ANY, + AccessType: sdkacltypes.AccessType_COMMIT, + IdentifierTemplate: utils.DefaultIDTemplate, + }, + } + + return accessOperations, nil +} + +func MsgBeginRedelegateDependencyGenerator(keeper aclkeeper.Keeper, ctx sdk.Context, msg sdk.Msg) ([]sdkacltypes.AccessOperation, error) { + msgBeingRedelegate, ok := msg.(*stakingtypes.MsgBeginRedelegate) + if !ok { + return []sdkacltypes.AccessOperation{}, ErrorInvalidMsgType + } + + accessOperations := []sdkacltypes.AccessOperation{ + // Treat Delegations and Redelegations to have the same ACL since they are highly coupled, no point in finer granularization + + // Get src delegation to verify it has sufficient funds to undelegate + // Get dest delegation to see if it already exists + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.DelegatorAddress+msgBeingRedelegate.ValidatorSrcAddress), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.DelegatorAddress+msgBeingRedelegate.ValidatorDstAddress), + }, + // Update/delete src and destination delegation after tokens have been unbonded + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.DelegatorAddress+msgBeingRedelegate.ValidatorSrcAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.DelegatorAddress+msgBeingRedelegate.ValidatorDstAddress), + }, + + // Update the delegator, src validator and dest validator account balances + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgBeingRedelegate.DelegatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgBeingRedelegate.DelegatorAddress), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgBeingRedelegate.ValidatorSrcAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgBeingRedelegate.ValidatorSrcAddress), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgBeingRedelegate.ValidatorDstAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.BANK, msgBeingRedelegate.ValidatorDstAddress), + }, + + // Update validators staking shares and power index + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.ValidatorSrcAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.ValidatorSrcAddress), + }, + { + AccessType: sdkacltypes.AccessType_READ, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.ValidatorDstAddress), + }, + { + AccessType: sdkacltypes.AccessType_WRITE, + ResourceType: sdkacltypes.ResourceType_KV, + IdentifierTemplate: utils.GetIdentifierTemplatePerModule(utils.STAKING, msgBeingRedelegate.ValidatorDstAddress), + }, + + // Last Operation should always be a commit + { + ResourceType: sdkacltypes.ResourceType_ANY, + AccessType: sdkacltypes.AccessType_COMMIT, + IdentifierTemplate: utils.DefaultIDTemplate, + }, + } + + return accessOperations, nil +} diff --git a/aclmapping/utils/identifier_templates.go b/aclmapping/utils/identifier_templates.go index ac28dad9ff..3098e33742 100644 --- a/aclmapping/utils/identifier_templates.go +++ b/aclmapping/utils/identifier_templates.go @@ -11,6 +11,7 @@ const ( ACCOUNT = "acc" BANK = "bank" AUTH = "auth" + STAKING = "staking" DefaultIDTemplate = "*" ) diff --git a/go.mod b/go.mod index eeb77f3c14..72b2d3097b 100644 --- a/go.mod +++ b/go.mod @@ -131,7 +131,7 @@ require ( ) replace ( - github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.1.176 + github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.1.192 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.1.59 diff --git a/go.sum b/go.sum index fd2cf03824..ff4bb4d040 100644 --- a/go.sum +++ b/go.sum @@ -1098,8 +1098,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/securego/gosec/v2 v2.11.0/go.mod h1:SX8bptShuG8reGC0XS09+a4H2BoWSJi+fscA+Pulbpo= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= -github.com/sei-protocol/sei-cosmos v0.1.176 h1:6UgAcryRx6C+UlouHDjxuY7T7hj3nck91QoeppyPdLc= -github.com/sei-protocol/sei-cosmos v0.1.176/go.mod h1:8ccWQxpBkWbpvBos/T4QO9K9gQxFs0duTqKRnagKo+0= +github.com/sei-protocol/sei-cosmos v0.1.192 h1:qrrzr2qHrj7zMDmU7KizOjlXm2uLROUtz8ogEYSxfSE= +github.com/sei-protocol/sei-cosmos v0.1.192/go.mod h1:8ccWQxpBkWbpvBos/T4QO9K9gQxFs0duTqKRnagKo+0= github.com/sei-protocol/sei-tendermint v0.1.59 h1:POGL60PumMQHF4EzAHzvkGfDnodQJLHpl65LuiwSO/Y= github.com/sei-protocol/sei-tendermint v0.1.59/go.mod h1:Olwbjyagrpoxj5DAUhHxMTWDVEfQ3FYdpypaJ3+6Hs8= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= diff --git a/loadtest/config.json b/loadtest/config.json index d8673d83b0..3ed19e2914 100644 --- a/loadtest/config.json +++ b/loadtest/config.json @@ -15,7 +15,10 @@ }, "message_type_distribution": { "limit_order_percentage": "0.2", - "market_order_percentage": "0.8" + "market_order_percentage": "0.8", + "delegate_percentage": "0.5", + "undelegate_percentage": "0.25", + "begin_redelegate_percentage": "0.25" }, "message_type": "basic", "contract_distribution": [ diff --git a/loadtest/main.go b/loadtest/main.go index 1759091eca..0ed24f0d41 100644 --- a/loadtest/main.go +++ b/loadtest/main.go @@ -22,6 +22,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" typestx "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/x/auth/tx" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/sei-protocol/sei-chain/app" "github.com/sei-protocol/sei-chain/utils" dextypes "github.com/sei-protocol/sei-chain/x/dex/types" @@ -59,12 +60,20 @@ func (d *NumericDistribution) Sample() sdk.Dec { return d.Min.Add(d.Max.Sub(d.Min).QuoInt64(d.NumDistinct).Mul(steps)) } +// @TODO: Eventually, want to convert so we can send a distribution of messages +// across various modules, currently only supports single module loadtesting +// For a specific module, the sum of the msg types must equal 1 type MsgTypeDistribution struct { + // dex msg distribution LimitOrderPct sdk.Dec `json:"limit_order_percentage"` MarketOrderPct sdk.Dec `json:"market_order_percentage"` + // staking msg distribution + DelegatePct sdk.Dec `json:"delegate_percentage"` + UndelegatePct sdk.Dec `json:"undelegate_percentage"` + BeginRedelegatePct sdk.Dec `json:"begin_redelegate_percentage"` } -func (d *MsgTypeDistribution) Sample() string { +func (d *MsgTypeDistribution) SampleDexMsgs() string { if !d.LimitOrderPct.Add(d.MarketOrderPct).Equal(sdk.OneDec()) { panic("Distribution percentages must add up to 1") } @@ -75,6 +84,19 @@ func (d *MsgTypeDistribution) Sample() string { return "market" } +func (d *MsgTypeDistribution) SampleStakingMsgs() string { + if !d.DelegatePct.Add(d.UndelegatePct).Add(d.BeginRedelegatePct).Equal(sdk.OneDec()) { + panic("Distribution percentages must add up to 1") + } + randNum := sdk.MustNewDecFromStr(fmt.Sprintf("%f", rand.Float64())) + if randNum.LT(d.DelegatePct) { + return "delegate" + } else if randNum.LT(d.DelegatePct.Add(d.UndelegatePct)) { + return "undelegate" + } + return "begin_redelegate" +} + type ContractDistributions []ContractDistribution func (d *ContractDistributions) Sample() string { @@ -146,6 +168,7 @@ func run(config Config) { if config.OrdersPerBlock < batchSize { panic("Must have more orders per block than batch size") } + qv := GetValidators() numberOfAccounts := config.OrdersPerBlock / batchSize * 2 // * 2 because we need two sets of accounts activeAccounts := []int{} @@ -172,7 +195,7 @@ func run(config Config) { for _, account := range activeAccounts { key := GetKey(uint64(account)) - msg := generateMessage(config, key, batchSize) + msg := generateMessage(config, key, batchSize, qv.Validators) txBuilder := TestConfig.TxConfig.NewTxBuilder() _ = txBuilder.SetMsgs(msg) seqDelta := uint64(i / 2) @@ -213,7 +236,7 @@ func run(config Config) { fmt.Printf("%s - Finished\n", time.Now().Format("2006-01-02T15:04:05")) } -func generateMessage(config Config, key cryptotypes.PrivKey, batchSize uint64) sdk.Msg { +func generateMessage(config Config, key cryptotypes.PrivKey, batchSize uint64, validators []Validator) sdk.Msg { var msg sdk.Msg switch config.MessageType { case "basic": @@ -225,8 +248,35 @@ func generateMessage(config Config, key cryptotypes.PrivKey, batchSize uint64) s Amount: sdk.NewInt(1), }), } + case "staking": + msgType := config.MsgTypeDistr.SampleStakingMsgs() + + switch msgType { + case "delegate": + msg = &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(key.PubKey().Address()).String(), + ValidatorAddress: validators[rand.Intn(len(validators))].OpperatorAddr, + Amount: sdk.Coin{Denom: "usei", Amount: sdk.NewInt(5)}, + } + case "undelegate": + msg = &stakingtypes.MsgUndelegate{ + DelegatorAddress: sdk.AccAddress(key.PubKey().Address()).String(), + ValidatorAddress: validators[rand.Intn(len(validators))].OpperatorAddr, + Amount: sdk.Coin{Denom: "usei", Amount: sdk.NewInt(1)}, + } + case "begin_redelegate": + msg = &stakingtypes.MsgBeginRedelegate{ + DelegatorAddress: sdk.AccAddress(key.PubKey().Address()).String(), + ValidatorSrcAddress: validators[rand.Intn(len(validators))].OpperatorAddr, + ValidatorDstAddress: validators[rand.Intn(len(validators))].OpperatorAddr, + Amount: sdk.Coin{Denom: "usei", Amount: sdk.NewInt(1)}, + } + default: + panic("Unknown message type") + } + case "dex": - msgType := config.MsgTypeDistr.Sample() + msgType := config.MsgTypeDistr.SampleDexMsgs() orderPlacements := []*dextypes.Order{} var orderType dextypes.OrderType if msgType == "limit" { diff --git a/loadtest/sign.go b/loadtest/sign.go index 0ebaccd94e..aeaffa5235 100644 --- a/loadtest/sign.go +++ b/loadtest/sign.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "os/exec" "path/filepath" "time" @@ -26,6 +27,29 @@ type AccountInfo struct { Mnemonic string `json:"mnemonic"` } +type Validator struct { + OpperatorAddr string `json:"operator_address"` +} + +type QueryValidators struct { + Validators []Validator `json:"validators"` +} + +func GetValidators() QueryValidators { + seid_query, err := exec.Command("seid", "query", "staking", "validators", "--output", "json").Output() + + if err != nil { + panic(err) + } + + qv := QueryValidators{} + if err := json.Unmarshal(seid_query, &qv); err != nil { + panic(err) + } + + return qv +} + func GetKey(accountIdx uint64) cryptotypes.PrivKey { userHomeDir, _ := os.UserHomeDir() accountKeyFilePath := filepath.Join(userHomeDir, "test_accounts", fmt.Sprintf("ta%d.json", accountIdx))