Skip to content

Commit

Permalink
[Tokenomics] Use service module directly to find a service (#726)
Browse files Browse the repository at this point in the history
The Tokenomics module uses the service module directly to find a service, in order to get the compute units per relay for the service.

Issue: https://github.com/pokt-network/poktroll/blob/04329baa174c52873023105d7551b9e2d745ceb8/x/tokenomics/keeper/settle_session_accounting.go#L233-L236

![image](https://github.com/user-attachments/assets/9a8efb6b-3515-4344-8fd5-733c8623162c)
  • Loading branch information
adshmh authored Aug 7, 2024
1 parent 04329ba commit 6ae0523
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 62 deletions.
1 change: 1 addition & 0 deletions testutil/integration/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ func NewCompleteIntegrationApp(t *testing.T) *App {
proofKeeper,
sharedKeeper,
sessionKeeper,
serviceKeeper,
)
tokenomicsModule := tokenomics.NewAppModule(
cdc,
Expand Down
37 changes: 29 additions & 8 deletions testutil/keeper/tokenomics.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type TokenomicsModuleKeepers struct {
tokenomicstypes.ProofKeeper
tokenomicstypes.SharedKeeper
tokenomicstypes.SessionKeeper
tokenomicstypes.ServiceKeeper

Codec *codec.ProtoCodec
}
Expand Down Expand Up @@ -113,14 +114,6 @@ func TokenomicsKeeperWithActorAddrs(
Stake: &sdk.Coin{Denom: "upokt", Amount: math.NewInt(100000)},
}

if service != nil {
application.ServiceConfigs = []*sharedtypes.ApplicationServiceConfig{
{
Service: service,
},
}
}

// Prepare the test supplier.
supplier := sharedtypes.Supplier{
Address: sample.AccAddress(),
Expand Down Expand Up @@ -176,6 +169,23 @@ func TokenomicsKeeperWithActorAddrs(
// Mock the session keeper
mockSessionKeeper := mocks.NewMockSessionKeeper(ctrl)

// Mock the service keeper
mockServiceKeeper := mocks.NewMockServiceKeeper(ctrl)

if service != nil {
// Get service if the ID matches.
mockServiceKeeper.EXPECT().
GetService(gomock.Any(), gomock.Eq(service.Id)).
Return(*service, true).
AnyTimes()
}

// Get zero-value service if the id does not match.
mockServiceKeeper.EXPECT().
GetService(gomock.Any(), gomock.Any()).
Return(sharedtypes.Service{}, false).
AnyTimes()

k := tokenomicskeeper.NewKeeper(
cdc,
runtime.NewKVStoreService(storeKey),
Expand All @@ -187,6 +197,7 @@ func TokenomicsKeeperWithActorAddrs(
mockProofKeeper,
mockSharedKeeper,
mockSessionKeeper,
mockServiceKeeper,
)

sdkCtx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger())
Expand Down Expand Up @@ -217,6 +228,7 @@ func NewTokenomicsModuleKeepers(
suppliertypes.StoreKey,
prooftypes.StoreKey,
sharedtypes.StoreKey,
servicetypes.StoreKey,
)

// Construct a multistore & mount store keys for each keeper that will interact with the state store.
Expand Down Expand Up @@ -367,6 +379,7 @@ func NewTokenomicsModuleKeepers(
proofKeeper,
sharedKeeper,
sessionKeeper,
serviceKeeper,
)

require.NoError(t, tokenomicsKeeper.SetParams(ctx, tokenomicstypes.DefaultParams()))
Expand All @@ -380,6 +393,7 @@ func NewTokenomicsModuleKeepers(
ProofKeeper: &proofKeeper,
SharedKeeper: &sharedKeeper,
SessionKeeper: &sessionKeeper,
ServiceKeeper: &serviceKeeper,

Codec: cdc,
}
Expand All @@ -391,3 +405,10 @@ func NewTokenomicsModuleKeepers(

return keepers, ctx
}

func WithService(service sharedtypes.Service) TokenomicsModuleKeepersOpt {
return func(ctx context.Context, keepers *TokenomicsModuleKeepers) context.Context {
keepers.SetService(ctx, service)
return ctx
}
}
3 changes: 3 additions & 0 deletions x/tokenomics/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Keeper struct {
proofKeeper types.ProofKeeper
sharedKeeper types.SharedKeeper
sessionKeeper types.SessionKeeper
serviceKeeper types.ServiceKeeper

sharedQuerier client.SharedQueryClient
}
Expand All @@ -45,6 +46,7 @@ func NewKeeper(
proofKeeper types.ProofKeeper,
sharedKeeper types.SharedKeeper,
sessionKeeper types.SessionKeeper,
serviceKeeper types.ServiceKeeper,
) Keeper {
if _, err := sdk.AccAddressFromBech32(authority); err != nil {
panic(fmt.Sprintf("invalid authority address: %s", authority))
Expand All @@ -64,6 +66,7 @@ func NewKeeper(
proofKeeper: proofKeeper,
sharedKeeper: sharedKeeper,
sessionKeeper: sessionKeeper,
serviceKeeper: serviceKeeper,

sharedQuerier: sharedQuerier,
}
Expand Down
9 changes: 5 additions & 4 deletions x/tokenomics/keeper/keeper_settle_pending_claims_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,29 +83,30 @@ func (s *TestSuite) SetupTest() {
preGeneratedAccts,
).String()

service := &sharedtypes.Service{Id: testServiceId}
service := sharedtypes.Service{Id: testServiceId}
s.keepers.SetService(s.ctx, service)

supplierStake := types.NewCoin("upokt", math.NewInt(1000000))
supplier := sharedtypes.Supplier{
Address: supplierAddr,
Stake: &supplierStake,
Services: []*sharedtypes.SupplierServiceConfig{{Service: service}},
Services: []*sharedtypes.SupplierServiceConfig{{Service: &service}},
}
s.keepers.SetSupplier(s.ctx, supplier)

appStake := types.NewCoin("upokt", math.NewInt(1000000))
app := apptypes.Application{
Address: appAddr,
Stake: &appStake,
ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{{Service: service}},
ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{{Service: &service}},
}
s.keepers.SetApplication(s.ctx, app)

// Get the session for the application/supplier pair which is expected
// to be claimed and for which a valid proof would be accepted.
sessionReq := &sessiontypes.QueryGetSessionRequest{
ApplicationAddress: appAddr,
Service: service,
Service: &service,
BlockHeight: 1,
}
sessionRes, err := s.keepers.GetSession(s.sdkCtx, sessionReq)
Expand Down
29 changes: 8 additions & 21 deletions x/tokenomics/keeper/settle_session_accounting.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ func (k Keeper) SettleSessionAccounting(
return tokenomicstypes.ErrTokenomicsApplicationNotFound
}

computeUnitsPerRelay, err := k.getComputeUnitsPerRelayFromApplication(application, sessionHeader.Service.Id)
computeUnitsPerRelay, err := k.getComputeUnitsPerRelayForService(ctx, sessionHeader.Service.Id)
if err != nil {
logger.Warn(fmt.Sprintf("service with address %q not found", sessionHeader.Service.Id))
return err
}

Expand Down Expand Up @@ -229,26 +230,12 @@ func relayCountToCoin(numRelays, computeUnitsPerRelay uint64, computeUnitsToToke
return cosmostypes.NewCoin(volatile.DenomuPOKT, upoktAmount), nil
}

// getComputeUnitsPerRelayFromApplication retrieves the ComputeUnitsPerRelay for a given service from the application's service configs
// TODO_REFACTOR: Rename this to getComputeUnitsPerRelayForService(serviceId) after
// adding a dependency on the service module to the tokenomics module so it is cleaner
// and more idiomatic, leveraging the `GetService` function directly. Would require updating logs below.
func (k Keeper) getComputeUnitsPerRelayFromApplication(application apptypes.Application, serviceID string) (cupr uint64, err error) {
logger := k.Logger().With("method", "getComputeUnitsPerRelayFromApplication")

serviceConfigs := application.ServiceConfigs
if len(serviceConfigs) == 0 {
logger.Warn(fmt.Sprintf("application with address %q has no service configs", application.Address))
return 0, tokenomicstypes.ErrTokenomicsApplicationNoServiceConfigs
}

for _, sc := range serviceConfigs {
service := sc.GetService()
if service.Id == serviceID {
return service.ComputeUnitsPerRelay, nil
}
// getComputeUnitsPerRelayForService retrieves the ComputeUnitsPerRelay for a given service using the service module.
func (k Keeper) getComputeUnitsPerRelayForService(ctx context.Context, serviceID string) (cupr uint64, err error) {
service, found := k.serviceKeeper.GetService(ctx, serviceID)
if !found {
return 0, tokenomicstypes.ErrTokenomicsServiceNotFound
}

logger.Warn(fmt.Sprintf("service with ID %q not found in application with address %q", serviceID, application.Address))
return 0, tokenomicstypes.ErrTokenomicsApplicationNoServiceConfigs
return service.ComputeUnitsPerRelay, nil
}
71 changes: 44 additions & 27 deletions x/tokenomics/keeper/settle_session_accounting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestSettleSessionAccounting_HandleAppGoingIntoDebt(t *testing.T) {
Address: sample.AccAddress(),
Stake: &appStake,
ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{
&sharedtypes.ApplicationServiceConfig{
{
Service: service,
},
},
Expand Down Expand Up @@ -81,7 +81,15 @@ func TestSettleSessionAccounting_HandleAppGoingIntoDebt(t *testing.T) {
}

func TestSettleSessionAccounting_ValidAccounting(t *testing.T) {
keepers, ctx := testkeeper.NewTokenomicsModuleKeepers(t, nil)
// Create a service that can be registered in the application and used in the claims
service := sharedtypes.Service{
Id: "svc1",
Name: "svcName1",
ComputeUnitsPerRelay: 1,
OwnerAddress: sample.AccAddress(),
}

keepers, ctx := testkeeper.NewTokenomicsModuleKeepers(t, nil, testkeeper.WithService(service))
appModuleAddress := authtypes.NewModuleAddress(apptypes.ModuleName).String()
supplierModuleAddress := authtypes.NewModuleAddress(suppliertypes.ModuleName).String()

Expand All @@ -91,13 +99,6 @@ func TestSettleSessionAccounting_ValidAccounting(t *testing.T) {
})
require.NoError(t, err)

// Create a service that can be registered in the application and used in the claims
service := &sharedtypes.Service{
Id: "svc1",
Name: "svcName1",
ComputeUnitsPerRelay: 1,
OwnerAddress: sample.AccAddress(),
}
// Add a new application
appStake := cosmostypes.NewCoin("upokt", math.NewInt(1000000))
// NB: Ensure a non-zero app stake end balance to assert against.
Expand All @@ -106,11 +107,6 @@ func TestSettleSessionAccounting_ValidAccounting(t *testing.T) {
app := apptypes.Application{
Address: sample.AccAddress(),
Stake: &appStake,
ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{
&sharedtypes.ApplicationServiceConfig{
Service: service,
},
},
}
keepers.SetApplication(ctx, app)

Expand Down Expand Up @@ -187,7 +183,15 @@ func TestSettleSessionAccounting_ValidAccounting(t *testing.T) {
}

func TestSettleSessionAccounting_AppStakeTooLow(t *testing.T) {
keepers, ctx := testkeeper.NewTokenomicsModuleKeepers(t, nil)
// Create a service that can be registered in the application and used in the claims
service := sharedtypes.Service{
Id: "svc1",
Name: "svcName1",
ComputeUnitsPerRelay: 1,
OwnerAddress: sample.AccAddress(),
}

keepers, ctx := testkeeper.NewTokenomicsModuleKeepers(t, nil, testkeeper.WithService(service))
appModuleAddress := authtypes.NewModuleAddress(apptypes.ModuleName).String()
supplierModuleAddress := authtypes.NewModuleAddress(suppliertypes.ModuleName).String()

Expand All @@ -197,25 +201,13 @@ func TestSettleSessionAccounting_AppStakeTooLow(t *testing.T) {
})
require.NoError(t, err)

// Create a service that can be registered in the application and used in the claims
service := &sharedtypes.Service{
Id: "svc1",
Name: "svcName1",
ComputeUnitsPerRelay: 1,
OwnerAddress: sample.AccAddress(),
}
// Add a new application
appStake := cosmostypes.NewCoin("upokt", math.NewInt(40000))
expectedAppEndStakeZeroAmount := cosmostypes.NewCoin("upokt", math.NewInt(0))
expectedAppBurn := appStake.AddAmount(math.NewInt(2000))
app := apptypes.Application{
Address: sample.AccAddress(),
Stake: &appStake,
ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{
&sharedtypes.ApplicationServiceConfig{
Service: service,
},
},
}
keepers.SetApplication(ctx, app)

Expand Down Expand Up @@ -339,6 +331,31 @@ func TestSettleSessionAccounting_AppNotFound(t *testing.T) {
require.ErrorIs(t, err, tokenomicstypes.ErrTokenomicsApplicationNotFound)
}

func TestSettleSessionAccounting_ServiceNotFound(t *testing.T) {

keeper, ctx, appAddr, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t, nil)

claim := prooftypes.Claim{
SupplierAddress: supplierAddr,
SessionHeader: &sessiontypes.SessionHeader{
ApplicationAddress: appAddr,
Service: &sharedtypes.Service{
Id: "non_existent_svc",
},
SessionId: "session_id",
SessionStartBlockHeight: 1,
SessionEndBlockHeight: testsession.GetSessionEndHeightWithDefaultParams(1),
},
RootHash: testproof.SmstRootWithSum(42),
}

// Execute test function
err := keeper.SettleSessionAccounting(ctx, &claim)

require.Error(t, err)
require.ErrorIs(t, err, tokenomicstypes.ErrTokenomicsServiceNotFound)
}

func TestSettleSessionAccounting_InvalidRoot(t *testing.T) {

// Create a service that can be registered in the application and used in the claims
Expand Down
2 changes: 2 additions & 0 deletions x/tokenomics/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ type ModuleInputs struct {
ProofKeeper types.ProofKeeper
SharedKeeper types.SharedKeeper
SessionKeeper types.SessionKeeper
ServiceKeeper types.ServiceKeeper
}

type ModuleOutputs struct {
Expand All @@ -209,6 +210,7 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
in.ProofKeeper,
in.SharedKeeper,
in.SessionKeeper,
in.ServiceKeeper,
)
m := NewAppModule(
in.Cdc,
Expand Down
2 changes: 1 addition & 1 deletion x/tokenomics/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ var (
ErrTokenomicsSupplierAddressInvalid = sdkerrors.Register(ModuleName, 1107, "the supplier address in the claim is not a valid bech32 address")
ErrTokenomicsApplicationNotFound = sdkerrors.Register(ModuleName, 1108, "application not found")
ErrTokenomicsApplicationModuleBurn = sdkerrors.Register(ModuleName, 1109, "failed to burn uPOKT from application module account")
ErrTokenomicsApplicationNoServiceConfigs = sdkerrors.Register(ModuleName, 1111, "application has no service configs")
ErrTokenomicsApplicationAddressInvalid = sdkerrors.Register(ModuleName, 1112, "the application address in the claim is not a valid bech32 address")
ErrTokenomicsParamsInvalid = sdkerrors.Register(ModuleName, 1113, "provided params are invalid")
ErrTokenomicsRootHashInvalid = sdkerrors.Register(ModuleName, 1114, "the root hash in the claim is invalid")
Expand All @@ -27,4 +26,5 @@ var (
ErrTokenomicsDuplicateIndex = sdkerrors.Register(ModuleName, 1119, "cannot have a duplicate index")
ErrTokenomicsMissingRelayMiningDifficulty = sdkerrors.Register(ModuleName, 1120, "missing relay mining difficulty")
ErrTokenomicsApplicationOverserviced = sdkerrors.Register(ModuleName, 1121, "application was overserviced")
ErrTokenomicsServiceNotFound = sdkerrors.Register(ModuleName, 1122, "service not found")
)
9 changes: 8 additions & 1 deletion x/tokenomics/types/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:generate mockgen -destination ../../../testutil/tokenomics/mocks/expected_keepers_mock.go -package mocks . AccountKeeper,BankKeeper,ApplicationKeeper,ProofKeeper,SharedKeeper,SessionKeeper
//go:generate mockgen -destination ../../../testutil/tokenomics/mocks/expected_keepers_mock.go -package mocks . AccountKeeper,BankKeeper,ApplicationKeeper,ProofKeeper,SharedKeeper,SessionKeeper,ServiceKeeper

package types

Expand Down Expand Up @@ -78,3 +78,10 @@ type SupplierKeeper interface {
GetSupplier(ctx context.Context, supplierAddr string) (supplier sharedtypes.Supplier, found bool)
SetSupplier(ctx context.Context, supplier sharedtypes.Supplier)
}

type ServiceKeeper interface {
GetService(ctx context.Context, serviceID string) (sharedtypes.Service, bool)
// NOTE: SetService is not used by the tokenomics keeper.
// It is only defined here to make it easier to add services to the service module in the tests.
SetService(ctx context.Context, service sharedtypes.Service)
}

0 comments on commit 6ae0523

Please sign in to comment.