Skip to content

Commit

Permalink
feat: add address to service (#10)
Browse files Browse the repository at this point in the history
## Description

This PR adds a new `address` field to a service, representing the
account that is associated to such service and will be used to keep all
the tokens that users restake with that service.

I've also improved the tests to make sure they use the `NewService`
method instead of the `types.Service` structure. This will make them
easier to maintain and update in the future.

<!-- Add a description of the changes that this PR introduces and the
files that
are the most critical to review. -->

---

### Author Checklist

*All items are required. Please add a note to the item if the item is
not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type
prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json)
in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR
Targeting](https://github.com/desmos-labs/desmos/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building
modules](https://docs.cosmos.network/v0.44/building-modules/intro.html)
- [x] included the necessary unit and integration
[tests](https://github.com/desmos-labs/desmos/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go
code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [x] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable
and please add
your handle next to the items reviewed if you only reviewed selected
items.*

I have...

- [ ] confirmed the correct [type
prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json)
in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
RiccardoM authored Jun 14, 2024
1 parent 414db96 commit 6b3ea76
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 150 deletions.
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,7 @@ func NewMilkyWayApp(
app.ServicesKeeper = serviceskeeper.NewKeeper(
app.appCodec,
keys[servicestypes.StoreKey],
app.AccountKeeper,
communityPoolKeeper,
authorityAddr,
)
Expand Down
5 changes: 5 additions & 0 deletions proto/milkyway/services/v1/models.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,9 @@ message Service {

// PictureURL is the URL of the picture of the service
string pictureURL = 7;

// Address is the address of the account associated with the service.
// This will be used in order to store all the tokens that are delegated to
// this service by various users.
string address = 8 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}
9 changes: 9 additions & 0 deletions x/services/keeper/alias_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ package keeper

import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/milkyway-labs/milkyway/x/services/types"
)

// createAccountIfNotExists creates an account if it does not exist
func (k *Keeper) createAccountIfNotExists(ctx sdk.Context, address sdk.AccAddress) {
if !k.accountKeeper.HasAccount(ctx, address) {
defer telemetry.IncrCounter(1, "new", "account")
k.accountKeeper.SetAccount(ctx, k.accountKeeper.NewAccountWithAddress(ctx, address))
}
}

// IterateServices iterates over the services in the store and performs a callback function
func (k *Keeper) IterateServices(ctx sdk.Context, cb func(service types.Service) (stop bool)) {
store := ctx.KVStore(k.storeKey)
Expand Down
1 change: 1 addition & 0 deletions x/services/keeper/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.k = keeper.NewKeeper(
suite.cdc,
suite.storeKey,
suite.ak,
keepers.NewCommunityPoolKeeper(suite.bk, authtypes.FeeCollectorName),
authorityAddr,
)
Expand Down
14 changes: 8 additions & 6 deletions x/services/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@ type Keeper struct {
cdc codec.BinaryCodec
hooks types.ServicesHooks

poolKeeper types.CommunityPoolKeeper
accountKeeper types.AccountKeeper
poolKeeper types.CommunityPoolKeeper

// authority represents the address capable of executing a MsgUpdateParams message.
// Typically, this should be the x/gov module account.
authority string
}

// NewKeeper creates a new keeper
func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, poolKeeper types.CommunityPoolKeeper, authority string) *Keeper {
func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, accountKeeper types.AccountKeeper, poolKeeper types.CommunityPoolKeeper, authority string) *Keeper {
return &Keeper{
storeKey: storeKey,
cdc: cdc,
poolKeeper: poolKeeper,
authority: authority,
storeKey: storeKey,
cdc: cdc,
accountKeeper: accountKeeper,
poolKeeper: poolKeeper,
authority: authority,
}
}

Expand Down
6 changes: 5 additions & 1 deletion x/services/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ func (suite *KeeperTestSuite) TestMsgServer_CreateService() {
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
), stored)

// Make sure the next service account has been incremented
// Make sure the service account has been created properly
hasAccount := suite.ak.HasAccount(ctx, types.GetServiceAddress(1))
suite.Require().True(hasAccount)

// Make sure the next service id has been incremented
nextServiceID, err := suite.k.GetNextServiceID(ctx)
suite.Require().NoError(err)
suite.Require().Equal(uint32(2), nextServiceID)
Expand Down
8 changes: 8 additions & 0 deletions x/services/keeper/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/milkyway-labs/milkyway/x/services/types"
)
Expand Down Expand Up @@ -49,6 +50,13 @@ func (k *Keeper) CreateService(ctx sdk.Context, service types.Service) error {
}
}

// Create the service account
serviceAddress, err := sdk.AccAddressFromBech32(service.Address)
if err != nil {
return errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid service address: %s", service.Address)
}
k.createAccountIfNotExists(ctx, serviceAddress)

// Store the service
k.SaveService(ctx, service)

Expand Down
4 changes: 4 additions & 0 deletions x/services/keeper/services_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ func (suite *KeeperTestSuite) TestKeeper_CreateService() {
poolBalance := suite.bk.GetBalance(ctx, authtypes.NewModuleAddress(authtypes.FeeCollectorName), "uatom")
suite.Require().Equal(sdk.NewCoin("uatom", sdkmath.NewInt(100_000_000)), poolBalance)

// Make sure the service account has been created
hasAccount := suite.ak.HasAccount(ctx, types.GetServiceAddress(1))
suite.Require().True(hasAccount)

// Make sure the service has been created
service, found := suite.k.GetService(ctx, 1)
suite.Require().True(found)
Expand Down
6 changes: 6 additions & 0 deletions x/services/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

type AccountKeeper interface {
NewAccountWithAddress(ctx context.Context, addr sdk.AccAddress) sdk.AccountI
HasAccount(ctx context.Context, addr sdk.AccAddress) bool
SetAccount(ctx context.Context, acc sdk.AccountI)
}

type CommunityPoolKeeper interface {
FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error
}
91 changes: 55 additions & 36 deletions x/services/types/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,25 @@ func TestValidateGenesis(t *testing.T) {
genesis: &types.GenesisState{
NextServiceID: 1,
Services: []types.Service{
{
ID: 1,
Status: types.SERVICE_STATUS_CREATED,
Admin: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4",
Name: "MilkyWay",
},
{
ID: 1,
Status: types.SERVICE_STATUS_CREATED,
Admin: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4",
Name: "MilkyWay",
}},
types.NewService(
1,
types.SERVICE_STATUS_ACTIVE,
"MilkyWay",
"MilkyWay is an AVS of a restaking platform",
"https://milkyway.com",
"https://milkyway.com/logo.png",
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
),
types.NewService(
1,
types.SERVICE_STATUS_ACTIVE,
"MilkyWay",
"MilkyWay is an AVS of a restaking platform",
"https://milkyway.com",
"https://milkyway.com/logo.png",
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
),
},
Params: types.DefaultParams(),
},
shouldErr: true,
Expand All @@ -51,18 +58,24 @@ func TestValidateGenesis(t *testing.T) {
genesis: &types.GenesisState{
NextServiceID: 1,
Services: []types.Service{
{
ID: 1,
Status: types.SERVICE_STATUS_CREATED,
Admin: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4",
Name: "MilkyWay",
},
{
ID: 2,
Status: types.SERVICE_STATUS_UNSPECIFIED,
Admin: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4",
Name: "IBC Relaying",
},
types.NewService(
1,
types.SERVICE_STATUS_ACTIVE,
"MilkyWay",
"MilkyWay is an AVS of a restaking platform",
"https://milkyway.com",
"https://milkyway.com/logo.png",
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
),
types.NewService(
2,
types.SERVICE_STATUS_UNSPECIFIED,
"Tucana",
"",
"",
"",
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
),
},
Params: types.DefaultParams(),
},
Expand All @@ -89,18 +102,24 @@ func TestValidateGenesis(t *testing.T) {
genesis: &types.GenesisState{
NextServiceID: 1,
Services: []types.Service{
{
ID: 1,
Status: types.SERVICE_STATUS_CREATED,
Admin: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4",
Name: "MilkyWay",
},
{
ID: 2,
Status: types.SERVICE_STATUS_ACTIVE,
Admin: "cosmos167x6ehhple8gwz5ezy9x0464jltvdpzl6qfdt4",
Name: "IBC Relaying",
},
types.NewService(
1,
types.SERVICE_STATUS_ACTIVE,
"MilkyWay",
"MilkyWay is an AVS of a restaking platform",
"https://milkyway.com",
"https://milkyway.com/logo.png",
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
),
types.NewService(
2,
types.SERVICE_STATUS_ACTIVE,
"Tucana",
"",
"",
"",
"cosmos13t6y2nnugtshwuy0zkrq287a95lyy8vzleaxmd",
),
},
Params: types.NewParams(
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(10))),
Expand Down
12 changes: 12 additions & 0 deletions x/services/types/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import (
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

// GetServiceAddress generates a service address from its id
func GetServiceAddress(serviceID uint32) sdk.AccAddress {
return authtypes.NewModuleAddress(fmt.Sprintf("service-%d", serviceID))
}

// ParseServiceID parses a string into a uint32
func ParseServiceID(value string) (uint32, error) {
id, err := strconv.ParseUint(value, 10, 32)
Expand Down Expand Up @@ -37,6 +43,7 @@ func NewService(
Website: website,
PictureURL: pictureURL,
Admin: admin,
Address: GetServiceAddress(id).String(),
}
}

Expand All @@ -59,6 +66,11 @@ func (a *Service) Validate() error {
return fmt.Errorf("invalid admin address")
}

_, err = sdk.AccAddressFromBech32(a.Address)
if err != nil {
return fmt.Errorf("invalid service address")
}

return nil
}

Expand Down
Loading

0 comments on commit 6b3ea76

Please sign in to comment.