From 1268edb63ce5ffa9d5297ec0a2d4227b4f432362 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 24 Jul 2024 16:44:06 -0400 Subject: [PATCH] [Utility] Feat: Chain-specific compute unit to tokens multipliers (#552) - Added a `compute_units_per_relay` param to new services - Updated `SettleSessionAccounting()` to compute units per relay along with the general compute units to tokens multipliers - Added new tests. - Updated existing tests - Added comments where necessary --- api/poktroll/shared/service.pulsar.go | 166 ++++++++++++------ config.yml | 2 + e2e/tests/0_settlement.feature | 1 + e2e/tests/init_test.go | 15 ++ proto/poktroll/shared/service.proto | 3 + testutil/keeper/tokenomics.go | 13 +- testutil/proof/fixture_generators.go | 14 +- .../keeper/msg_server_add_service_test.go | 21 ++- x/service/module/tx_add_service.go | 23 ++- x/service/module/tx_add_service_test.go | 31 +++- x/service/types/errors.go | 25 +-- x/service/types/message_add_service.go | 33 +++- x/service/types/message_add_service_test.go | 45 ++++- x/shared/types/service.go | 10 ++ x/shared/types/service.pb.go | 97 +++++++--- .../keeper_settle_pending_claims_test.go | 15 +- x/tokenomics/keeper/msg_server_test.go | 2 +- x/tokenomics/keeper/msg_update_params_test.go | 2 +- x/tokenomics/keeper/query_params_test.go | 4 +- .../keeper/settle_session_accounting.go | 45 +++-- .../keeper/settle_session_accounting_test.go | 89 ++++++++-- x/tokenomics/module/genesis_test.go | 2 +- x/tokenomics/types/errors.go | 1 + 23 files changed, 501 insertions(+), 158 deletions(-) create mode 100644 x/shared/types/service.go diff --git a/api/poktroll/shared/service.pulsar.go b/api/poktroll/shared/service.pulsar.go index 52de53638..363e474cc 100644 --- a/api/poktroll/shared/service.pulsar.go +++ b/api/poktroll/shared/service.pulsar.go @@ -13,9 +13,10 @@ import ( ) var ( - md_Service protoreflect.MessageDescriptor - fd_Service_id protoreflect.FieldDescriptor - fd_Service_name protoreflect.FieldDescriptor + md_Service protoreflect.MessageDescriptor + fd_Service_id protoreflect.FieldDescriptor + fd_Service_name protoreflect.FieldDescriptor + fd_Service_compute_units_per_relay protoreflect.FieldDescriptor ) func init() { @@ -23,6 +24,7 @@ func init() { md_Service = File_poktroll_shared_service_proto.Messages().ByName("Service") fd_Service_id = md_Service.Fields().ByName("id") fd_Service_name = md_Service.Fields().ByName("name") + fd_Service_compute_units_per_relay = md_Service.Fields().ByName("compute_units_per_relay") } var _ protoreflect.Message = (*fastReflection_Service)(nil) @@ -102,6 +104,12 @@ func (x *fastReflection_Service) Range(f func(protoreflect.FieldDescriptor, prot return } } + if x.ComputeUnitsPerRelay != uint64(0) { + value := protoreflect.ValueOfUint64(x.ComputeUnitsPerRelay) + if !f(fd_Service_compute_units_per_relay, value) { + return + } + } } // Has reports whether a field is populated. @@ -121,6 +129,8 @@ func (x *fastReflection_Service) Has(fd protoreflect.FieldDescriptor) bool { return x.Id != "" case "poktroll.shared.Service.name": return x.Name != "" + case "poktroll.shared.Service.compute_units_per_relay": + return x.ComputeUnitsPerRelay != uint64(0) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: poktroll.shared.Service")) @@ -141,6 +151,8 @@ func (x *fastReflection_Service) Clear(fd protoreflect.FieldDescriptor) { x.Id = "" case "poktroll.shared.Service.name": x.Name = "" + case "poktroll.shared.Service.compute_units_per_relay": + x.ComputeUnitsPerRelay = uint64(0) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: poktroll.shared.Service")) @@ -163,6 +175,9 @@ func (x *fastReflection_Service) Get(descriptor protoreflect.FieldDescriptor) pr case "poktroll.shared.Service.name": value := x.Name return protoreflect.ValueOfString(value) + case "poktroll.shared.Service.compute_units_per_relay": + value := x.ComputeUnitsPerRelay + return protoreflect.ValueOfUint64(value) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: poktroll.shared.Service")) @@ -187,6 +202,8 @@ func (x *fastReflection_Service) Set(fd protoreflect.FieldDescriptor, value prot x.Id = value.Interface().(string) case "poktroll.shared.Service.name": x.Name = value.Interface().(string) + case "poktroll.shared.Service.compute_units_per_relay": + x.ComputeUnitsPerRelay = value.Uint() default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: poktroll.shared.Service")) @@ -211,6 +228,8 @@ func (x *fastReflection_Service) Mutable(fd protoreflect.FieldDescriptor) protor panic(fmt.Errorf("field id of message poktroll.shared.Service is not mutable")) case "poktroll.shared.Service.name": panic(fmt.Errorf("field name of message poktroll.shared.Service is not mutable")) + case "poktroll.shared.Service.compute_units_per_relay": + panic(fmt.Errorf("field compute_units_per_relay of message poktroll.shared.Service is not mutable")) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: poktroll.shared.Service")) @@ -228,6 +247,8 @@ func (x *fastReflection_Service) NewField(fd protoreflect.FieldDescriptor) proto return protoreflect.ValueOfString("") case "poktroll.shared.Service.name": return protoreflect.ValueOfString("") + case "poktroll.shared.Service.compute_units_per_relay": + return protoreflect.ValueOfUint64(uint64(0)) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: poktroll.shared.Service")) @@ -305,6 +326,9 @@ func (x *fastReflection_Service) ProtoMethods() *protoiface.Methods { if l > 0 { n += 1 + l + runtime.Sov(uint64(l)) } + if x.ComputeUnitsPerRelay != 0 { + n += 1 + runtime.Sov(uint64(x.ComputeUnitsPerRelay)) + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -334,6 +358,11 @@ func (x *fastReflection_Service) ProtoMethods() *protoiface.Methods { i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if x.ComputeUnitsPerRelay != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.ComputeUnitsPerRelay)) + i-- + dAtA[i] = 0x18 + } if len(x.Name) > 0 { i -= len(x.Name) copy(dAtA[i:], x.Name) @@ -461,6 +490,25 @@ func (x *fastReflection_Service) ProtoMethods() *protoiface.Methods { } x.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ComputeUnitsPerRelay", wireType) + } + x.ComputeUnitsPerRelay = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.ComputeUnitsPerRelay |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -2708,6 +2756,8 @@ type Service struct { Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Unique identifier for the service // TODO_BETA: Name is currently unused but acts as a reminder that an optional onchain representation of the service is necessary Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // (Optional) Semantic human readable name for the service + // Used alongside the global 'compute_units_to_tokens_multipler' to calculate the cost of a relay for this service + ComputeUnitsPerRelay uint64 `protobuf:"varint,3,opt,name=compute_units_per_relay,json=computeUnitsPerRelay,proto3" json:"compute_units_per_relay,omitempty"` // Compute units required per relay for this service } func (x *Service) Reset() { @@ -2744,6 +2794,13 @@ func (x *Service) GetName() string { return "" } +func (x *Service) GetComputeUnitsPerRelay() uint64 { + if x != nil { + return x.ComputeUnitsPerRelay + } + return 0 +} + // ApplicationServiceConfig holds the service configuration the application stakes for type ApplicationServiceConfig struct { state protoimpl.MessageState @@ -2926,57 +2983,60 @@ var file_poktroll_shared_service_proto_rawDesc = []byte{ 0x0a, 0x1d, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x22, 0x2d, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x22, 0x64, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x4e, 0x0a, 0x18, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, - 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, - 0x8c, 0x01, 0x0a, 0x15, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x6f, 0x6b, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3f, 0x0a, - 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, - 0x65, 0x64, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x92, - 0x01, 0x0a, 0x10, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x33, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, - 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x52, 0x50, 0x43, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x07, 0x72, 0x70, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, - 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x73, 0x22, 0x56, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, - 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2a, 0x4b, 0x0a, 0x07, 0x52, - 0x50, 0x43, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, - 0x4e, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, - 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x45, 0x42, 0x53, 0x4f, 0x43, 0x4b, 0x45, 0x54, 0x10, 0x02, - 0x12, 0x0c, 0x0a, 0x08, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x03, 0x12, 0x08, - 0x0a, 0x04, 0x52, 0x45, 0x53, 0x54, 0x10, 0x04, 0x2a, 0x30, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x42, 0xa2, 0x01, 0x0a, 0x13, 0x63, - 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, - 0x65, 0x64, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x20, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x73, 0x68, - 0x61, 0x72, 0x65, 0x64, 0xa2, 0x02, 0x03, 0x50, 0x53, 0x58, 0xaa, 0x02, 0x0f, 0x50, 0x6f, 0x6b, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0xca, 0x02, 0x0f, 0x50, - 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0xe2, 0x02, - 0x1b, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x10, 0x50, - 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x3a, 0x3a, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x35, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x50, 0x65, + 0x72, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x4e, 0x0a, 0x18, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, + 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x15, 0x53, 0x75, 0x70, 0x70, 0x6c, + 0x69, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x32, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x64, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x69, + 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x10, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x69, + 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x33, 0x0a, 0x08, + 0x72, 0x70, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, + 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, + 0x2e, 0x52, 0x50, 0x43, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x70, 0x63, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x68, + 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x22, 0x56, 0x0a, 0x0c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x2a, 0x4b, 0x0a, 0x07, 0x52, 0x50, 0x43, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, + 0x0b, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x00, 0x12, 0x08, + 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x45, 0x42, 0x53, + 0x4f, 0x43, 0x4b, 0x45, 0x54, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, + 0x52, 0x50, 0x43, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x53, 0x54, 0x10, 0x04, 0x2a, + 0x30, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x47, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, + 0x01, 0x42, 0xa2, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6b, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x20, 0x63, 0x6f, 0x73, 0x6d, 0x6f, + 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6b, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0xa2, 0x02, 0x03, 0x50, 0x53, + 0x58, 0xaa, 0x02, 0x0f, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x53, 0x68, 0x61, + 0x72, 0x65, 0x64, 0xca, 0x02, 0x0f, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x5c, 0x53, + 0x68, 0x61, 0x72, 0x65, 0x64, 0xe2, 0x02, 0x1b, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x10, 0x50, 0x6f, 0x6b, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x3a, 0x3a, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/config.yml b/config.yml index fe52ad944..62fa1b766 100644 --- a/config.yml +++ b/config.yml @@ -141,9 +141,11 @@ genesis: - service: id: anvil name: "" + compute_units_per_relay: 1 - service: id: ollama name: "" + compute_units_per_relay: 1 stake: # NB: This value should be exactly 1upokt smaller than the value in # `supplier1_stake_config.yaml` so that the stake command causes a state change. diff --git a/e2e/tests/0_settlement.feature b/e2e/tests/0_settlement.feature index 719e8ec0d..b537fc3ed 100644 --- a/e2e/tests/0_settlement.feature +++ b/e2e/tests/0_settlement.feature @@ -13,6 +13,7 @@ Feature: Tokenomics Namespace And the "supplier" account for "supplier1" is staked And an account exists for "app1" And the "application" account for "app1" is staked + And the service "anvil" registered for application "app1" has a compute units per relay of "1" When the supplier "supplier1" has serviced a session with "10" relays for service "anvil" for application "app1" And the user should wait for the "proof" module "CreateClaim" Message to be submitted And the user should wait for the "proof" module "SubmitProof" Message to be submitted diff --git a/e2e/tests/init_test.go b/e2e/tests/init_test.go index 084fa26b9..8c7a88d16 100644 --- a/e2e/tests/init_test.go +++ b/e2e/tests/init_test.go @@ -353,6 +353,21 @@ func (s *suite) TheAccountForIsStaked(actorType, accName string) { s.scenarioState[accStakeKey(actorType, accName)] = stakeAmount // save the stakeAmount for later } +func (s *suite) TheServiceRegisteredForApplicationHasAComputeUnitsPerRelayOf(serviceId string, appName string, cuprStr string) { + app, ok := accNameToAppMap[appName] + require.True(s, ok, "application %s not found", appName) + + for _, serviceConfig := range app.ServiceConfigs { + if serviceConfig.Service.Id == serviceId { + cupr, err := strconv.ParseUint(cuprStr, 10, 64) + require.NoError(s, err) + require.Equal(s, cupr, serviceConfig.Service.ComputeUnitsPerRelay) + return + } + } + s.Fatalf("ERROR: service %s is not registered for application %s", serviceId, appName) +} + func (s *suite) TheForAccountIsNotStaked(actorType, accName string) { _, ok := s.getStakedAmount(actorType, accName) require.Falsef(s, ok, "account %s of type %s SHOULD NOT be staked", accName, actorType) diff --git a/proto/poktroll/shared/service.proto b/proto/poktroll/shared/service.proto index 5ba16f7ef..7f45534c3 100644 --- a/proto/poktroll/shared/service.proto +++ b/proto/poktroll/shared/service.proto @@ -13,6 +13,9 @@ message Service { // TODO_BETA: Name is currently unused but acts as a reminder that an optional onchain representation of the service is necessary string name = 2; // (Optional) Semantic human readable name for the service + + // Used alongside the global 'compute_units_to_tokens_multipler' to calculate the cost of a relay for this service + uint64 compute_units_per_relay = 3; // Compute units required per relay for this service } // ApplicationServiceConfig holds the service configuration the application stakes for diff --git a/testutil/keeper/tokenomics.go b/testutil/keeper/tokenomics.go index a0b5ffc19..78089a0fe 100644 --- a/testutil/keeper/tokenomics.go +++ b/testutil/keeper/tokenomics.go @@ -72,13 +72,14 @@ type TokenomicsKeepersOpt func(context.Context, *TokenomicsModuleKeepers) contex func TokenomicsKeeper(t testing.TB) (tokenomicsKeeper tokenomicskeeper.Keeper, ctx context.Context) { t.Helper() - k, ctx, _, _ := TokenomicsKeeperWithActorAddrs(t) + k, ctx, _, _ := TokenomicsKeeperWithActorAddrs(t, nil) return k, ctx } // TODO_TECHDEBT: Have the callers use the keepers to find `appAddr` and `supplierAddr` // rather than returning them explicitly. -func TokenomicsKeeperWithActorAddrs(t testing.TB) ( +// TODO_TECHDEBT(@Olshansk): Remove `service` parameter and convert proper options. +func TokenomicsKeeperWithActorAddrs(t testing.TB, service *sharedtypes.Service) ( tokenomicsKeeper tokenomicskeeper.Keeper, ctx context.Context, appAddr string, @@ -106,6 +107,14 @@ func TokenomicsKeeperWithActorAddrs(t testing.TB) ( 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(), diff --git a/testutil/proof/fixture_generators.go b/testutil/proof/fixture_generators.go index 19855b8af..f6e6aedbb 100644 --- a/testutil/proof/fixture_generators.go +++ b/testutil/proof/fixture_generators.go @@ -16,16 +16,20 @@ import ( sharedtypes "github.com/pokt-network/poktroll/x/shared/types" ) +const ( + // DefaultTestServiceID is the default test service ID + DefaultTestServiceID = "svc1" +) + // BaseClaim returns a base (default, example, etc..) claim with the given app -// address, supplier address, and sum that can be used for testing. -func BaseClaim(appAddr, supplierAddr string, sum uint64) prooftypes.Claim { +// address, supplier address, sum, and serviceID that can be used for testing. +func BaseClaim(appAddr, supplierAddr string, sum uint64, serviceId string) prooftypes.Claim { return prooftypes.Claim{ SupplierAddress: supplierAddr, SessionHeader: &sessiontypes.SessionHeader{ ApplicationAddress: appAddr, Service: &sharedtypes.Service{ - Id: "svc1", - Name: "svcName1", + Id: serviceId, }, SessionId: "session_id", SessionStartBlockHeight: 1, @@ -40,7 +44,7 @@ func BaseClaim(appAddr, supplierAddr string, sum uint64) prooftypes.Claim { // generated this way will have a random chance to require a proof via probabilistic // selection. func ClaimWithRandomHash(t *testing.T, appAddr, supplierAddr string, sum uint64) prooftypes.Claim { - claim := BaseClaim(appAddr, supplierAddr, sum) + claim := BaseClaim(appAddr, supplierAddr, sum, DefaultTestServiceID) claim.RootHash = RandSmstRootWithSum(t, sum) return claim } diff --git a/x/service/keeper/msg_server_add_service_test.go b/x/service/keeper/msg_server_add_service_test.go index 0930ede44..961fb9912 100644 --- a/x/service/keeper/msg_server_add_service_test.go +++ b/x/service/keeper/msg_server_add_service_test.go @@ -21,13 +21,15 @@ func TestMsgServer_AddService(t *testing.T) { // Declare test services svc1 := sharedtypes.Service{ - Id: "svc1", - Name: "service 1", + Id: "svc1", + Name: "service 1", + ComputeUnitsPerRelay: 1, } preExistingService := sharedtypes.Service{ - Id: "svc2", - Name: "service 2", + Id: "svc2", + Name: "service 2", + ComputeUnitsPerRelay: 1, } // Generate a valid address @@ -125,6 +127,17 @@ func TestMsgServer_AddService(t *testing.T) { }, expectedErr: types.ErrServiceMissingName, }, + { + desc: "invalid - zero compute units per relay", + setup: func(t *testing.T) {}, + address: sample.AccAddress(), + service: sharedtypes.Service{ + Id: "svc1", + Name: "service 1", + ComputeUnitsPerRelay: 0, + }, + expectedErr: types.ErrServiceInvalidComputUnitsPerRelay, + }, { desc: "invalid - service already exists (same service supplier)", setup: func(t *testing.T) {}, diff --git a/x/service/module/tx_add_service.go b/x/service/module/tx_add_service.go index 48ed62ccb..435d47c88 100644 --- a/x/service/module/tx_add_service.go +++ b/x/service/module/tx_add_service.go @@ -1,6 +1,10 @@ package service +// TODO_BETA: Add `UpdateService` or modify `AddService` to `UpsertService` to allow service owners +// to update parameters of existing services. This will requiring updating `proto/poktroll/service/tx.proto` and +// all downstream code paths. import ( + "fmt" "strconv" "github.com/cosmos/cosmos-sdk/client" @@ -13,16 +17,17 @@ import ( var _ = strconv.Itoa(0) +// TODO_MAINNET: Make it possible to update a service (e.g. update # of compute units per relay func CmdAddService() *cobra.Command { cmd := &cobra.Command{ - Use: "add-service ", + Use: fmt.Sprintf("add-service [compute_units_per_relay: default={%d}]", types.DefaultComputeUnitsPerRelay), 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 but the service name doesn't have to be. Example: -$ poktrolld tx service add-service "svc1" "service_one" --keyring-backend test --from $(SUPPLIER) --node $(POCKET_NODE) --home $(POKTROLLD_HOME)`, - Args: cobra.ExactArgs(2), +$ poktrolld tx service add-service "svc1" "service_one" 1 --keyring-backend test --from $(SUPPLIER) --node $(POCKET_NODE) --home $(POKTROLLD_HOME)`, + Args: cobra.MinimumNArgs(2), RunE: func(cmd *cobra.Command, args []string) (err error) { serviceIdStr := args[0] serviceNameStr := args[1] @@ -32,10 +37,22 @@ $ poktrolld tx service add-service "svc1" "service_one" --keyring-backend test - return err } + computeUnitsPerRelay := types.DefaultComputeUnitsPerRelay + // if compute units per relay argument is provided + if len(args) > 2 { + computeUnitsPerRelay, err = strconv.ParseUint(args[2], 10, 64) + if err != nil { + return types.ErrServiceInvalidComputUnitsPerRelay.Wrapf("unable to parse as uint64: %s", args[2]) + } + } else { + fmt.Printf("Using default compute_units_per_relay: %d\n", types.DefaultComputeUnitsPerRelay) + } + msg := types.NewMsgAddService( clientCtx.GetFromAddress().String(), serviceIdStr, serviceNameStr, + computeUnitsPerRelay, ) if err := msg.ValidateBasic(); err != nil { return err diff --git a/x/service/module/tx_add_service_test.go b/x/service/module/tx_add_service_test.go index bd2b2131f..4c2d4dd60 100644 --- a/x/service/module/tx_add_service_test.go +++ b/x/service/module/tx_add_service_test.go @@ -2,6 +2,7 @@ package service_test import ( "fmt" + "strconv" "testing" sdkerrors "cosmossdk.io/errors" @@ -52,12 +53,14 @@ func TestCLI_AddService(t *testing.T) { // Prepare two valid services svc1 := sharedtypes.Service{ - Id: "svc1", - Name: "service name", + Id: "svc1", + Name: "service name", + ComputeUnitsPerRelay: 1, } svc2 := sharedtypes.Service{ - Id: "svc2", - Name: "service name 2", + Id: "svc2", + Name: "service name 2", + ComputeUnitsPerRelay: 1, } // Add svc2 to the network args := []string{ @@ -81,6 +84,15 @@ func TestCLI_AddService(t *testing.T) { supplierAddress: account.Address.String(), service: svc1, }, + { + desc: "valid - add new service without specifying compute units per relay so that it uses the default", + supplierAddress: account.Address.String(), + service: sharedtypes.Service{ + Id: svc1.Id, + Name: svc1.Name, + ComputeUnitsPerRelay: 0, // this parameter is omitted when the test is run + }, + }, { desc: "invalid - missing service id", supplierAddress: account.Address.String(), @@ -114,12 +126,17 @@ func TestCLI_AddService(t *testing.T) { require.NoError(t, net.WaitForNextBlock()) // Prepare the arguments for the CLI command - args := []string{ + argsAndFlags := []string{ test.service.Id, test.service.Name, - fmt.Sprintf("--%s=%s", flags.FlagFrom, test.supplierAddress), } - args = append(args, commonArgs...) + if test.service.ComputeUnitsPerRelay > 0 { + // Only include compute units per relay argument if provided + argsAndFlags = append(argsAndFlags, strconv.FormatUint(test.service.ComputeUnitsPerRelay, 10)) + } + argsAndFlags = append(argsAndFlags, fmt.Sprintf("--%s=%s", flags.FlagFrom, test.supplierAddress)) + + args := append(argsAndFlags, commonArgs...) // Execute the command addServiceOutput, err := clitestutil.ExecTestCLICmd(ctx, service.CmdAddService(), args) diff --git a/x/service/types/errors.go b/x/service/types/errors.go index 252ff75f9..862ae0edc 100644 --- a/x/service/types/errors.go +++ b/x/service/types/errors.go @@ -6,16 +6,17 @@ import sdkerrors "cosmossdk.io/errors" // x/service module sentinel errors var ( - ErrServiceInvalidSigner = sdkerrors.Register(ModuleName, 1100, "expected gov account as only signer for proposal message") - ErrServiceDuplicateIndex = sdkerrors.Register(ModuleName, 1101, "duplicate index when adding a new service") - ErrServiceInvalidAddress = sdkerrors.Register(ModuleName, 1102, "invalid address when adding a new service") - ErrServiceMissingID = sdkerrors.Register(ModuleName, 1103, "missing service ID") - ErrServiceMissingName = sdkerrors.Register(ModuleName, 1104, "missing service name") - ErrServiceAlreadyExists = sdkerrors.Register(ModuleName, 1105, "service already exists") - ErrServiceInvalidServiceFee = sdkerrors.Register(ModuleName, 1106, "invalid ServiceFee") - ErrServiceAccountNotFound = sdkerrors.Register(ModuleName, 1107, "account not found") - ErrServiceNotEnoughFunds = sdkerrors.Register(ModuleName, 1108, "not enough funds to add service") - ErrServiceFailedToDeductFee = sdkerrors.Register(ModuleName, 1109, "failed to deduct fee") - ErrServiceInvalidRelayResponse = sdkerrors.Register(ModuleName, 1110, "invalid relay response") - ErrServiceInvalidRelayRequest = sdkerrors.Register(ModuleName, 1111, "invalid relay request") + ErrServiceInvalidSigner = sdkerrors.Register(ModuleName, 1100, "expected gov account as only signer for proposal message") + ErrServiceDuplicateIndex = sdkerrors.Register(ModuleName, 1101, "duplicate index when adding a new service") + ErrServiceInvalidAddress = sdkerrors.Register(ModuleName, 1102, "invalid address when adding a new service") + ErrServiceMissingID = sdkerrors.Register(ModuleName, 1103, "missing service ID") + ErrServiceMissingName = sdkerrors.Register(ModuleName, 1104, "missing service name") + ErrServiceAlreadyExists = sdkerrors.Register(ModuleName, 1105, "service already exists") + ErrServiceInvalidServiceFee = sdkerrors.Register(ModuleName, 1106, "invalid ServiceFee") + ErrServiceAccountNotFound = sdkerrors.Register(ModuleName, 1107, "account not found") + ErrServiceNotEnoughFunds = sdkerrors.Register(ModuleName, 1108, "not enough funds to add service") + ErrServiceFailedToDeductFee = sdkerrors.Register(ModuleName, 1109, "failed to deduct fee") + ErrServiceInvalidRelayResponse = sdkerrors.Register(ModuleName, 1110, "invalid relay response") + ErrServiceInvalidRelayRequest = sdkerrors.Register(ModuleName, 1111, "invalid relay request") + ErrServiceInvalidComputUnitsPerRelay = sdkerrors.Register(ModuleName, 1112, "invalid compute units per relay") ) diff --git a/x/service/types/message_add_service.go b/x/service/types/message_add_service.go index b19d920e3..d14ada7f4 100644 --- a/x/service/types/message_add_service.go +++ b/x/service/types/message_add_service.go @@ -2,19 +2,27 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/pokt-network/poktroll/x/shared/types" ) +const ( + DefaultComputeUnitsPerRelay uint64 = 1 + // ComputeUnitsPerRelayMax is the maximum allowed compute_units_per_relay value when adding or updating a service. + // TODO_MAINNET: The reason we have a maximum is to account for potential integer overflows. This is + // something that needs to be revisited or reconsidered prior to mainnet. + ComputeUnitsPerRelayMax uint64 = 2 ^ 16 +) + var _ sdk.Msg = (*MsgAddService)(nil) -func NewMsgAddService(address, serviceId, serviceName string) *MsgAddService { +func NewMsgAddService(address, serviceId, serviceName string, computeUnitsPerRelay uint64) *MsgAddService { return &MsgAddService{ Address: address, - Service: types.Service{ - Id: serviceId, - Name: serviceName, - }, + Service: *types.NewService( + serviceId, + serviceName, + computeUnitsPerRelay, + ), } } @@ -30,5 +38,18 @@ func (msg *MsgAddService) ValidateBasic() error { if msg.Service.Name == "" { return ErrServiceMissingName } + if err := ValidateComputeUnitsPerRelay(msg.Service.ComputeUnitsPerRelay); err != nil { + return err + } + return nil +} + +// ValidateComputeUnitsPerRelay makes sure the compute units per relay is a valid value +func ValidateComputeUnitsPerRelay(computeUnitsPerRelay uint64) error { + if computeUnitsPerRelay == 0 { + return ErrServiceInvalidComputUnitsPerRelay.Wrap("compute units per relay must be greater than 0") + } else if computeUnitsPerRelay > ComputeUnitsPerRelayMax { + return ErrServiceInvalidComputUnitsPerRelay.Wrapf("compute units per relay must be less than %d", ComputeUnitsPerRelayMax) + } return nil } diff --git a/x/service/types/message_add_service_test.go b/x/service/types/message_add_service_test.go index 05fd283c1..b187ea89f 100644 --- a/x/service/types/message_add_service_test.go +++ b/x/service/types/message_add_service_test.go @@ -36,11 +36,18 @@ func TestMsgAddService_ValidateBasic(t *testing.T) { Service: sharedtypes.Service{Id: "svc1"}, // Name intentionally omitted }, expectedErr: ErrServiceMissingName, + }, { + desc: "valid service supplier address - zero compute units per relay", + msg: MsgAddService{ + Address: sample.AccAddress(), + Service: sharedtypes.Service{Id: "svc1", Name: "service name", ComputeUnitsPerRelay: 0}, + }, + expectedErr: ErrServiceInvalidComputUnitsPerRelay, }, { desc: "valid service supplier address and service", msg: MsgAddService{ Address: sample.AccAddress(), - Service: sharedtypes.Service{Id: "svc1", Name: "service name"}, + Service: sharedtypes.Service{Id: "svc1", Name: "service name", ComputeUnitsPerRelay: 1}, }, expectedErr: nil, }, @@ -56,3 +63,39 @@ func TestMsgAddService_ValidateBasic(t *testing.T) { }) } } + +func TestValidateComputeUnitsPerRelay(t *testing.T) { + tests := []struct { + desc string + computeUnitsPerRelay uint64 + expectedErr error + }{ + { + desc: "zero compute units per relay", + computeUnitsPerRelay: 0, + expectedErr: ErrServiceInvalidComputUnitsPerRelay, + }, { + desc: "valid compute units per relay", + computeUnitsPerRelay: 1, + expectedErr: nil, + }, { + desc: "max compute units per relay", + computeUnitsPerRelay: ComputeUnitsPerRelayMax, + expectedErr: nil, + }, { + desc: "compute units per relay greater than max", + computeUnitsPerRelay: ComputeUnitsPerRelayMax + 1, + expectedErr: ErrServiceInvalidComputUnitsPerRelay, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + err := ValidateComputeUnitsPerRelay(test.computeUnitsPerRelay) + if test.expectedErr != nil { + require.ErrorIs(t, err, test.expectedErr) + return + } + require.NoError(t, err) + }) + } +} diff --git a/x/shared/types/service.go b/x/shared/types/service.go new file mode 100644 index 000000000..e05aa2711 --- /dev/null +++ b/x/shared/types/service.go @@ -0,0 +1,10 @@ +package types + +// NewService creates a new Service instance +func NewService(id string, name string, computeUnitsPerRelay uint64) *Service { + return &Service{ + Id: id, + Name: name, + ComputeUnitsPerRelay: computeUnitsPerRelay, + } +} diff --git a/x/shared/types/service.pb.go b/x/shared/types/service.pb.go index 7338d5226..4a5a0201a 100644 --- a/x/shared/types/service.pb.go +++ b/x/shared/types/service.pb.go @@ -93,6 +93,8 @@ type Service struct { Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // TODO_BETA: Name is currently unused but acts as a reminder that an optional onchain representation of the service is necessary Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Used alongside the global 'compute_units_to_tokens_multipler' to calculate the cost of a relay for this service + ComputeUnitsPerRelay uint64 `protobuf:"varint,3,opt,name=compute_units_per_relay,json=computeUnitsPerRelay,proto3" json:"compute_units_per_relay,omitempty"` } func (m *Service) Reset() { *m = Service{} } @@ -142,6 +144,13 @@ func (m *Service) GetName() string { return "" } +func (m *Service) GetComputeUnitsPerRelay() uint64 { + if m != nil { + return m.ComputeUnitsPerRelay + } + return 0 +} + // ApplicationServiceConfig holds the service configuration the application stakes for type ApplicationServiceConfig struct { Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` @@ -367,35 +376,38 @@ func init() { func init() { proto.RegisterFile("poktroll/shared/service.proto", fileDescriptor_302c2f793a11ae1e) } var fileDescriptor_302c2f793a11ae1e = []byte{ - // 446 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x52, 0x4b, 0x6f, 0xd3, 0x40, - 0x10, 0xf6, 0xa3, 0xe0, 0x64, 0xd2, 0xa6, 0xd6, 0x08, 0x24, 0x5f, 0x6a, 0x15, 0x9f, 0xaa, 0x4a, - 0xb5, 0xab, 0xf4, 0xc0, 0x11, 0x51, 0xcb, 0x54, 0x21, 0xc2, 0xae, 0xd6, 0x2e, 0x95, 0xb8, 0x54, - 0xa9, 0xb3, 0xb4, 0x56, 0x5c, 0xef, 0x6a, 0xed, 0x14, 0xf2, 0x1f, 0x38, 0x20, 0x7e, 0x15, 0xc7, - 0x1e, 0x39, 0xa2, 0xe4, 0x8f, 0xa0, 0xf5, 0x83, 0x47, 0x8b, 0xb8, 0x70, 0x1b, 0x7b, 0xbe, 0xd7, - 0xcc, 0x0e, 0xec, 0x70, 0x36, 0xaf, 0x04, 0xcb, 0x73, 0xaf, 0xbc, 0x9e, 0x0a, 0x3a, 0xf3, 0x4a, - 0x2a, 0x6e, 0xb3, 0x94, 0xba, 0x5c, 0xb0, 0x8a, 0xe1, 0x76, 0xd7, 0x76, 0x9b, 0xb6, 0x73, 0x00, - 0x46, 0xdc, 0x20, 0x70, 0x08, 0x5a, 0x36, 0xb3, 0xd4, 0x5d, 0x75, 0xaf, 0x4f, 0xb4, 0x6c, 0x86, - 0x08, 0x1b, 0xc5, 0xf4, 0x86, 0x5a, 0x5a, 0xfd, 0xa7, 0xae, 0x9d, 0x10, 0xac, 0x97, 0x9c, 0xe7, - 0x59, 0x3a, 0xad, 0x32, 0x56, 0xb4, 0x4c, 0x9f, 0x15, 0xef, 0xb3, 0x2b, 0x1c, 0x81, 0xd1, 0x9a, - 0xd5, 0x22, 0x83, 0x91, 0xe5, 0xde, 0x73, 0x73, 0x5b, 0x02, 0xe9, 0x80, 0xce, 0x27, 0x15, 0x9e, - 0xc6, 0x0b, 0xa9, 0x48, 0xc5, 0x7f, 0xab, 0xe1, 0x0b, 0xe8, 0xd3, 0x62, 0xc6, 0x59, 0x56, 0x54, - 0xa5, 0xa5, 0xed, 0xea, 0x7b, 0x83, 0xd1, 0xb3, 0x87, 0xac, 0xd6, 0x2e, 0x68, 0x91, 0xe4, 0x17, - 0xc7, 0xf9, 0xa2, 0x82, 0x79, 0xbf, 0x8f, 0x26, 0xe8, 0x0b, 0x91, 0xb7, 0x8b, 0x91, 0x25, 0x1e, - 0x41, 0x4f, 0xf0, 0xf4, 0xa2, 0x5a, 0xf2, 0x66, 0x3b, 0xc3, 0xbf, 0x84, 0x23, 0xa7, 0x7e, 0xb2, - 0xe4, 0x94, 0x18, 0x82, 0xa7, 0xb2, 0xc0, 0xe7, 0x60, 0xa4, 0xf5, 0x68, 0xa5, 0xa5, 0xd7, 0xd1, - 0x76, 0x1e, 0x70, 0x9a, 0xd1, 0x23, 0x2e, 0x77, 0x4b, 0x3a, 0xb4, 0xf3, 0x16, 0x36, 0x7f, 0x6f, - 0xe0, 0x21, 0xe8, 0x73, 0xba, 0xac, 0xf3, 0x0c, 0x47, 0xf6, 0x3f, 0x45, 0x4a, 0x22, 0xa1, 0xf8, - 0x04, 0x1e, 0xdd, 0x4e, 0xf3, 0x45, 0xf7, 0x94, 0xcd, 0xc7, 0xfe, 0x04, 0x8c, 0x36, 0x24, 0x6e, - 0xc3, 0xe0, 0x2c, 0x9c, 0x84, 0xd1, 0x79, 0x78, 0x41, 0x4e, 0x7d, 0x53, 0xc1, 0x1e, 0x6c, 0x9c, - 0xc8, 0x4a, 0xc5, 0x2d, 0xe8, 0x9f, 0x07, 0xc7, 0x71, 0xe4, 0x4f, 0x82, 0xc4, 0xd4, 0x70, 0x13, - 0x7a, 0xaf, 0xe3, 0xa8, 0x81, 0xe9, 0x12, 0x46, 0x82, 0x38, 0x31, 0x37, 0xf6, 0x0f, 0x61, 0xeb, - 0x0f, 0x63, 0x44, 0x18, 0x76, 0x92, 0x7e, 0x14, 0xbe, 0x1a, 0x9f, 0x98, 0x0a, 0x0e, 0xc0, 0x48, - 0xc6, 0x6f, 0x82, 0xe8, 0x2c, 0x31, 0xd5, 0xe3, 0xf1, 0xd7, 0x95, 0xad, 0xde, 0xad, 0x6c, 0xf5, - 0xfb, 0xca, 0x56, 0x3f, 0xaf, 0x6d, 0xe5, 0x6e, 0x6d, 0x2b, 0xdf, 0xd6, 0xb6, 0xf2, 0xce, 0xbb, - 0xca, 0xaa, 0xeb, 0xc5, 0xa5, 0x9b, 0xb2, 0x1b, 0x4f, 0x4e, 0x77, 0x50, 0xd0, 0xea, 0x03, 0x13, - 0x73, 0xef, 0xe7, 0x6d, 0x7f, 0xec, 0xae, 0x5b, 0x3e, 0x41, 0x79, 0xf9, 0xb8, 0x3e, 0xee, 0xa3, - 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x1d, 0x9c, 0x9c, 0xfd, 0x02, 0x00, 0x00, + // 484 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x4d, 0x6f, 0xd3, 0x40, + 0x10, 0xcd, 0xc6, 0x01, 0x27, 0x93, 0x36, 0xb5, 0x56, 0x45, 0xf8, 0x52, 0x2b, 0xe4, 0x14, 0x55, + 0x22, 0xa9, 0x52, 0x21, 0x8e, 0x88, 0x46, 0xa6, 0x0a, 0x11, 0x76, 0xb4, 0x76, 0xa8, 0xc4, 0xc5, + 0x72, 0xed, 0xa5, 0xb5, 0xe2, 0x78, 0x57, 0x6b, 0xbb, 0x90, 0xff, 0xc0, 0x01, 0xf1, 0xab, 0x38, + 0xf6, 0xc8, 0x11, 0x25, 0x7f, 0x04, 0xad, 0x3f, 0xf8, 0x68, 0x11, 0x17, 0x6e, 0x63, 0xbf, 0x37, + 0xef, 0xbd, 0x19, 0x7b, 0xe0, 0x88, 0xb3, 0x55, 0x26, 0x58, 0x1c, 0x8f, 0xd3, 0x6b, 0x5f, 0xd0, + 0x70, 0x9c, 0x52, 0x71, 0x13, 0x05, 0x74, 0xc4, 0x05, 0xcb, 0x18, 0x3e, 0xa8, 0xe1, 0x51, 0x09, + 0x0f, 0x42, 0x50, 0x9d, 0x92, 0x81, 0x7b, 0xd0, 0x8c, 0x42, 0x1d, 0xf5, 0xd1, 0xb0, 0x43, 0x9a, + 0x51, 0x88, 0x31, 0xb4, 0x12, 0x7f, 0x4d, 0xf5, 0x66, 0xf1, 0xa6, 0xa8, 0xf1, 0x33, 0x78, 0x1c, + 0xb0, 0x35, 0xcf, 0x33, 0xea, 0xe5, 0x49, 0x94, 0xa5, 0x1e, 0xa7, 0xc2, 0x13, 0x34, 0xf6, 0x37, + 0xba, 0xd2, 0x47, 0xc3, 0x16, 0x39, 0xac, 0xe0, 0xa5, 0x44, 0x17, 0x54, 0x10, 0x89, 0x0d, 0x2c, + 0xd0, 0x5f, 0x72, 0x1e, 0x47, 0x81, 0x9f, 0x45, 0x2c, 0xa9, 0x0c, 0xa7, 0x2c, 0x79, 0x1f, 0x5d, + 0xe1, 0x09, 0xa8, 0x55, 0xc6, 0xc2, 0xbb, 0x3b, 0xd1, 0x47, 0x77, 0x42, 0x8e, 0xaa, 0x06, 0x52, + 0x13, 0x07, 0x9f, 0x10, 0x3c, 0x72, 0x72, 0xa9, 0x48, 0xc5, 0x7f, 0xab, 0xe1, 0x17, 0xd0, 0xa1, + 0x49, 0xc8, 0x59, 0x94, 0x64, 0xa9, 0xde, 0xec, 0x2b, 0xc3, 0xee, 0xe4, 0xc9, 0xfd, 0xae, 0xca, + 0xce, 0xac, 0x98, 0xe4, 0x57, 0xcf, 0xe0, 0x0b, 0x02, 0xed, 0x2e, 0x8e, 0x35, 0x50, 0x72, 0x11, + 0x57, 0xfb, 0x94, 0x25, 0x3e, 0x85, 0xb6, 0xe0, 0x81, 0x97, 0x6d, 0x78, 0xb9, 0xd4, 0xde, 0x5f, + 0xc2, 0x91, 0xc5, 0xd4, 0xdd, 0x70, 0x4a, 0x54, 0xc1, 0x03, 0x59, 0xe0, 0xe7, 0xa0, 0x06, 0xc5, + 0x68, 0xa9, 0xae, 0x14, 0xd1, 0x8e, 0xee, 0xf5, 0x94, 0xa3, 0xdb, 0x5c, 0xee, 0x96, 0xd4, 0xec, + 0xc1, 0x5b, 0xd8, 0xfb, 0x1d, 0xc0, 0x27, 0xa0, 0xac, 0xe8, 0xa6, 0xc8, 0xd3, 0x9b, 0x18, 0xff, + 0x14, 0x49, 0x89, 0xa4, 0xe2, 0x43, 0x78, 0x70, 0xe3, 0xc7, 0x79, 0xfd, 0x07, 0x94, 0x0f, 0xc7, + 0x73, 0x50, 0xab, 0x90, 0xf8, 0x00, 0xba, 0x4b, 0x6b, 0x6e, 0xd9, 0x17, 0x96, 0x47, 0x16, 0x53, + 0xad, 0x81, 0xdb, 0xd0, 0x3a, 0x97, 0x15, 0xc2, 0xfb, 0xd0, 0xb9, 0x30, 0xcf, 0x1c, 0x7b, 0x3a, + 0x37, 0x5d, 0xad, 0x89, 0xf7, 0xa0, 0xfd, 0xda, 0xb1, 0x4b, 0x9a, 0x22, 0x69, 0xc4, 0x74, 0x5c, + 0xad, 0x75, 0x7c, 0x02, 0xfb, 0x7f, 0x18, 0x63, 0x0c, 0xbd, 0x5a, 0x72, 0x6a, 0x5b, 0xaf, 0x66, + 0xe7, 0x5a, 0x03, 0x77, 0x41, 0x75, 0x67, 0x6f, 0x4c, 0x7b, 0xe9, 0x6a, 0xe8, 0x6c, 0xf6, 0x75, + 0x6b, 0xa0, 0xdb, 0xad, 0x81, 0xbe, 0x6f, 0x0d, 0xf4, 0x79, 0x67, 0x34, 0x6e, 0x77, 0x46, 0xe3, + 0xdb, 0xce, 0x68, 0xbc, 0x1b, 0x5f, 0x45, 0xd9, 0x75, 0x7e, 0x39, 0x0a, 0xd8, 0x7a, 0x2c, 0xa7, + 0x7b, 0x9a, 0xd0, 0xec, 0x03, 0x13, 0xab, 0xf1, 0xcf, 0x93, 0xf8, 0x58, 0x1f, 0x85, 0xfc, 0x04, + 0xe9, 0xe5, 0xc3, 0xe2, 0x26, 0x4e, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff, 0xd2, 0xa4, 0x5d, 0x3a, + 0x34, 0x03, 0x00, 0x00, } func (m *Service) Marshal() (dAtA []byte, err error) { @@ -418,6 +430,11 @@ func (m *Service) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ComputeUnitsPerRelay != 0 { + i = encodeVarintService(dAtA, i, uint64(m.ComputeUnitsPerRelay)) + i-- + dAtA[i] = 0x18 + } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -628,6 +645,9 @@ func (m *Service) Size() (n int) { if l > 0 { n += 1 + l + sovService(uint64(l)) } + if m.ComputeUnitsPerRelay != 0 { + n += 1 + sovService(uint64(m.ComputeUnitsPerRelay)) + } return n } @@ -800,6 +820,25 @@ func (m *Service) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ComputeUnitsPerRelay", wireType) + } + m.ComputeUnitsPerRelay = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ComputeUnitsPerRelay |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) diff --git a/x/tokenomics/keeper/keeper_settle_pending_claims_test.go b/x/tokenomics/keeper/keeper_settle_pending_claims_test.go index 6c97a0e5a..581bc8a0e 100644 --- a/x/tokenomics/keeper/keeper_settle_pending_claims_test.go +++ b/x/tokenomics/keeper/keeper_settle_pending_claims_test.go @@ -59,12 +59,19 @@ func (s *TestSuite) SetupTest() { // such that by default, s.claim will require a proof 100% of the time. s.expectedComputeUnits = prooftypes.DefaultProofRequirementThreshold + // Create a service that can be registered in the application and used in the claim + service := sharedtypes.NewService( + testServiceId, + "", + 1, + ) + // Prepare a claim that can be inserted s.claim = prooftypes.Claim{ SupplierAddress: supplierAddr, SessionHeader: &sessiontypes.SessionHeader{ ApplicationAddress: appAddr, - Service: &sharedtypes.Service{Id: testServiceId}, + Service: &sharedtypes.Service{Id: service.Id}, SessionId: "session_id", SessionStartBlockHeight: 1, SessionEndBlockHeight: testsession.GetSessionEndHeightWithDefaultParams(1), @@ -86,6 +93,11 @@ func (s *TestSuite) SetupTest() { app := apptypes.Application{ Address: appAddr, Stake: &appStake, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{ + { + Service: service, + }, + }, } s.keepers.SetApplication(s.ctx, app) } @@ -399,6 +411,7 @@ func (s *TestSuite) TestSettlePendingClaims_ClaimPendingAfterSettlement() { sessionOneClaim.GetSessionHeader().GetApplicationAddress(), sessionOneClaim.GetSupplierAddress(), s.expectedComputeUnits, + sessionOneClaim.GetSessionHeader().GetService().Id, ) sessionOneProofWindowCloseHeight := shared.GetProofWindowCloseHeight(&sharedParams, sessionOneEndHeight) diff --git a/x/tokenomics/keeper/msg_server_test.go b/x/tokenomics/keeper/msg_server_test.go index 9329a3b79..d64987d84 100644 --- a/x/tokenomics/keeper/msg_server_test.go +++ b/x/tokenomics/keeper/msg_server_test.go @@ -14,7 +14,7 @@ import ( func setupMsgServer(t testing.TB) (keeper.Keeper, types.MsgServer, context.Context) { t.Helper() - k, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t) + k, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t, nil) return k, keeper.NewMsgServerImpl(k), ctx } diff --git a/x/tokenomics/keeper/msg_update_params_test.go b/x/tokenomics/keeper/msg_update_params_test.go index 71f3f1436..850895087 100644 --- a/x/tokenomics/keeper/msg_update_params_test.go +++ b/x/tokenomics/keeper/msg_update_params_test.go @@ -92,7 +92,7 @@ func TestMsgUpdateParams(t *testing.T) { } func TestUpdateParams_ComputeUnitsToTokensMultiplier(t *testing.T) { - tokenomicsKeeper, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t) + tokenomicsKeeper, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t, nil) srv := keeper.NewMsgServerImpl(tokenomicsKeeper) // Set the default params diff --git a/x/tokenomics/keeper/query_params_test.go b/x/tokenomics/keeper/query_params_test.go index 42044a1ee..2b62d826d 100644 --- a/x/tokenomics/keeper/query_params_test.go +++ b/x/tokenomics/keeper/query_params_test.go @@ -10,7 +10,7 @@ import ( ) func TestGetParams(t *testing.T) { - k, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t) + k, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t, nil) // TODO_TECHDEBT(@bryanchriswhite, #394): Params tests don't assert initial state. params := types.DefaultParams() @@ -20,7 +20,7 @@ func TestGetParams(t *testing.T) { } func TestParamsQuery(t *testing.T) { - keeper, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t) + keeper, ctx, _, _ := testkeeper.TokenomicsKeeperWithActorAddrs(t, nil) params := types.DefaultParams() require.NoError(t, keeper.SetParams(ctx, params)) diff --git a/x/tokenomics/keeper/settle_session_accounting.go b/x/tokenomics/keeper/settle_session_accounting.go index 61dfb6aa6..6f4f12763 100644 --- a/x/tokenomics/keeper/settle_session_accounting.go +++ b/x/tokenomics/keeper/settle_session_accounting.go @@ -106,10 +106,17 @@ func (k Keeper) SettleSessionAccounting( return tokenomicstypes.ErrTokenomicsApplicationNotFound } - logger.Info(fmt.Sprintf("About to start settling claim for %d compute units", claimComputeUnits)) + computeUnitsPerRelay, err := k.getComputUnitsPerRelayFromApplication(application, sessionHeader.Service.Id) + if err != nil { + return err + } + + computeUnitsToTokensMultiplier := k.GetParams(ctx).ComputeUnitsToTokensMultiplier + + logger.Info(fmt.Sprintf("About to start settling claim for %d compute units with CUPR %d and CUTTM %d", claimComputeUnits, computeUnitsPerRelay, computeUnitsToTokensMultiplier)) // Calculate the amount of tokens to mint & burn - settlementCoin, err = k.getCoinFromComputeUnits(ctx, root) + settlementCoin, err = relayCountToCoin(claimComputeUnits, computeUnitsPerRelay, computeUnitsToTokensMultiplier) if err != nil { return err } @@ -211,16 +218,9 @@ func (k Keeper) SettleSessionAccounting( return nil } -func (k Keeper) getCoinFromComputeUnits(ctx context.Context, root smt.MerkleSumRoot) (cosmostypes.Coin, error) { - // Retrieve the existing tokenomics params - params := k.GetParams(ctx) - - sum, err := root.Sum() - if err != nil { - return cosmostypes.Coin{}, err - } - - upokt := math.NewInt(int64(sum * params.ComputeUnitsToTokensMultiplier)) +// relayCountToCoin calculates the amount of uPOKT to mint based on the number of relays, the service-specific ComputeUnitsPerRelay, and the ComputeUnitsPerTokenMultiplier tokenomics param +func relayCountToCoin(numRelays, computeUnitsPerRelay uint64, computeUnitsToTokensMultiplier uint64) (cosmostypes.Coin, error) { + upokt := math.NewInt(int64(numRelays * computeUnitsPerRelay * computeUnitsToTokensMultiplier)) if upokt.IsNegative() { return cosmostypes.Coin{}, tokenomicstypes.ErrTokenomicsRootHashInvalid.Wrap("sum * compute_units_to_tokens_multiplier is negative") @@ -228,3 +228,24 @@ func (k Keeper) getCoinFromComputeUnits(ctx context.Context, root smt.MerkleSumR return cosmostypes.NewCoin(volatile.DenomuPOKT, upokt), nil } + +// getComputUnitsPerRelayFromApplication retrieves the ComputeUnitsPerRelay for a given service from the application's service configs +func (k Keeper) getComputUnitsPerRelayFromApplication(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 + } + } + + logger.Warn(fmt.Sprintf("service with ID %q not found in application with address %q", serviceID, application.Address)) + return 0, tokenomicstypes.ErrTokenomicsApplicationNoServiceConfigs +} diff --git a/x/tokenomics/keeper/settle_session_accounting_test.go b/x/tokenomics/keeper/settle_session_accounting_test.go index 86a74b04f..3f4274bf8 100644 --- a/x/tokenomics/keeper/settle_session_accounting_test.go +++ b/x/tokenomics/keeper/settle_session_accounting_test.go @@ -33,11 +33,23 @@ func TestSettleSessionAccounting_HandleAppGoingIntoDebt(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.NewService( + "svc1", + "svcName1", + 1, + ) + // Add a new application appStake := cosmostypes.NewCoin("upokt", math.NewInt(1000000)) app := apptypes.Application{ Address: sample.AccAddress(), Stake: &appStake, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{ + &sharedtypes.ApplicationServiceConfig{ + Service: service, + }, + }, } keepers.SetApplication(ctx, app) @@ -55,8 +67,7 @@ func TestSettleSessionAccounting_HandleAppGoingIntoDebt(t *testing.T) { SessionHeader: &sessiontypes.SessionHeader{ ApplicationAddress: app.Address, Service: &sharedtypes.Service{ - Id: "svc1", - Name: "svcName1", + Id: service.Id, }, SessionId: "session_id", SessionStartBlockHeight: 1, @@ -80,6 +91,12 @@ 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.NewService( + "svc1", + "svcName1", + 1, + ) // Add a new application appStake := cosmostypes.NewCoin("upokt", math.NewInt(1000000)) // NB: Ensure a non-zero app stake end balance to assert against. @@ -88,6 +105,11 @@ 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) @@ -117,8 +139,7 @@ func TestSettleSessionAccounting_ValidAccounting(t *testing.T) { SessionHeader: &sessiontypes.SessionHeader{ ApplicationAddress: app.Address, Service: &sharedtypes.Service{ - Id: "svc1", - Name: "svcName1", + Id: service.Id, }, SessionId: "session_id", SessionStartBlockHeight: 1, @@ -175,6 +196,12 @@ 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.NewService( + "svc1", + "svcName1", + 1, + ) // Add a new application appStake := cosmostypes.NewCoin("upokt", math.NewInt(40000)) expectedAppEndStakeZeroAmount := cosmostypes.NewCoin("upokt", math.NewInt(0)) @@ -182,6 +209,11 @@ func TestSettleSessionAccounting_AppStakeTooLow(t *testing.T) { app := apptypes.Application{ Address: sample.AccAddress(), Stake: &appStake, + ServiceConfigs: []*sharedtypes.ApplicationServiceConfig{ + &sharedtypes.ApplicationServiceConfig{ + Service: service, + }, + }, } keepers.SetApplication(ctx, app) @@ -211,8 +243,7 @@ func TestSettleSessionAccounting_AppStakeTooLow(t *testing.T) { SessionHeader: &sessiontypes.SessionHeader{ ApplicationAddress: app.Address, Service: &sharedtypes.Service{ - Id: "svc1", - Name: "svcName1", + Id: service.Id, }, SessionId: "session_id", SessionStartBlockHeight: 1, @@ -276,7 +307,14 @@ func TestSettleSessionAccounting_AppStakeTooLow(t *testing.T) { } func TestSettleSessionAccounting_AppNotFound(t *testing.T) { - keeper, ctx, _, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t) + + service := sharedtypes.NewService( + "svc1", + "svcName1", + 1, + ) + + keeper, ctx, _, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t, service) // The base claim whose root will be customized for testing purposes claim := prooftypes.Claim{ @@ -284,8 +322,7 @@ func TestSettleSessionAccounting_AppNotFound(t *testing.T) { SessionHeader: &sessiontypes.SessionHeader{ ApplicationAddress: sample.AccAddress(), // Random address Service: &sharedtypes.Service{ - Id: "svc1", - Name: "svcName1", + Id: service.Id, }, SessionId: "session_id", SessionStartBlockHeight: 1, @@ -300,7 +337,15 @@ func TestSettleSessionAccounting_AppNotFound(t *testing.T) { } func TestSettleSessionAccounting_InvalidRoot(t *testing.T) { - keeper, ctx, appAddr, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t) + + // Create a service that can be registered in the application and used in the claims + service := sharedtypes.NewService( + "svc1", + "svcName1", + 1, + ) + + keeper, ctx, appAddr, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t, service) // Define test cases tests := []struct { @@ -352,8 +397,8 @@ func TestSettleSessionAccounting_InvalidRoot(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { // Setup claim by copying the testproof.BaseClaim and updating the root - claim := testproof.BaseClaim(appAddr, supplierAddr, 0) - claim.RootHash = smt.MerkleSumRoot(test.root[:]) + claim := testproof.BaseClaim(appAddr, supplierAddr, 0, service.Id) + claim.RootHash = smt.MerkleRoot(test.root[:]) // Execute test function err := keeper.SettleSessionAccounting(ctx, &claim) @@ -369,7 +414,15 @@ func TestSettleSessionAccounting_InvalidRoot(t *testing.T) { } func TestSettleSessionAccounting_InvalidClaim(t *testing.T) { - keeper, ctx, appAddr, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t) + + // Create a service that can be registered in the application and used in the claims + service := sharedtypes.NewService( + "svc1", + "svcName1", + 1, + ) + + keeper, ctx, appAddr, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t, service) // Define test cases tests := []struct { @@ -382,7 +435,7 @@ func TestSettleSessionAccounting_InvalidClaim(t *testing.T) { { desc: "Valid Claim", claim: func() *prooftypes.Claim { - claim := testproof.BaseClaim(appAddr, supplierAddr, 42) + claim := testproof.BaseClaim(appAddr, supplierAddr, 42, service.Id) return &claim }(), errExpected: false, @@ -396,7 +449,7 @@ func TestSettleSessionAccounting_InvalidClaim(t *testing.T) { { desc: "Claim with nil session header", claim: func() *prooftypes.Claim { - claim := testproof.BaseClaim(appAddr, supplierAddr, 42) + claim := testproof.BaseClaim(appAddr, supplierAddr, 42, service.Id) claim.SessionHeader = nil return &claim }(), @@ -406,7 +459,7 @@ func TestSettleSessionAccounting_InvalidClaim(t *testing.T) { { desc: "Claim with invalid session id", claim: func() *prooftypes.Claim { - claim := testproof.BaseClaim(appAddr, supplierAddr, 42) + claim := testproof.BaseClaim(appAddr, supplierAddr, 42, service.Id) claim.SessionHeader.SessionId = "" return &claim }(), @@ -416,7 +469,7 @@ func TestSettleSessionAccounting_InvalidClaim(t *testing.T) { { desc: "Claim with invalid application address", claim: func() *prooftypes.Claim { - claim := testproof.BaseClaim(appAddr, supplierAddr, 42) + claim := testproof.BaseClaim(appAddr, supplierAddr, 42, service.Id) claim.SessionHeader.ApplicationAddress = "invalid address" return &claim }(), @@ -426,7 +479,7 @@ func TestSettleSessionAccounting_InvalidClaim(t *testing.T) { { desc: "Claim with invalid supplier address", claim: func() *prooftypes.Claim { - claim := testproof.BaseClaim(appAddr, supplierAddr, 42) + claim := testproof.BaseClaim(appAddr, supplierAddr, 42, service.Id) claim.SupplierAddress = "invalid address" return &claim }(), diff --git a/x/tokenomics/module/genesis_test.go b/x/tokenomics/module/genesis_test.go index ebe89fa4e..9b156df28 100644 --- a/x/tokenomics/module/genesis_test.go +++ b/x/tokenomics/module/genesis_test.go @@ -26,7 +26,7 @@ func TestGenesis(t *testing.T) { // this line is used by starport scaffolding # genesis/test/state } - k, ctx, _, _ := keepertest.TokenomicsKeeperWithActorAddrs(t) + k, ctx, _, _ := keepertest.TokenomicsKeeperWithActorAddrs(t, nil) tokenomics.InitGenesis(ctx, k, genesisState) got := tokenomics.ExportGenesis(ctx, k) require.NotNil(t, got) diff --git a/x/tokenomics/types/errors.go b/x/tokenomics/types/errors.go index 659142d1c..812b3746a 100644 --- a/x/tokenomics/types/errors.go +++ b/x/tokenomics/types/errors.go @@ -16,6 +16,7 @@ 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")