Skip to content

Commit

Permalink
[Service] Implement AddService with extensive tests (#316)
Browse files Browse the repository at this point in the history
  • Loading branch information
h5law authored Jan 19, 2024
1 parent fd67d44 commit 67e4123
Show file tree
Hide file tree
Showing 21 changed files with 637 additions and 45 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ localnet/*/config/*.json
.vscode
.env
.idea/
.mise.local.toml

# Compiled protos
**/*.pb.go
Expand Down
10 changes: 0 additions & 10 deletions docs/static/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47279,14 +47279,12 @@ paths:
- GRPC
- WEBSOCKET
- JSON_RPC
- REST
default: UNKNOWN_RPC
description: |-
- UNKNOWN_RPC: Undefined RPC type
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
configs:
type: array
items:
Expand Down Expand Up @@ -77807,14 +77805,12 @@ definitions:
- GRPC
- WEBSOCKET
- JSON_RPC
- REST
default: UNKNOWN_RPC
description: |-
- UNKNOWN_RPC: Undefined RPC type
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
configs:
type: array
items:
Expand Down Expand Up @@ -78061,14 +78057,12 @@ definitions:
- GRPC
- WEBSOCKET
- JSON_RPC
- REST
default: UNKNOWN_RPC
description: |-
- UNKNOWN_RPC: Undefined RPC type
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
configs:
type: array
items:
Expand Down Expand Up @@ -78215,7 +78209,6 @@ definitions:
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
title: Enum to define RPC types
pocket.shared.Supplier:
type: object
Expand Down Expand Up @@ -78284,7 +78277,6 @@ definitions:
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
configs:
type: array
items:
Expand Down Expand Up @@ -78343,7 +78335,6 @@ definitions:
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
configs:
type: array
items:
Expand Down Expand Up @@ -78416,7 +78407,6 @@ definitions:
- GRPC: gRPC
- WEBSOCKET: WebSocket
- JSON_RPC: JSON-RPC
- REST: REST
configs:
type: array
items:
Expand Down
2 changes: 2 additions & 0 deletions proto/pocket/service/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package pocket.service;

import "gogoproto/gogo.proto";
import "pocket/service/params.proto";
import "pocket/shared/service.proto";

option go_package = "github.com/pokt-network/poktroll/x/service/types";

// GenesisState defines the service module's genesis state.
message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];
repeated pocket.shared.Service service_list = 2 [(gogoproto.nullable) = false];
}
3 changes: 1 addition & 2 deletions proto/pocket/service/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package pocket.service;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "pocket/service/params.proto";

option go_package = "github.com/pokt-network/poktroll/x/service/types";
Expand All @@ -23,4 +22,4 @@ message QueryParamsRequest {}
message QueryParamsResponse {
// params holds all the parameters of this module.
Params params = 1 [(gogoproto.nullable) = false];
}
}
2 changes: 0 additions & 2 deletions proto/pocket/service/relay.proto
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
syntax = "proto3";
package pocket.service;

import "cosmos_proto/cosmos.proto";
import "pocket/application/application.proto";
import "pocket/session/session.proto";

option go_package = "github.com/pokt-network/poktroll/x/service/types";
Expand Down
15 changes: 14 additions & 1 deletion proto/pocket/service/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@ syntax = "proto3";

package pocket.service;

import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "pocket/shared/service.proto";

option go_package = "github.com/pokt-network/poktroll/x/service/types";

// Msg defines the Msg service.
service Msg {
rpc AddService (MsgAddService) returns (MsgAddServiceResponse);
}

// MsgAddService defines a message for adding a new message to the network.
// Services can be added by any actor in the network making them truly
// permissionless.
// TODO_DOCUMENT(@h5law): This is a key function in making services
// permissionless, document it's usage and design - in docusaurus covering how
// the entire process works.
message MsgAddService {
string address = 1;
option (cosmos.msg.v1.signer) = "address"; // https://docs.cosmos.network/main/build/building-modules/messages-and-queries
string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the service supplier using cosmos' ScalarDescriptor
shared.Service service = 2 [(gogoproto.nullable) = false]; // The Service for which the supplier is adding to the network
}

message MsgAddServiceResponse {}
Expand Down
19 changes: 15 additions & 4 deletions x/service/client/cli/tx_add_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,27 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/pokt-network/poktroll/x/service/types"
"github.com/spf13/cobra"

"github.com/pokt-network/poktroll/x/service/types"
)

var _ = strconv.Itoa(0)

func CmdAddService() *cobra.Command {
cmd := &cobra.Command{
Use: "add-service",
Short: "Broadcast message add-service",
Args: cobra.ExactArgs(0),
Use: "add-service <service_id> <service_name>",
Short: "Add a new service to the network",
Long: `Add a new service to the network that will be available for applications,
gateways and suppliers to use. The service id MUST be unique - or the command
will fail, however the name you use to describe it does not have to be unique.
Example:
$ poktrolld tx service add-service "srv1" "service_one" --keyring-backend test --from $(SUPPLIER) --node $(POCKET_NODE) --home=$(POKTROLLD_HOME)`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
serviceIdStr := args[0]
serviceNameStr := args[1]

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
Expand All @@ -26,6 +35,8 @@ func CmdAddService() *cobra.Command {

msg := types.NewMsgAddService(
clientCtx.GetFromAddress().String(),
serviceIdStr,
serviceNameStr,
)
if err := msg.ValidateBasic(); err != nil {
return err
Expand Down
143 changes: 143 additions & 0 deletions x/service/client/cli/tx_add_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package cli_test

import (
"fmt"
"testing"

sdkerrors "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/status"

"github.com/pokt-network/poktroll/testutil/network"
"github.com/pokt-network/poktroll/x/service/client/cli"
"github.com/pokt-network/poktroll/x/service/types"
sharedtypes "github.com/pokt-network/poktroll/x/shared/types"
)

func TestCLI_AddService(t *testing.T) {
net := network.New(t, network.DefaultConfig())
val := net.Validators[0]
ctx := val.ClientCtx

// Create a keyring and add an account for the address adding the service
kr := ctx.Keyring
accounts := testutil.CreateKeyringAccounts(t, kr, 1)
account := accounts[0]

// Update the context with the new keyring
ctx = ctx.WithKeyring(kr)

// Common args used for all requests
commonArgs := []string{
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf(
"--%s=%s",
flags.FlagFees,
sdk.NewCoins(sdk.NewCoin(net.Config.BondDenom, sdkmath.NewInt(10))).String(),
),
}

// Initialize the Supplier account by sending it some funds from the
// validator account that is part of genesis
network.InitAccountWithSequence(t, net, account.Address, 1)

// Wait for a new block to be committed
require.NoError(t, net.WaitForNextBlock())

// Prepare two valid services
svc1 := sharedtypes.Service{
Id: "srv1",
Name: "service name",
}
svc2 := sharedtypes.Service{
Id: "srv2",
Name: "service name 2",
}
// Add srv2 to the network
args := []string{
svc2.Id,
svc2.Name,
fmt.Sprintf("--%s=%s", flags.FlagFrom, account.Address.String()),
}
args = append(args, commonArgs...)
_, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdAddService(), args)
require.NoError(t, err)

tests := []struct {
desc string
supplierAddress string
service sharedtypes.Service
err *sdkerrors.Error
}{
{
desc: "valid - add new service",
supplierAddress: account.Address.String(),
service: svc1,
},
{
desc: "invalid - missing service id",
supplierAddress: account.Address.String(),
service: sharedtypes.Service{Name: "service name"}, // ID intentionally omitted
err: types.ErrServiceMissingID,
},
{
desc: "invalid - missing service name",
supplierAddress: account.Address.String(),
service: sharedtypes.Service{Id: "srv1"}, // Name intentionally omitted
err: types.ErrServiceMissingName,
},
{
desc: "invalid - invalid supplier address",
supplierAddress: "invalid address",
service: svc1,
err: types.ErrServiceInvalidAddress,
},
{
desc: "invalid - service already staked",
supplierAddress: account.Address.String(),
service: svc2,
err: types.ErrServiceAlreadyExists,
},
}

// Run the tests
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
// Wait for a new block to be committed
require.NoError(t, net.WaitForNextBlock())

// Prepare the arguments for the CLI command
args := []string{
tt.service.Id,
tt.service.Name,
fmt.Sprintf("--%s=%s", flags.FlagFrom, tt.supplierAddress),
}
args = append(args, commonArgs...)

// Execute the command
addServiceOutput, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdAddService(), args)

// Validate the error if one is expected
if tt.err != nil {
stat, ok := status.FromError(tt.err)
require.True(t, ok)
require.Contains(t, stat.Message(), tt.err.Error())
return
}
require.NoError(t, err)

// Check the response
var resp sdk.TxResponse
require.NoError(t, net.Config.Codec.UnmarshalJSON(addServiceOutput.Bytes(), &resp))
require.NotNil(t, resp)
require.NotNil(t, resp.TxHash)
require.Equal(t, uint32(0), resp.Code)
})
}
}
5 changes: 5 additions & 0 deletions x/service/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (

// InitGenesis initializes the module's state from a provided genesis state.
func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) {
// Set all the initial services
for _, service := range genState.ServiceList {
k.SetService(ctx, service)
}
// this line is used by starport scaffolding # genesis/module/init
k.SetParams(ctx, genState.Params)
}
Expand All @@ -18,6 +22,7 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
genesis := types.DefaultGenesis()
genesis.Params = k.GetParams(ctx)

genesis.ServiceList = k.GetAllServices(ctx)
// this line is used by starport scaffolding # genesis/module/export

return genesis
Expand Down
29 changes: 26 additions & 3 deletions x/service/keeper/msg_server_add_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,39 @@ package keeper

import (
"context"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/pokt-network/poktroll/x/service/types"
)

func (k msgServer) AddService(goCtx context.Context, msg *types.MsgAddService) (*types.MsgAddServiceResponse, error) {
// AddService handles MsgAddService and adds a service to the network storing
// it in the service keeper's store using the provided ID from the message.
// TODO(#337): Add a governance parameter defining the cost to add a service
// and enforce it then send the fee to the service module, if the signer has
// enough funds otherwise fail.
func (k msgServer) AddService(
goCtx context.Context,
msg *types.MsgAddService,
) (*types.MsgAddServiceResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

// TODO: Handling the message
_ = ctx
logger := k.Logger(ctx).With("method", "AddService")
logger.Info(fmt.Sprintf("About to add a new service with msg: %v", msg))

if err := msg.ValidateBasic(); err != nil {
logger.Error(fmt.Sprintf("Adding service failed basic validation: %v", err))
return nil, err
}

if _, found := k.GetService(ctx, msg.Service.Id); found {
logger.Error(fmt.Sprintf("Service already exists: %v", msg.Service))
return nil, types.ErrServiceAlreadyExists
}

logger.Info(fmt.Sprintf("Adding service: %v", msg.Service))
k.SetService(ctx, msg.Service)

return &types.MsgAddServiceResponse{}, nil
}
Loading

0 comments on commit 67e4123

Please sign in to comment.