From b0f7406d2fa7d0d38fff2c55f6511cefa3676cc9 Mon Sep 17 00:00:00 2001 From: omerlavanet Date: Tue, 19 Dec 2023 11:00:26 +0200 Subject: [PATCH 01/11] added adjustments to storage and their calculations --- .../lava/subscription/adjustment.proto | 11 + x/pairing/keeper/epoch_payments.go | 37 +- .../keeper/msg_server_relay_payment_test.go | 2 +- x/pairing/keeper/provider_payment_storage.go | 10 + x/pairing/types/expected_keepers.go | 1 + x/subscription/keeper/adjustment.go | 159 +++++++ x/subscription/keeper/cu_tracker.go | 12 +- x/subscription/types/adjustment.pb.go | 389 ++++++++++++++++++ x/subscription/types/key_adjustment.go | 23 ++ x/subscription/types/keys.go | 4 + 10 files changed, 639 insertions(+), 9 deletions(-) create mode 100644 proto/lavanet/lava/subscription/adjustment.proto create mode 100644 x/subscription/keeper/adjustment.go create mode 100644 x/subscription/types/adjustment.pb.go create mode 100644 x/subscription/types/key_adjustment.go diff --git a/proto/lavanet/lava/subscription/adjustment.proto b/proto/lavanet/lava/subscription/adjustment.proto new file mode 100644 index 0000000000..d3645220b8 --- /dev/null +++ b/proto/lavanet/lava/subscription/adjustment.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package lavanet.lava.subscription; + +option go_package = "github.com/lavanet/lava/x/subscription/types"; + +message Adjustment { + string index = 1; + uint64 adjustedUsage = 2; + uint64 totalUsage = 3; +} + diff --git a/x/pairing/keeper/epoch_payments.go b/x/pairing/keeper/epoch_payments.go index a2a4f40f56..869b1e7c0e 100644 --- a/x/pairing/keeper/epoch_payments.go +++ b/x/pairing/keeper/epoch_payments.go @@ -67,7 +67,7 @@ func (k Keeper) GetAllEpochPayments(ctx sdk.Context) (list []types.EpochPayments // Function to remove epochPayments objects from deleted epochs (older than the chain's memory) func (k Keeper) RemoveOldEpochPayment(ctx sdk.Context) { for _, epoch := range k.epochStorageKeeper.GetDeletedEpochs(ctx) { - k.RemoveAllEpochPaymentsForBlock(ctx, epoch) + k.RemoveAllEpochPaymentsForBlockAppendAdjustments(ctx, epoch) } } @@ -115,7 +115,7 @@ func (k Keeper) AddEpochPayment(ctx sdk.Context, chainID string, epoch uint64, p } // Function to remove all epochPayments objects from a specific epoch -func (k Keeper) RemoveAllEpochPaymentsForBlock(ctx sdk.Context, blockForDelete uint64) { +func (k Keeper) RemoveAllEpochPaymentsForBlockAppendAdjustments(ctx sdk.Context, blockForDelete uint64) { // get the epochPayments object of blockForDelete epochPayments, found, key := k.GetEpochPaymentsFromBlock(ctx, blockForDelete) if !found { @@ -123,18 +123,25 @@ func (k Keeper) RemoveAllEpochPaymentsForBlock(ctx sdk.Context, blockForDelete u } // TODO: update Qos in providerQosFS. new consumers (cluster.subUsage = 0) get default QoS (what is default?) - + consumerUsage := map[string]uint64{} + type couplingConsumerProvider struct { + consumer string + provider string + } + // we are keeping the iteration keys to keep determinism when going over the map + iterationOrder := []couplingConsumerProvider{} + couplingUsage := map[couplingConsumerProvider]uint64{} // go over the epochPayments object's providerPaymentStorageKeys userPaymentsStorageKeys := epochPayments.GetProviderPaymentStorageKeys() for _, userPaymentStorageKey := range userPaymentsStorageKeys { // get the providerPaymentStorage object - userPaymentStorage, found := k.GetProviderPaymentStorage(ctx, userPaymentStorageKey) + providerPaymentStorage, found := k.GetProviderPaymentStorage(ctx, userPaymentStorageKey) if !found { continue } // go over the providerPaymentStorage object's uniquePaymentStorageClientProviderKeys - uniquePaymentStoragesCliProKeys := userPaymentStorage.GetUniquePaymentStorageClientProviderKeys() + uniquePaymentStoragesCliProKeys := providerPaymentStorage.GetUniquePaymentStorageClientProviderKeys() for _, uniquePaymentStorageKey := range uniquePaymentStoragesCliProKeys { // get the uniquePaymentStorageClientProvider object uniquePaymentStorage, found := k.GetUniquePaymentStorageClientProvider(ctx, uniquePaymentStorageKey) @@ -156,12 +163,28 @@ func (k Keeper) RemoveAllEpochPaymentsForBlock(ctx sdk.Context, blockForDelete u // delete the uniquePaymentStorageClientProvider object k.RemoveUniquePaymentStorageClientProvider(ctx, uniquePaymentStorage.Index) + consumer := k.GetConsumerFromUniquePayment(&uniquePaymentStorage) + + provider, err := k.GetProviderFromProviderPaymentStorage(&providerPaymentStorage) + if err != nil { + utils.LavaFormatError("failed getting provider from payment storage", err) + continue + } + coupling := couplingConsumerProvider{consumer: consumer, provider: provider} + if _, ok := couplingUsage[coupling]; !ok { + // only add it if it doesn't exist + iterationOrder = append(iterationOrder, coupling) + } + consumerUsage[consumer] += uniquePaymentStorage.UsedCU + couplingUsage[coupling] += uniquePaymentStorage.UsedCU } // after we're done deleting the uniquePaymentStorageClientProvider objects, delete the providerPaymentStorage object - k.RemoveProviderPaymentStorage(ctx, userPaymentStorage.Index) + k.RemoveProviderPaymentStorage(ctx, providerPaymentStorage.Index) + } + for _, coupling := range iterationOrder { + k.subscriptionKeeper.AppendAdjustment(ctx, coupling.consumer, coupling.provider, consumerUsage[coupling.consumer], couplingUsage[coupling]) } - // after we're done deleting the providerPaymentStorage objects, delete the epochPayments object k.RemoveEpochPayments(ctx, key) } diff --git a/x/pairing/keeper/msg_server_relay_payment_test.go b/x/pairing/keeper/msg_server_relay_payment_test.go index 98fb06cac4..5bf9b5c3f2 100644 --- a/x/pairing/keeper/msg_server_relay_payment_test.go +++ b/x/pairing/keeper/msg_server_relay_payment_test.go @@ -611,7 +611,7 @@ func TestBadgeValidation(t *testing.T) { ts.AdvanceBlock() // remove past payments to avoid double spending (first error payment succeeded) - ts.Keepers.Pairing.RemoveAllEpochPaymentsForBlock(ts.Ctx, tt.epoch) + ts.Keepers.Pairing.RemoveAllEpochPaymentsForBlockAppendAdjustments(ts.Ctx, tt.epoch) } badge := types.CreateBadge(badgeCuAllocation, tt.epoch, tt.badgeAddress, tt.lavaChainID, []byte{}) diff --git a/x/pairing/keeper/provider_payment_storage.go b/x/pairing/keeper/provider_payment_storage.go index b9c42d97bf..c11cb962f7 100644 --- a/x/pairing/keeper/provider_payment_storage.go +++ b/x/pairing/keeper/provider_payment_storage.go @@ -3,6 +3,7 @@ package keeper import ( "fmt" "strconv" + "strings" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -63,6 +64,15 @@ func (k Keeper) GetAllProviderPaymentStorage(ctx sdk.Context) (list []types.Prov return } +func (k Keeper) GetProviderFromProviderPaymentStorage(providerPaymentStorage *types.ProviderPaymentStorage) (string, error) { + index := providerPaymentStorage.Index + // index consists of chain_epoch_providerAddress + lastIndex := strings.LastIndex(index, "_") + if lastIndex != -1 { + return index[lastIndex+1:], nil + } + return "", fmt.Errorf("invalid provider payment storage key %s", index) +} // Function to get a providerPaymentStorage object's key (key is chainID_epoch_providerAddress, epoch in hex representation) func (k Keeper) GetProviderPaymentStorageKey(ctx sdk.Context, chainID string, epoch uint64, providerAddress sdk.AccAddress) string { diff --git a/x/pairing/types/expected_keepers.go b/x/pairing/types/expected_keepers.go index 31c1eb7601..261d47f3a1 100644 --- a/x/pairing/types/expected_keepers.go +++ b/x/pairing/types/expected_keepers.go @@ -88,6 +88,7 @@ type SubscriptionKeeper interface { CalcTotalMonthlyReward(ctx sdk.Context, totalAmount math.Int, trackedCu uint64, totalCuUsedBySub uint64) math.Int AddTrackedCu(ctx sdk.Context, sub string, provider string, chainID string, cu uint64, block uint64) error GetAllSubscriptionsIndices(ctx sdk.Context) []string + AppendAdjustment(ctx sdk.Context, consumer string, provider string, totalConsumerUsage uint64, usageWithThisProvider uint64) } type PlanKeeper interface { diff --git a/x/subscription/keeper/adjustment.go b/x/subscription/keeper/adjustment.go new file mode 100644 index 0000000000..211a67a7d3 --- /dev/null +++ b/x/subscription/keeper/adjustment.go @@ -0,0 +1,159 @@ +package keeper + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/utils" + "github.com/lavanet/lava/x/subscription/types" +) + +// SetUniquePaymentStorageClientProvider set a specific uniquePaymentStorageClientProvider in the store from its index +func (k Keeper) SetAdjustment(ctx sdk.Context, adjustment types.Adjustment) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) + b := k.cdc.MustMarshal(&adjustment) + store.Set(types.AdjustmentKey( + adjustment.Index, + ), b) +} + +// GetUniquePaymentStorageClientProvider returns a uniquePaymentStorageClientProvider from its index +func (k Keeper) GetAdjustment( + ctx sdk.Context, + index string, +) (val types.Adjustment, found bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) + b := store.Get(types.AdjustmentKey( + index, + )) + if b == nil { + return val, false + } + + k.cdc.MustUnmarshal(b, &val) + return val, true +} + +// RemoveUniquePaymentStorageClientProvider removes a uniquePaymentStorageClientProvider from the store +func (k Keeper) RemoveAdjustment( + ctx sdk.Context, + index string, +) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) + store.Delete(types.AdjustmentKey( + index, + )) +} + +// GetAllUniquePaymentStorageClientProvider returns all uniquePaymentStorageClientProvider +func (k Keeper) GetAllAdjustment(ctx sdk.Context) (list []types.Adjustment) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) + iterator := sdk.KVStorePrefixIterator(store, []byte{}) + + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var val types.Adjustment + k.cdc.MustUnmarshal(iterator.Value(), &val) + list = append(list, val) + } + + return +} + +func (k Keeper) GetProviderFromAdjustment(adjustment *types.Adjustment) (string, error) { + index := adjustment.Index + // index consists of chain_epoch_providerAddress + lastIndex := strings.LastIndex(index, "_") + if lastIndex != -1 { + return index[lastIndex+1:], nil + } + return "", fmt.Errorf("invalid adjustment key %s", index) +} + +func (k Keeper) AdjustmentIndex(consumer string, provider string) string { + // consumer must come first for the deleteConsumer iteration + return consumer + "_" + provider +} + +func (k Keeper) AppendAdjustment(ctx sdk.Context, consumer string, provider string, totalConsumerUsage uint64, usageWithThisProvider uint64) { + index := k.AdjustmentIndex(consumer, provider) + adjustment, _ := k.GetAdjustment(ctx, index) + // adjustment = weighted average(adjustment/epoch) + // this epoch adjustment = usageWithThisProvider / totalConsumerUsage + // adjustment = sum(epoch_adjustment * cu_used_this_epoch) / total_used_cu + // so we need to save: + // 1. sum(epoch_adjustment * cu_used_this_epoch) = sum(usageWithThisProvider) + // 2. total_used_cu = sum(totalConsumerUsage) + + // we need to append, in both cases of existing adjustment or a not found one + adjustment.TotalUsage += totalConsumerUsage + adjustment.AdjustedUsage += usageWithThisProvider + adjustment.Index = index + k.SetAdjustment(ctx, adjustment) +} + +func (k Keeper) GetConsumerAdjustments(ctx sdk.Context, consumer string) (list []types.Adjustment) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) + // set consumer prefix + iterator := sdk.KVStorePrefixIterator(store, []byte(consumer)) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var val types.Adjustment + k.cdc.MustUnmarshal(iterator.Value(), &val) + list = append(list, val) + } + return +} + +// assumes consumer comes first in the key, when querying by subscription it will catch all +func (k Keeper) RemoveConsumerAdjustments(ctx sdk.Context, consumer string) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) + // set consumer prefix + iterator := sdk.KVStorePrefixIterator(store, []byte(consumer)) + defer iterator.Close() + + keysToDelete := []string{} + for ; iterator.Valid(); iterator.Next() { + keysToDelete = append(keysToDelete, string(iterator.Key())) + } + for _, key := range keysToDelete { + k.RemoveAdjustment(ctx, key) + } +} + +func (k Keeper) GetAdjustmentFactorProvider(ctx sdk.Context, adjustments []types.Adjustment) map[string]sdk.Dec { + type usage struct { + total int64 + adjusted int64 + } + providers := []string{} + providerUsage := map[string]usage{} + for _, adjustment := range adjustments { + provider, err := k.GetProviderFromAdjustment(&adjustment) + if err != nil { + utils.LavaFormatError("could not get provider from adjustment", err) + continue + } + usage := providerUsage[provider] + usage.adjusted += int64(adjustment.AdjustedUsage) + usage.total += int64(adjustment.TotalUsage) + providerUsage[provider] = usage + providers = append(providers, provider) + } + + providerAdjustment := map[string]sdk.Dec{} + // we use providers list to iterate deterministically + for _, provider := range providers { + if _, ok := providerAdjustment[provider]; !ok { + totalUsage := providerUsage[provider].total + totalAdjustedUsage := providerUsage[provider].adjusted + // indexes may repeat but we only need to handle each provider once + sdk.OneDec().MulInt64(int64(totalAdjustedUsage)).QuoInt64(int64(totalUsage)) + } + } + return providerAdjustment +} diff --git a/x/subscription/keeper/cu_tracker.go b/x/subscription/keeper/cu_tracker.go index 778ea10449..d1f52ccd3b 100644 --- a/x/subscription/keeper/cu_tracker.go +++ b/x/subscription/keeper/cu_tracker.go @@ -149,6 +149,11 @@ func (k Keeper) RewardAndResetCuTracker(ctx sdk.Context, cuTrackerTimerKeyBytes totalTokenAmount = sdk.NewIntFromUint64(LIMIT_TOKEN_PER_CU * totalCuTracked) } + // get the adjustment factor, and delete the entries + adjustments := k.GetConsumerAdjustments(ctx, sub) + adjustmentFactorForProvider := k.GetAdjustmentFactorProvider(ctx, adjustments) + k.RemoveConsumerAdjustments(ctx, sub) + for _, trackedCuInfo := range trackedCuList { trackedCu := trackedCuInfo.trackedCu provider := trackedCuInfo.provider @@ -168,7 +173,12 @@ func (k Keeper) RewardAndResetCuTracker(ctx sdk.Context, cuTrackerTimerKeyBytes // provider monthly reward = (tracked_CU / total_CU_used_in_sub_this_month) * plan_price // TODO: deal with the reward's remainder (uint division...) - + providerAdjustment, ok := adjustmentFactorForProvider[provider] + if !ok { + providerAdjustment = math.LegacyZeroDec() + } + // TODO: send the adjustment to rewards module and cap adjustment by rewards module params + _ = providerAdjustment totalMonthlyReward := k.CalcTotalMonthlyReward(ctx, totalTokenAmount, trackedCu, totalCuTracked) // calculate the provider reward (smaller than totalMonthlyReward diff --git a/x/subscription/types/adjustment.pb.go b/x/subscription/types/adjustment.pb.go new file mode 100644 index 0000000000..9c006ccb0c --- /dev/null +++ b/x/subscription/types/adjustment.pb.go @@ -0,0 +1,389 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: lavanet/lava/subscription/adjustment.proto + +package types + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Adjustment struct { + Index string `protobuf:"bytes,1,opt,name=index,proto3" json:"index,omitempty"` + AdjustedUsage uint64 `protobuf:"varint,2,opt,name=adjustedUsage,proto3" json:"adjustedUsage,omitempty"` + TotalUsage uint64 `protobuf:"varint,3,opt,name=totalUsage,proto3" json:"totalUsage,omitempty"` +} + +func (m *Adjustment) Reset() { *m = Adjustment{} } +func (m *Adjustment) String() string { return proto.CompactTextString(m) } +func (*Adjustment) ProtoMessage() {} +func (*Adjustment) Descriptor() ([]byte, []int) { + return fileDescriptor_6061843cba96837b, []int{0} +} +func (m *Adjustment) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Adjustment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Adjustment.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Adjustment) XXX_Merge(src proto.Message) { + xxx_messageInfo_Adjustment.Merge(m, src) +} +func (m *Adjustment) XXX_Size() int { + return m.Size() +} +func (m *Adjustment) XXX_DiscardUnknown() { + xxx_messageInfo_Adjustment.DiscardUnknown(m) +} + +var xxx_messageInfo_Adjustment proto.InternalMessageInfo + +func (m *Adjustment) GetIndex() string { + if m != nil { + return m.Index + } + return "" +} + +func (m *Adjustment) GetAdjustedUsage() uint64 { + if m != nil { + return m.AdjustedUsage + } + return 0 +} + +func (m *Adjustment) GetTotalUsage() uint64 { + if m != nil { + return m.TotalUsage + } + return 0 +} + +func init() { + proto.RegisterType((*Adjustment)(nil), "lavanet.lava.subscription.Adjustment") +} + +func init() { + proto.RegisterFile("lavanet/lava/subscription/adjustment.proto", fileDescriptor_6061843cba96837b) +} + +var fileDescriptor_6061843cba96837b = []byte{ + // 195 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xca, 0x49, 0x2c, 0x4b, + 0xcc, 0x4b, 0x2d, 0xd1, 0x07, 0xd1, 0xfa, 0xc5, 0xa5, 0x49, 0xc5, 0xc9, 0x45, 0x99, 0x05, 0x25, + 0x99, 0xf9, 0x79, 0xfa, 0x89, 0x29, 0x59, 0xa5, 0xc5, 0x25, 0xb9, 0xa9, 0x79, 0x25, 0x7a, 0x05, + 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0xb5, 0x7a, 0x20, 0x5a, 0x0f, 0x59, 0xad, 0x52, 0x06, + 0x17, 0x97, 0x23, 0x5c, 0xb9, 0x90, 0x08, 0x17, 0x6b, 0x66, 0x5e, 0x4a, 0x6a, 0x85, 0x04, 0xa3, + 0x02, 0xa3, 0x06, 0x67, 0x10, 0x84, 0x23, 0xa4, 0xc2, 0xc5, 0x0b, 0x31, 0x32, 0x35, 0x25, 0xb4, + 0x38, 0x31, 0x3d, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0x25, 0x08, 0x55, 0x50, 0x48, 0x8e, 0x8b, + 0xab, 0x24, 0xbf, 0x24, 0x31, 0x07, 0xa2, 0x84, 0x19, 0xac, 0x04, 0x49, 0xc4, 0xc9, 0xed, 0xc4, + 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, + 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x74, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, + 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x51, 0x7c, 0x55, 0x81, 0xea, 0xaf, 0x92, 0xca, 0x82, 0xd4, 0xe2, + 0x24, 0x36, 0xb0, 0x9f, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x87, 0x8e, 0x73, 0xb8, 0x01, + 0x01, 0x00, 0x00, +} + +func (m *Adjustment) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Adjustment) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Adjustment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TotalUsage != 0 { + i = encodeVarintAdjustment(dAtA, i, uint64(m.TotalUsage)) + i-- + dAtA[i] = 0x18 + } + if m.AdjustedUsage != 0 { + i = encodeVarintAdjustment(dAtA, i, uint64(m.AdjustedUsage)) + i-- + dAtA[i] = 0x10 + } + if len(m.Index) > 0 { + i -= len(m.Index) + copy(dAtA[i:], m.Index) + i = encodeVarintAdjustment(dAtA, i, uint64(len(m.Index))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAdjustment(dAtA []byte, offset int, v uint64) int { + offset -= sovAdjustment(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Adjustment) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Index) + if l > 0 { + n += 1 + l + sovAdjustment(uint64(l)) + } + if m.AdjustedUsage != 0 { + n += 1 + sovAdjustment(uint64(m.AdjustedUsage)) + } + if m.TotalUsage != 0 { + n += 1 + sovAdjustment(uint64(m.TotalUsage)) + } + return n +} + +func sovAdjustment(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAdjustment(x uint64) (n int) { + return sovAdjustment(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Adjustment) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAdjustment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Adjustment: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Adjustment: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAdjustment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAdjustment + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAdjustment + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Index = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AdjustedUsage", wireType) + } + m.AdjustedUsage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAdjustment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AdjustedUsage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalUsage", wireType) + } + m.TotalUsage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAdjustment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalUsage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAdjustment(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAdjustment + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAdjustment(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAdjustment + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAdjustment + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAdjustment + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAdjustment + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAdjustment + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAdjustment + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAdjustment = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAdjustment = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAdjustment = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/subscription/types/key_adjustment.go b/x/subscription/types/key_adjustment.go new file mode 100644 index 0000000000..9e170a8e19 --- /dev/null +++ b/x/subscription/types/key_adjustment.go @@ -0,0 +1,23 @@ +package types + +import "encoding/binary" + +var _ binary.ByteOrder + +const ( + // AdjustmentKeyPrefix is the prefix to retrieve all Adjustment + AdjustmentKeyPrefix = "Adjustment/value/" +) + +// AdjustmentKey returns the store key to retrieve an Adjustment from the index fields +func AdjustmentKey( + index string, +) []byte { + var key []byte + + indexBytes := []byte(index) + key = append(key, indexBytes...) + key = append(key, []byte("/")...) + + return key +} diff --git a/x/subscription/types/keys.go b/x/subscription/types/keys.go index f32c231963..b6566263b9 100644 --- a/x/subscription/types/keys.go +++ b/x/subscription/types/keys.go @@ -43,3 +43,7 @@ func DecodeCuTrackerKey(key string) (sub string, provider string, chainID string decodedKey := strings.Split(key, " ") return decodedKey[0], decodedKey[1], decodedKey[2] } + +func KeyPrefix(p string) []byte { + return []byte(p) +} From 0d8ad52db9179c16af770592b5a47e3130239244 Mon Sep 17 00:00:00 2001 From: omerlavanet Date: Tue, 19 Dec 2023 11:01:28 +0200 Subject: [PATCH 02/11] added adjutment to the event --- x/subscription/keeper/cu_tracker.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/x/subscription/keeper/cu_tracker.go b/x/subscription/keeper/cu_tracker.go index d1f52ccd3b..9e1a823109 100644 --- a/x/subscription/keeper/cu_tracker.go +++ b/x/subscription/keeper/cu_tracker.go @@ -212,13 +212,14 @@ func (k Keeper) RewardAndResetCuTracker(ctx sdk.Context, cuTrackerTimerKeyBytes return } else { utils.LogLavaEvent(ctx, k.Logger(ctx), types.MonthlyCuTrackerProviderRewardEventName, map[string]string{ - "provider": provider, - "sub": sub, - "plan": plan.Index, - "tracked_cu": strconv.FormatUint(trackedCu, 10), - "plan_price": plan.Price.String(), - "reward": providerReward.String(), - "block": strconv.FormatInt(ctx.BlockHeight(), 10), + "provider": provider, + "sub": sub, + "plan": plan.Index, + "tracked_cu": strconv.FormatUint(trackedCu, 10), + "plan_price": plan.Price.String(), + "reward": providerReward.String(), + "block": strconv.FormatInt(ctx.BlockHeight(), 10), + "adjustment_raw": providerAdjustment.String(), }, "Provider got monthly reward successfully") } } From 2be684254eb659bbb2097499b2d22f3bc427d979 Mon Sep 17 00:00:00 2001 From: omerlavanet Date: Tue, 19 Dec 2023 19:47:47 +0200 Subject: [PATCH 03/11] tested adjustments --- protocol/chainlib/common_test_utils.go | 2 +- testutil/common/common.go | 2 +- testutil/common/tester.go | 79 ++++++++++++- x/projects/keeper/project_test.go | 6 +- x/subscription/keeper/adjustment.go | 31 ++--- x/subscription/keeper/adjustment_test.go | 137 +++++++++++++++++++++++ x/subscription/types/key_adjustment.go | 13 --- 7 files changed, 233 insertions(+), 37 deletions(-) create mode 100644 x/subscription/keeper/adjustment_test.go diff --git a/protocol/chainlib/common_test_utils.go b/protocol/chainlib/common_test_utils.go index c73f79043e..1b76c1ec32 100644 --- a/protocol/chainlib/common_test_utils.go +++ b/protocol/chainlib/common_test_utils.go @@ -211,7 +211,7 @@ func SetupForTests(t *testing.T, numOfProviders int, specID string, getToTopMost var stake int64 = 50000000000 // subscribe consumer - testcommon.BuySubscription(t, ts.Ctx, *ts.Keepers, *ts.Servers, ts.Consumer, ts.Plan.Index) + testcommon.BuySubscription(ts.Ctx, *ts.Keepers, *ts.Servers, ts.Consumer, ts.Plan.Index) // stake providers for _, provider := range ts.Providers { diff --git a/testutil/common/common.go b/testutil/common/common.go index b3b7c6a381..ccd7619643 100644 --- a/testutil/common/common.go +++ b/testutil/common/common.go @@ -33,7 +33,7 @@ func StakeAccount(t *testing.T, ctx context.Context, keepers testkeeper.Keepers, require.Nil(t, err) } -func BuySubscription(t *testing.T, ctx context.Context, keepers testkeeper.Keepers, servers testkeeper.Servers, acc sigs.Account, plan string) { +func BuySubscription(ctx context.Context, keepers testkeeper.Keepers, servers testkeeper.Servers, acc sigs.Account, plan string) { servers.SubscriptionServer.Buy(ctx, &subscriptiontypes.MsgBuy{Creator: acc.Addr.String(), Consumer: acc.Addr.String(), Index: plan, Duration: 1}) } diff --git a/testutil/common/tester.go b/testutil/common/tester.go index 2719072f60..f6a4768f6a 100644 --- a/testutil/common/tester.go +++ b/testutil/common/tester.go @@ -43,9 +43,10 @@ type Tester struct { } const ( - PROVIDER string = "provider" - CONSUMER string = "consumer" - VALIDATOR string = "validator" + PROVIDER string = "provider_" + CONSUMER string = "consumer_" + VALIDATOR string = "validator_" + DEVELOPER string = "developer_" ) func NewTester(t *testing.T) *Tester { @@ -158,13 +159,16 @@ func (ts *Tester) StakeProviderExtra( // if necessary, generate mock endpoints if endpoints == nil { - apiInterface := spec.ApiCollections[0].CollectionData.ApiInterface + apiInterfaces := []string{} + for _, apiCollection := range spec.ApiCollections { + apiInterfaces = append(apiInterfaces, apiCollection.CollectionData.ApiInterface) + } geolocations := planstypes.GetGeolocationsFromUint(geoloc) for _, geo := range geolocations { endpoint := epochstoragetypes.Endpoint{ IPPORT: "123", - ApiInterfaces: []string{apiInterface}, + ApiInterfaces: apiInterfaces, Geolocation: int32(geo), } endpoints = append(endpoints, endpoint) @@ -172,7 +176,7 @@ func (ts *Tester) StakeProviderExtra( } stake := sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(amount)) - _, err := ts.TxPairingStakeProvider(addr, spec.Name, stake, endpoints, geoloc, moniker) + _, err := ts.TxPairingStakeProvider(addr, spec.Index, stake, endpoints, geoloc, moniker) return err } @@ -967,9 +971,72 @@ func (ts *Tester) AdvanceMonthsFrom(from time.Time, months int) *Tester { } return ts } +func (ts *Tester) BondDenom() string { + return ts.Keepers.StakingKeeper.BondDenom(sdk.UnwrapSDKContext(ts.Ctx)) +} // AdvanceMonth advanced blocks by given months, like AdvanceMonthsFrom, // starting from the current block's timestamp func (ts *Tester) AdvanceMonths(months int) *Tester { return ts.AdvanceMonthsFrom(ts.BlockTime(), months) } + +func (ts *Tester) SetupForTests(getToTopMostPath string, specId string, validators int, subscriptions int, projectsInSubscription int, providers int) error { + var balance int64 = 100000000000 + + start := len(ts.Accounts(VALIDATOR)) + for i := 0; i < validators; i++ { + acc, _ := ts.AddAccount(VALIDATOR, start+i, balance) + ts.TxCreateValidator(acc, math.NewInt(balance)) + } + + sdkContext := sdk.UnwrapSDKContext(ts.Ctx) + spec, err := testkeeper.GetASpec(specId, getToTopMostPath, &sdkContext, &ts.Keepers.Spec) + if err != nil { + return err + } + ts.AddSpec(spec.Index, spec) + ts.Keepers.Spec.SetSpec(sdk.UnwrapSDKContext(ts.Ctx), spec) + start = len(ts.Accounts(CONSUMER)) + for i := 0; i < subscriptions; i++ { + // setup consumer + consumerAcc, consumerAddress := ts.AddAccount(CONSUMER, start+i, balance) + ts.AddPlan("free", CreateMockPlan()) + ts.AddPolicy("mock", CreateMockPolicy()) + plan := ts.plans["free"] + // subscribe consumer + BuySubscription(ts.Ctx, *ts.Keepers, *ts.Servers, consumerAcc, plan.Index) + // create projects: + _, pd2both := ts.AddAccount(DEVELOPER, start+i, 10000) + keys_1_admin_dev := []projectstypes.ProjectKey{ + projectstypes.NewProjectKey(pd2both). + AddType(projectstypes.ProjectKey_ADMIN). + AddType(projectstypes.ProjectKey_DEVELOPER), + } + policy := ts.Policy("mock") + pd := projectstypes.ProjectData{ + Name: "proj", + Enabled: true, + ProjectKeys: keys_1_admin_dev, + Policy: &policy, + } + ts.AddProjectData("projdata", pd) + err = ts.Keepers.Projects.CreateProject(ts.Ctx, consumerAddress, pd, plan) + if err != nil { + return err + } + } + // setup providers + start = len(ts.Accounts(PROVIDER)) + for i := 0; i < providers; i++ { + _, addr := ts.AddAccount(PROVIDER, start+i, balance) + err := ts.StakeProviderExtra(addr, spec, spec.MinStakeProvider.Amount.Int64(), nil, 1, "prov"+strconv.Itoa(start+i)) + if err != nil { + return err + } + } + + // advance for the staking to be valid + ts.AdvanceEpoch() + return nil +} diff --git a/x/projects/keeper/project_test.go b/x/projects/keeper/project_test.go index 16d39918e6..c916a965c0 100644 --- a/x/projects/keeper/project_test.go +++ b/x/projects/keeper/project_test.go @@ -1139,9 +1139,9 @@ func TestSetPolicyByGeolocation(t *testing.T) { basicUser := common.CreateNewAccount(_ctx, *keepers, 10000) premiumUser := common.CreateNewAccount(_ctx, *keepers, 10000) - common.BuySubscription(t, _ctx, *keepers, *servers, freeUser, freePlan.Index) - common.BuySubscription(t, _ctx, *keepers, *servers, basicUser, basicPlan.Index) - common.BuySubscription(t, _ctx, *keepers, *servers, premiumUser, premiumPlan.Index) + common.BuySubscription(_ctx, *keepers, *servers, freeUser, freePlan.Index) + common.BuySubscription(_ctx, *keepers, *servers, basicUser, basicPlan.Index) + common.BuySubscription(_ctx, *keepers, *servers, premiumUser, premiumPlan.Index) templates := []struct { name string diff --git a/x/subscription/keeper/adjustment.go b/x/subscription/keeper/adjustment.go index 211a67a7d3..2c6cef38dd 100644 --- a/x/subscription/keeper/adjustment.go +++ b/x/subscription/keeper/adjustment.go @@ -14,9 +14,7 @@ import ( func (k Keeper) SetAdjustment(ctx sdk.Context, adjustment types.Adjustment) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) b := k.cdc.MustMarshal(&adjustment) - store.Set(types.AdjustmentKey( - adjustment.Index, - ), b) + store.Set([]byte(adjustment.Index), b) } // GetUniquePaymentStorageClientProvider returns a uniquePaymentStorageClientProvider from its index @@ -25,9 +23,7 @@ func (k Keeper) GetAdjustment( index string, ) (val types.Adjustment, found bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) - b := store.Get(types.AdjustmentKey( - index, - )) + b := store.Get([]byte(index)) if b == nil { return val, false } @@ -42,9 +38,7 @@ func (k Keeper) RemoveAdjustment( index string, ) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) - store.Delete(types.AdjustmentKey( - index, - )) + store.Delete([]byte(index)) } // GetAllUniquePaymentStorageClientProvider returns all uniquePaymentStorageClientProvider @@ -85,12 +79,23 @@ func (k Keeper) AppendAdjustment(ctx sdk.Context, consumer string, provider stri // this epoch adjustment = usageWithThisProvider / totalConsumerUsage // adjustment = sum(epoch_adjustment * cu_used_this_epoch) / total_used_cu // so we need to save: - // 1. sum(epoch_adjustment * cu_used_this_epoch) = sum(usageWithThisProvider) + // 1. sum(epoch_adjustment * total_cu_used_this_epoch) // 2. total_used_cu = sum(totalConsumerUsage) + maxRewardsBoost := int64(5) // TODO: yarom, this needs to be read from rewards module + + // check for adjustment limits: adjustment = min(1,1/rewardsMaxBoost * epoch_sum_cu/cu_with_provider) + if totalConsumerUsage >= uint64(maxRewardsBoost)*usageWithThisProvider { + // epoch adjustment is 1 + adjustment.TotalUsage += totalConsumerUsage + adjustment.AdjustedUsage += totalConsumerUsage + } else { + // totalConsumerUsage < uint64(maxRewardsBoost)*usageWithThisProvider + adjustment.TotalUsage += totalConsumerUsage + // epoch adjustment is (1/maxRewardsBoost * totalConsumerUsage/usageWithThisProvider) * totalConsumerUsage + adjustment.AdjustedUsage += (totalConsumerUsage / uint64(maxRewardsBoost)) * (totalConsumerUsage / usageWithThisProvider) + } // we need to append, in both cases of existing adjustment or a not found one - adjustment.TotalUsage += totalConsumerUsage - adjustment.AdjustedUsage += usageWithThisProvider adjustment.Index = index k.SetAdjustment(ctx, adjustment) } @@ -152,7 +157,7 @@ func (k Keeper) GetAdjustmentFactorProvider(ctx sdk.Context, adjustments []types totalUsage := providerUsage[provider].total totalAdjustedUsage := providerUsage[provider].adjusted // indexes may repeat but we only need to handle each provider once - sdk.OneDec().MulInt64(int64(totalAdjustedUsage)).QuoInt64(int64(totalUsage)) + providerAdjustment[provider] = sdk.OneDec().MulInt64(int64(totalAdjustedUsage)).QuoInt64(int64(totalUsage)) } } return providerAdjustment diff --git a/x/subscription/keeper/adjustment_test.go b/x/subscription/keeper/adjustment_test.go new file mode 100644 index 0000000000..c6b32ee91f --- /dev/null +++ b/x/subscription/keeper/adjustment_test.go @@ -0,0 +1,137 @@ +package keeper_test + +import ( + "testing" + + "cosmossdk.io/math" + "github.com/lavanet/lava/testutil/common" + "github.com/stretchr/testify/require" +) + +func TestAdjustment(t *testing.T) { + ts := common.NewTester(t) + err := ts.SetupForTests("../../../", "LAV1", 1, 2, 1, 2) + require.NoError(t, err) + _, consumer1Addr := ts.GetAccount(common.CONSUMER, 0) + projects := ts.Keepers.Projects.GetAllProjectsForSubscription(ts.Ctx, consumer1Addr) + require.Len(t, projects, 2) + _, consumer2Addr := ts.GetAccount(common.CONSUMER, 1) + projects = append(projects, ts.Keepers.Projects.GetAllProjectsForSubscription(ts.Ctx, consumer2Addr)...) + require.Len(t, projects, 4) + _, provider1 := ts.GetAccount(common.PROVIDER, 0) + _, provider2 := ts.GetAccount(common.PROVIDER, 1) + type indexConsumer struct { + index int + proj string + } + usage := map[indexConsumer]struct { + provider1Usage uint64 + provider2Usage uint64 + }{ + { + index: 0, + proj: projects[0], + }: { + provider1Usage: 90, + provider2Usage: 10, + }, + { + index: 1, + proj: projects[0], + }: { + provider1Usage: 180, + provider2Usage: 20, + }, + { + index: 0, + proj: projects[1], + }: { + provider1Usage: 90, + provider2Usage: 10, + }, + { + index: 1, + proj: projects[1], + }: { + provider1Usage: 180, + provider2Usage: 20, + }, + { + index: 0, + proj: projects[2], + }: { + provider1Usage: 50, + provider2Usage: 50, + }, + { + index: 1, + proj: projects[2], + }: { + provider1Usage: 100, + provider2Usage: 100, + }, + { + index: 0, + proj: projects[3], + }: { + provider1Usage: 50, + provider2Usage: 50, + }, + { + index: 1, + proj: projects[3], + }: { + provider1Usage: 100, + provider2Usage: 1, + }, + } + + for idxConsumer, cus := range usage { + totalUsageThisIndex := cus.provider1Usage + cus.provider2Usage + ts.Keepers.Subscription.AppendAdjustment(ts.Ctx, idxConsumer.proj, provider1, totalUsageThisIndex, cus.provider1Usage) + ts.Keepers.Subscription.AppendAdjustment(ts.Ctx, idxConsumer.proj, provider2, totalUsageThisIndex, cus.provider2Usage) + } + + allAdjustments := ts.Keepers.Subscription.GetAllAdjustment(ts.Ctx) + require.Len(t, allAdjustments, 8) + for _, adjustment := range allAdjustments { + provider, err := ts.Keepers.Subscription.GetProviderFromAdjustment(&adjustment) + require.NoError(t, err) + require.True(t, provider == provider1 || provider == provider2) + } + + consumerAdjustments := ts.Keepers.Subscription.GetConsumerAdjustments(ts.Ctx, consumer1Addr) + require.Len(t, consumerAdjustments, 4) + seenProviders := map[string]struct{}{} + for _, adjustment := range allAdjustments { + provider, err := ts.Keepers.Subscription.GetProviderFromAdjustment(&adjustment) + require.NoError(t, err) + require.True(t, provider == provider1 || provider == provider2) + seenProviders[provider] = struct{}{} + } + require.Len(t, seenProviders, 2) + + providersFactors := ts.Keepers.Subscription.GetAdjustmentFactorProvider(ts.Ctx, consumerAdjustments) + require.Len(t, providersFactors, 2) + ts.Keepers.Subscription.RemoveConsumerAdjustments(ts.Ctx, consumer1Addr) + allAdjustments = ts.Keepers.Subscription.GetAllAdjustment(ts.Ctx) + require.Len(t, allAdjustments, 4) + + consumerAdjustments = ts.Keepers.Subscription.GetConsumerAdjustments(ts.Ctx, consumer2Addr) + require.Len(t, consumerAdjustments, 4) + providersFactors2 := ts.Keepers.Subscription.GetAdjustmentFactorProvider(ts.Ctx, consumerAdjustments) + require.Len(t, providersFactors2, 2) + ts.Keepers.Subscription.RemoveConsumerAdjustments(ts.Ctx, consumer2Addr) + allAdjustments = ts.Keepers.Subscription.GetAllAdjustment(ts.Ctx) + require.Len(t, allAdjustments, 0) + + // check adjustment values: + //consuemr 1 + require.True(t, providersFactors[provider1].Equal(math.LegacyMustNewDecFromStr("0.2")), providersFactors[provider1].String()) + require.True(t, providersFactors[provider2].Equal(math.LegacyMustNewDecFromStr("1")), providersFactors[provider2].String()) + // consumer2 + // 120/300 = 0.4, ((100/5) + 0.4 * 100)/201 = 0.29850746, 0.4*300 + 0.29850746*201 = (60+120) / 501 = 0.35928144 + require.True(t, providersFactors2[provider1].Equal(math.LegacyMustNewDecFromStr("180").QuoInt64(501)), providersFactors2[provider1].String()) + // 120/300, 141/201 = 261/501 + require.True(t, providersFactors2[provider2].Equal(math.LegacyMustNewDecFromStr("261").QuoInt64(501)), providersFactors2[provider2].String()) +} diff --git a/x/subscription/types/key_adjustment.go b/x/subscription/types/key_adjustment.go index 9e170a8e19..c55e8dc4dd 100644 --- a/x/subscription/types/key_adjustment.go +++ b/x/subscription/types/key_adjustment.go @@ -8,16 +8,3 @@ const ( // AdjustmentKeyPrefix is the prefix to retrieve all Adjustment AdjustmentKeyPrefix = "Adjustment/value/" ) - -// AdjustmentKey returns the store key to retrieve an Adjustment from the index fields -func AdjustmentKey( - index string, -) []byte { - var key []byte - - indexBytes := []byte(index) - key = append(key, indexBytes...) - key = append(key, []byte("/")...) - - return key -} From 3cff3fedb10dfbc15281ed60bf59f433baf9285e Mon Sep 17 00:00:00 2001 From: omerlavanet Date: Wed, 20 Dec 2023 12:03:41 +0200 Subject: [PATCH 04/11] added unitest for edge cases --- x/dualstaking/keeper/delegate.go | 2 +- x/dualstaking/keeper/hooks.go | 2 +- x/subscription/keeper/adjustment_test.go | 101 +++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/x/dualstaking/keeper/delegate.go b/x/dualstaking/keeper/delegate.go index 377886c31d..b568578e33 100644 --- a/x/dualstaking/keeper/delegate.go +++ b/x/dualstaking/keeper/delegate.go @@ -603,7 +603,7 @@ func (k Keeper) VerifyDelegatorBalance(ctx sdk.Context, delAddr sdk.AccAddress) for _, d := range delegations { v, found := k.stakingKeeper.GetValidator(ctx, d.GetValidatorAddr()) if found { - sumValidatorDelegations = sumValidatorDelegations.Add(v.TokensFromSharesRoundUp(d.Shares).TruncateInt()) + sumValidatorDelegations = sumValidatorDelegations.Add(v.TokensFromSharesRoundUp(d.Shares).Ceil().TruncateInt()) } } diff --git a/x/dualstaking/keeper/hooks.go b/x/dualstaking/keeper/hooks.go index 72b77c6a9c..ec1b2de0c4 100644 --- a/x/dualstaking/keeper/hooks.go +++ b/x/dualstaking/keeper/hooks.go @@ -154,7 +154,7 @@ func (h Hooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, if err != nil { return nil } - amount := validator.TokensFromSharesRoundUp(delegation.Shares).TruncateInt() + amount := validator.TokensFromSharesRoundUp(delegation.Shares).Ceil().TruncateInt() err = h.k.UnbondUniformProviders(ctx, delAddr.String(), sdk.NewCoin(h.k.stakingKeeper.BondDenom(ctx), amount)) if err != nil { return utils.LavaFormatError("delegation removed hook failed", err, diff --git a/x/subscription/keeper/adjustment_test.go b/x/subscription/keeper/adjustment_test.go index c6b32ee91f..a6612abe61 100644 --- a/x/subscription/keeper/adjustment_test.go +++ b/x/subscription/keeper/adjustment_test.go @@ -135,3 +135,104 @@ func TestAdjustment(t *testing.T) { // 120/300, 141/201 = 261/501 require.True(t, providersFactors2[provider2].Equal(math.LegacyMustNewDecFromStr("261").QuoInt64(501)), providersFactors2[provider2].String()) } + +func TestAdjustmentEdgeValues(t *testing.T) { + playbook := []struct { + name string + firstProviderCU uint64 + expectedAdjustmentDec string + }{ + { + name: "all-equal", + firstProviderCU: 1000, + expectedAdjustmentDec: "1", + }, + { + name: "very little", + firstProviderCU: 1, + expectedAdjustmentDec: "1", + }, + { + name: "top", + firstProviderCU: 1000000000000000000, + expectedAdjustmentDec: "0.2", + }, + } + for _, play := range playbook { + t.Run(play.name, func(t *testing.T) { + ts := common.NewTester(t) + err := ts.SetupForTests("../../../", "LAV1", 1, 1, 1, 5) + require.NoError(t, err) + _, consumer1Addr := ts.GetAccount(common.CONSUMER, 0) + projects := ts.Keepers.Projects.GetAllProjectsForSubscription(ts.Ctx, consumer1Addr) + require.Len(t, projects, 2) + providers := ts.Accounts(common.PROVIDER) + + usage := []struct { + providerUsage []uint64 + }{ + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + {providerUsage: []uint64{ + play.firstProviderCU, 1000, 1000, 1000, 1000, + }}, + } + + totalUsage := func(cus []uint64) uint64 { + res := uint64(0) + for _, cu := range cus { + res += cu + } + return res + } + + for _, usageProviders := range usage { + totalUsageThisIndex := totalUsage(usageProviders.providerUsage) + for providerIdx, cu := range usageProviders.providerUsage { + ts.Keepers.Subscription.AppendAdjustment(ts.Ctx, consumer1Addr, providers[providerIdx].Addr.String(), totalUsageThisIndex, cu) + } + } + + consumerAdjustments := ts.Keepers.Subscription.GetConsumerAdjustments(ts.Ctx, consumer1Addr) + require.NotEmpty(t, consumerAdjustments) + seenProviders := map[string]struct{}{} + for _, adjustment := range consumerAdjustments { + provider, err := ts.Keepers.Subscription.GetProviderFromAdjustment(&adjustment) + require.NoError(t, err) + seenProviders[provider] = struct{}{} + } + require.Len(t, seenProviders, len(providers)) + + providersFactors := ts.Keepers.Subscription.GetAdjustmentFactorProvider(ts.Ctx, consumerAdjustments) + require.Len(t, providersFactors, len(providers)) + prevFactor := providersFactors[providers[1].Addr.String()] + for idx, factor := range providersFactors { + if idx == providers[0].Addr.String() { + require.True(t, factor.Equal(math.LegacyMustNewDecFromStr(play.expectedAdjustmentDec)), factor.String()) + } else { + require.Equal(t, prevFactor, factor) + } + } + }) + } + +} From 691baded68ef6062d0cbb86383a854109d109565 Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Wed, 20 Dec 2023 12:34:46 +0000 Subject: [PATCH 05/11] integration --- x/rewards/keeper/providers.go | 4 ++-- x/subscription/keeper/adjustment.go | 14 ++++++++------ x/subscription/keeper/cu_tracker.go | 7 +++---- x/subscription/types/expected_keepers.go | 3 ++- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/x/rewards/keeper/providers.go b/x/rewards/keeper/providers.go index 322b0a0651..75c7ff0e98 100644 --- a/x/rewards/keeper/providers.go +++ b/x/rewards/keeper/providers.go @@ -8,10 +8,10 @@ import ( subscriptionTypes "github.com/lavanet/lava/x/subscription/types" ) -func (k Keeper) AggregateRewards(ctx sdk.Context, provider, chainid string, adjustmentDenom uint64, rewards math.Int) { +func (k Keeper) AggregateRewards(ctx sdk.Context, provider, chainid string, adjustment sdk.Dec, rewards math.Int) { index := types.BasePayIndex{Provider: provider, ChainID: chainid} basepay, found := k.getBasePay(ctx, index) - adjustedPay := sdk.NewDecFromInt(rewards).QuoInt64(int64(adjustmentDenom)) + adjustedPay := adjustment.MulInt(rewards) if !found { basepay = types.BasePay{Total: rewards, TotalAdjusted: adjustedPay} } else { diff --git a/x/subscription/keeper/adjustment.go b/x/subscription/keeper/adjustment.go index 794f682faf..7dab75eb3e 100644 --- a/x/subscription/keeper/adjustment.go +++ b/x/subscription/keeper/adjustment.go @@ -74,7 +74,10 @@ func (k Keeper) AdjustmentIndex(consumer string, provider string) string { func (k Keeper) AppendAdjustment(ctx sdk.Context, consumer string, provider string, totalConsumerUsage uint64, usageWithThisProvider uint64) { index := k.AdjustmentIndex(consumer, provider) - adjustment, _ := k.GetAdjustment(ctx, index) + adjustment, found := k.GetAdjustment(ctx, index) + if !found { + adjustment = types.Adjustment{Index: index, AdjustedUsage: 0, TotalUsage: 0} + } // adjustment = weighted average(adjustment/epoch) // this epoch adjustment = usageWithThisProvider / totalConsumerUsage // adjustment = sum(epoch_adjustment * cu_used_this_epoch) / total_used_cu @@ -82,10 +85,10 @@ func (k Keeper) AppendAdjustment(ctx sdk.Context, consumer string, provider stri // 1. sum(epoch_adjustment * total_cu_used_this_epoch) // 2. total_used_cu = sum(totalConsumerUsage) - maxRewardsBoost := int64(5) // TODO: yarom, this needs to be read from rewards module + maxRewardsBoost := k.rewardsKeeper.MaxRewardBoost(ctx) // check for adjustment limits: adjustment = min(1,1/rewardsMaxBoost * epoch_sum_cu/cu_with_provider) - if totalConsumerUsage >= uint64(maxRewardsBoost)*usageWithThisProvider { + if totalConsumerUsage >= maxRewardsBoost*usageWithThisProvider { // epoch adjustment is 1 adjustment.TotalUsage += totalConsumerUsage adjustment.AdjustedUsage += totalConsumerUsage @@ -93,10 +96,9 @@ func (k Keeper) AppendAdjustment(ctx sdk.Context, consumer string, provider stri // totalConsumerUsage < uint64(maxRewardsBoost)*usageWithThisProvider adjustment.TotalUsage += totalConsumerUsage // epoch adjustment is (1/maxRewardsBoost * totalConsumerUsage/usageWithThisProvider) * totalConsumerUsage - adjustment.AdjustedUsage += (totalConsumerUsage / uint64(maxRewardsBoost)) * (totalConsumerUsage / usageWithThisProvider) + adjustment.AdjustedUsage += (totalConsumerUsage / maxRewardsBoost) * (totalConsumerUsage / usageWithThisProvider) } - // we need to append, in both cases of existing adjustment or a not found one - adjustment.Index = index + k.SetAdjustment(ctx, adjustment) } diff --git a/x/subscription/keeper/cu_tracker.go b/x/subscription/keeper/cu_tracker.go index 16da8023f8..06bb93cb16 100644 --- a/x/subscription/keeper/cu_tracker.go +++ b/x/subscription/keeper/cu_tracker.go @@ -175,10 +175,9 @@ func (k Keeper) RewardAndResetCuTracker(ctx sdk.Context, cuTrackerTimerKeyBytes // TODO: deal with the reward's remainder (uint division...) providerAdjustment, ok := adjustmentFactorForProvider[provider] if !ok { - providerAdjustment = math.LegacyZeroDec() + providerAdjustment = sdk.OneDec().QuoInt64(int64(k.rewardsKeeper.MaxRewardBoost(ctx))) } - // TODO: send the adjustment to rewards module and cap adjustment by rewards module params - _ = providerAdjustment + totalMonthlyReward := k.CalcTotalMonthlyReward(ctx, totalTokenAmount, trackedCu, totalCuTracked) // calculate the provider reward (smaller than totalMonthlyReward @@ -192,7 +191,7 @@ func (k Keeper) RewardAndResetCuTracker(ctx sdk.Context, cuTrackerTimerKeyBytes } // aggregate the reward for the provider - k.rewardsKeeper.AggregateRewards(ctx, provider, chainID, 1, totalMonthlyReward) + k.rewardsKeeper.AggregateRewards(ctx, provider, chainID, providerAdjustment, totalMonthlyReward) // Note: if the reward function doesn't reward the provider // because he was unstaked, we only print an error and not returning diff --git a/x/subscription/types/expected_keepers.go b/x/subscription/types/expected_keepers.go index 02e040d5c6..972025c8cf 100644 --- a/x/subscription/types/expected_keepers.go +++ b/x/subscription/types/expected_keepers.go @@ -63,7 +63,8 @@ type DualStakingKeeper interface { } type RewardsKeeper interface { - AggregateRewards(ctx sdk.Context, provider, chainid string, adjustmentDenom uint64, rewards math.Int) + AggregateRewards(ctx sdk.Context, provider, chainid string, adjustment sdk.Dec, rewards math.Int) + MaxRewardBoost(ctx sdk.Context) (res uint64) } type StakingKeeper interface { From f0bc856176c04cb5ef62c590efd9ed038df8b715 Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Wed, 20 Dec 2023 13:04:56 +0000 Subject: [PATCH 06/11] fix tests --- x/rewards/keeper/providers_test.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/x/rewards/keeper/providers_test.go b/x/rewards/keeper/providers_test.go index 885947e6b9..b6dd5b4292 100644 --- a/x/rewards/keeper/providers_test.go +++ b/x/rewards/keeper/providers_test.go @@ -79,7 +79,7 @@ func TestBasicBoostProvidersRewards(t *testing.T) { res, err = ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), providerAcc.Addr.String(), "") require.Nil(t, err) require.Len(t, res.Rewards, 1) - require.Equal(t, res.Rewards[0].Amount.Amount.Uint64(), baserewards*subscription.LIMIT_TOKEN_PER_CU+baserewards*subscription.LIMIT_TOKEN_PER_CU*ts.Keepers.Rewards.GetParams(ts.Ctx).MaxRewardBoost) + require.Equal(t, res.Rewards[0].Amount.Amount.Uint64(), baserewards*subscription.LIMIT_TOKEN_PER_CU+baserewards*subscription.LIMIT_TOKEN_PER_CU) _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) require.Nil(t, err) } @@ -110,6 +110,8 @@ func TestSpecAllocationProvidersRewards(t *testing.T) { require.Nil(t, err) require.Len(t, res.Rewards, 1) require.Equal(t, res.Rewards[0].Amount, ts.plan.Price) + _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) + require.Nil(t, err) // now the provider should get all of the provider allocation ts.AdvanceMonths(1) @@ -119,7 +121,7 @@ func TestSpecAllocationProvidersRewards(t *testing.T) { res, err = ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), providerAcc.Addr.String(), "") require.Nil(t, err) require.Len(t, res.Rewards, 1) - require.Equal(t, res.Rewards[0].Amount, ts.plan.Price.AddAmount(distBalance)) + require.Equal(t, distBalance.QuoRaw(int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))), res.Rewards[0].Amount.Amount) _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) require.Nil(t, err) } @@ -164,7 +166,7 @@ func TestProvidersDiminishingRewards(t *testing.T) { require.Nil(t, err) require.Len(t, res.Rewards, 1) - require.Equal(t, res.Rewards[0].Amount.Amount, sdk.NewDecWithPrec(15, 1).MulInt(distBalance).Sub(sdk.NewDecWithPrec(5, 1).MulInt(ts.plan.Price.Amount.MulRaw(7))).TruncateInt()) + require.Equal(t, sdk.NewDecWithPrec(15, 1).MulInt(distBalance).Sub(sdk.NewDecWithPrec(5, 1).MulInt(ts.plan.Price.Amount.MulRaw(7))).TruncateInt().QuoRaw(int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))), res.Rewards[0].Amount.Amount) _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) require.Nil(t, err) } @@ -266,7 +268,7 @@ func Test2SpecsZeroShares(t *testing.T) { res, err = ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), providerAcc.Addr.String(), "") require.Nil(t, err) require.Len(t, res.Rewards, 1) - require.Equal(t, distBalance, res.Rewards[0].Amount.Amount) + require.Equal(t, distBalance.QuoRaw(int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))), res.Rewards[0].Amount.Amount) require.Equal(t, res.Rewards[0].ChainId, ts.spec.Index) _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) require.Nil(t, err) @@ -327,7 +329,7 @@ func Test2SpecsDoubleShares(t *testing.T) { res, err = ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), providerAcc.Addr.String(), "") require.Nil(t, err) require.Len(t, res.Rewards, 2) - require.Equal(t, res.Rewards[0].Amount.Amount, res.Rewards[1].Amount.Amount.MulRaw(2)) + require.Equal(t, res.Rewards[0].Amount.Amount.QuoRaw(2), res.Rewards[1].Amount.Amount) _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), providerAcc.Addr.String()) require.Nil(t, err) } @@ -398,7 +400,7 @@ func TestBonusRewards3Providers(t *testing.T) { require.Nil(t, err) require.Len(t, res.Rewards, 1) // we sub 3 because of truncating - require.Equal(t, res1.Rewards[0].Amount.Amount, distBalance.QuoRaw(7).SubRaw(3)) + require.Equal(t, res1.Rewards[0].Amount.Amount, distBalance.QuoRaw(7*int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))).SubRaw(1)) _, err = ts.TxDualstakingClaimRewards(providerAcc1.Addr.String(), providerAcc1.Addr.String()) require.Nil(t, err) @@ -406,7 +408,7 @@ func TestBonusRewards3Providers(t *testing.T) { require.Nil(t, err) require.Len(t, res.Rewards, 1) // we sub 1 because of truncating - require.Equal(t, res2.Rewards[0].Amount.Amount, distBalance.QuoRaw(7).MulRaw(2)) + require.Equal(t, res2.Rewards[0].Amount.Amount, distBalance.QuoRaw(7*int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))).MulRaw(2)) _, err = ts.TxDualstakingClaimRewards(providerAcc2.Addr.String(), providerAcc2.Addr.String()) require.Nil(t, err) @@ -414,7 +416,7 @@ func TestBonusRewards3Providers(t *testing.T) { require.Nil(t, err) require.Len(t, res.Rewards, 1) // we add 6 because of truncating - require.Equal(t, res3.Rewards[0].Amount.Amount, distBalance.QuoRaw(7).MulRaw(4).AddRaw(6)) + require.Equal(t, res3.Rewards[0].Amount.Amount, distBalance.QuoRaw(7*int64(ts.Keepers.Rewards.MaxRewardBoost(ts.Ctx))).MulRaw(4).AddRaw(1)) _, err = ts.TxDualstakingClaimRewards(providerAcc3.Addr.String(), providerAcc3.Addr.String()) require.Nil(t, err) } From bd414a508d7788a4cc9507cee4b72a1b0ba4c49f Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Thu, 21 Dec 2023 11:55:53 +0000 Subject: [PATCH 07/11] added unitests --- x/rewards/keeper/helpers_test.go | 1 + x/rewards/keeper/providers_test.go | 136 +++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/x/rewards/keeper/helpers_test.go b/x/rewards/keeper/helpers_test.go index 0d1d53b7e8..eb06fc79e7 100644 --- a/x/rewards/keeper/helpers_test.go +++ b/x/rewards/keeper/helpers_test.go @@ -37,6 +37,7 @@ func newTester(t *testing.T) *tester { ts.plan.Price.Amount = monthlyProvidersPool.QuoRaw(5).AddRaw(5) ts.plan.PlanPolicy.EpochCuLimit = monthlyProvidersPool.Uint64() * 5 ts.plan.PlanPolicy.TotalCuLimit = monthlyProvidersPool.Uint64() * 5 + ts.plan.PlanPolicy.MaxProvidersToPair = 5 ts.AddPlan(ts.plan.Index, ts.plan) ts.spec = ts.AddSpec("mock", common.CreateMockSpec()).Spec("mock") diff --git a/x/rewards/keeper/providers_test.go b/x/rewards/keeper/providers_test.go index b6dd5b4292..58e4767ab7 100644 --- a/x/rewards/keeper/providers_test.go +++ b/x/rewards/keeper/providers_test.go @@ -3,8 +3,10 @@ package keeper_test import ( "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/testutil/common" + "github.com/lavanet/lava/utils/sigs" "github.com/lavanet/lava/x/rewards/types" subscription "github.com/lavanet/lava/x/subscription/keeper" "github.com/stretchr/testify/require" @@ -420,3 +422,137 @@ func TestBonusRewards3Providers(t *testing.T) { _, err = ts.TxDualstakingClaimRewards(providerAcc3.Addr.String(), providerAcc3.Addr.String()) require.Nil(t, err) } + +func TestBonusRewardsEquall5Providers(t *testing.T) { + ts := newTester(t) + + count := 5 + providerAccs := []sigs.Account{} + consAccs := []sigs.Account{} + + for i := 0; i < count; i++ { + providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + providerAccs = append(providerAccs, providerAcc) + require.Nil(t, err) + + consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + consAccs = append(consAccs, consumerAcc) + require.Nil(t, err) + } + + for i := 1; i < 10; i++ { + ts.AdvanceEpoch() + + for _, providerAcc := range providerAccs { + for _, consAcc := range consAccs { + msg := ts.SendRelay(providerAcc.Addr.String(), consAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/uint64(count)/1000) + _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.Nil(t, err) + } + } + } + + // first months there are no bonus rewards, just payment ffrom the subscription + ts.AdvanceMonths(1) + ts.AdvanceBlocks(ts.BlocksToSave() + 1) + + for _, providerAcc := range providerAccs { + res, err := ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), "", "") + require.Nil(t, err) + require.Len(t, res.Rewards, 1) + _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), "") + require.Nil(t, err) + } + + // now the provider should get all of the provider allocation + ts.AdvanceMonths(1) + distBalance := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, types.ProviderRewardsDistributionPool) + ts.AdvanceEpoch() + + for _, providerAcc := range providerAccs { + res, err := ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), "", "") + require.Nil(t, err) + require.Len(t, res.Rewards, 1) + require.Equal(t, distBalance.QuoRaw(int64(count)), res.Rewards[0].Amount.Amount) + _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), "") + require.Nil(t, err) + } +} + +// in this test we have 5 providers and 5 consumers +// all the providers serve the same amount of cu in total +// cons1 relays only to prov1 -> expected adjustment 1/5 (1 out of maxrewardboost) +// cons2-5 relays to all prov2-5 -> expected adjustment 4/5 (1 out of maxrewardboost) +func TestBonusRewards5Providers(t *testing.T) { + ts := newTester(t) + + count := 5 + providerAccs := []sigs.Account{} + consAccs := []sigs.Account{} + + for i := 0; i < count; i++ { + providerAcc, _ := ts.AddAccount(common.PROVIDER, 1, testBalance) + err := ts.StakeProvider(providerAcc.Addr.String(), ts.spec, testBalance) + providerAccs = append(providerAccs, providerAcc) + require.Nil(t, err) + + consumerAcc, _ := ts.AddAccount(common.CONSUMER, 1, ts.plan.Price.Amount.Int64()) + _, err = ts.TxSubscriptionBuy(consumerAcc.Addr.String(), consumerAcc.Addr.String(), ts.plan.Index, 1, false) + consAccs = append(consAccs, consumerAcc) + require.Nil(t, err) + } + + for i := 1; i < 10; i++ { + ts.AdvanceEpoch() + + msg := ts.SendRelay(providerAccs[0].Addr.String(), consAccs[0], []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/100) + _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.Nil(t, err) + + for _, providerAcc := range providerAccs[1:] { + for _, consAcc := range consAccs[1:] { + msg := ts.SendRelay(providerAcc.Addr.String(), consAcc, []string{ts.spec.Index}, ts.plan.Price.Amount.Uint64()/uint64(count)/100) + _, err := ts.TxPairingRelayPayment(msg.Creator, msg.Relays...) + require.Nil(t, err) + } + } + } + + // first months there are no bonus rewards, just payment ffrom the subscription + ts.AdvanceMonths(1) + ts.AdvanceBlocks(ts.BlocksToSave() + 1) + + for _, providerAcc := range providerAccs { + res, err := ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), "", "") + require.Nil(t, err) + require.Len(t, res.Rewards, 1) + _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), "") + require.Nil(t, err) + } + + // now the provider should get all of the provider allocation + ts.AdvanceMonths(1) + distBalance := ts.Keepers.Rewards.TotalPoolTokens(ts.Ctx, types.ProviderRewardsDistributionPool) + ts.AdvanceEpoch() + + // distribution pool divided between all providers (5) equally (they served the same amount of CU in total) + fullProvReward := distBalance.QuoRaw(5) + for i, providerAcc := range providerAccs { + var expected math.Int + if i == 0 { + // gets only 1/5 of the full reward (sub 1 for trancating) + expected = fullProvReward.MulRaw(1).QuoRaw(5).SubRaw(1) + } else { + // gets only 4/5 of the full reward (sub 2 for trancating) + expected = fullProvReward.MulRaw(4).QuoRaw(5).SubRaw(2) + } + res, err := ts.QueryDualstakingDelegatorRewards(providerAcc.Addr.String(), "", "") + require.Nil(t, err) + require.Len(t, res.Rewards, 1) + require.Equal(t, expected, res.Rewards[0].Amount.Amount) + _, err = ts.TxDualstakingClaimRewards(providerAcc.Addr.String(), "") + require.Nil(t, err) + } +} From 3cf692814d247830f95e83c67d00e527b5d63115 Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Thu, 21 Dec 2023 12:03:02 +0000 Subject: [PATCH 08/11] added genesis adjustments --- proto/lavanet/lava/subscription/genesis.proto | 2 + x/subscription/genesis.go | 2 + x/subscription/keeper/adjustment.go | 15 ++- x/subscription/types/genesis.pb.go | 106 ++++++++++++++---- 4 files changed, 100 insertions(+), 25 deletions(-) diff --git a/proto/lavanet/lava/subscription/genesis.proto b/proto/lavanet/lava/subscription/genesis.proto index dd1a469825..0b04484171 100644 --- a/proto/lavanet/lava/subscription/genesis.proto +++ b/proto/lavanet/lava/subscription/genesis.proto @@ -3,6 +3,7 @@ package lavanet.lava.subscription; import "gogoproto/gogo.proto"; import "lavanet/lava/subscription/params.proto"; +import "lavanet/lava/subscription/adjustment.proto"; import "lavanet/lava/fixationstore/fixation.proto"; import "lavanet/lava/timerstore/timer.proto"; // this line is used by starport scaffolding # genesis/proto/import @@ -16,5 +17,6 @@ message GenesisState { lavanet.lava.timerstore.GenesisState subsTS = 3 [(gogoproto.nullable) = false]; lavanet.lava.fixationstore.GenesisState cuTrackerFS = 4 [(gogoproto.nullable) = false]; lavanet.lava.timerstore.GenesisState cuTrackerTS = 5 [(gogoproto.nullable) = false]; + repeated Adjustment adjustments = 6 [(gogoproto.nullable) = false]; // this line is used by starport scaffolding # genesis/proto/state } diff --git a/x/subscription/genesis.go b/x/subscription/genesis.go index a1a715ad4d..346e08a4ad 100644 --- a/x/subscription/genesis.go +++ b/x/subscription/genesis.go @@ -15,6 +15,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) k.InitSubscriptionsTimers(ctx, genState.SubsTS) k.InitCuTrackers(ctx, genState.CuTrackerFS) k.InitCuTrackerTimers(ctx, genState.CuTrackerTS) + k.SetAllAdjustment(ctx, genState.Adjustments) } // ExportGenesis returns the capability module's exported genesis. @@ -25,6 +26,7 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { genesis.SubsTS = k.ExportSubscriptionsTimers(ctx) genesis.CuTrackerFS = k.ExportCuTrackers(ctx) genesis.CuTrackerTS = k.ExportCuTrackerTimers(ctx) + genesis.Adjustments = k.GetAllAdjustment(ctx) // this line is used by starport scaffolding # genesis/module/export return genesis diff --git a/x/subscription/keeper/adjustment.go b/x/subscription/keeper/adjustment.go index 7dab75eb3e..a51d9c264c 100644 --- a/x/subscription/keeper/adjustment.go +++ b/x/subscription/keeper/adjustment.go @@ -10,14 +10,14 @@ import ( "github.com/lavanet/lava/x/subscription/types" ) -// SetUniquePaymentStorageClientProvider set a specific uniquePaymentStorageClientProvider in the store from its index +// SetAdjustment set a specific Adjustment in the store from its index func (k Keeper) SetAdjustment(ctx sdk.Context, adjustment types.Adjustment) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) b := k.cdc.MustMarshal(&adjustment) store.Set([]byte(adjustment.Index), b) } -// GetUniquePaymentStorageClientProvider returns a uniquePaymentStorageClientProvider from its index +// GetAdjustment returns a Adjustment from its index func (k Keeper) GetAdjustment( ctx sdk.Context, index string, @@ -32,7 +32,7 @@ func (k Keeper) GetAdjustment( return val, true } -// RemoveUniquePaymentStorageClientProvider removes a uniquePaymentStorageClientProvider from the store +// RemoveAdjustment removes a Adjustment from the store func (k Keeper) RemoveAdjustment( ctx sdk.Context, index string, @@ -41,7 +41,7 @@ func (k Keeper) RemoveAdjustment( store.Delete([]byte(index)) } -// GetAllUniquePaymentStorageClientProvider returns all uniquePaymentStorageClientProvider +// GetAllAdjustment returns all Adjustment func (k Keeper) GetAllAdjustment(ctx sdk.Context) (list []types.Adjustment) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.AdjustmentKeyPrefix)) iterator := sdk.KVStorePrefixIterator(store, []byte{}) @@ -57,6 +57,13 @@ func (k Keeper) GetAllAdjustment(ctx sdk.Context) (list []types.Adjustment) { return } +// SetAllAdjustment sets all adjustments to the store +func (k Keeper) SetAllAdjustment(ctx sdk.Context, list []types.Adjustment) { + for _, a := range list { + k.SetAdjustment(ctx, a) + } +} + func (k Keeper) GetProviderFromAdjustment(adjustment *types.Adjustment) (string, error) { index := adjustment.Index // index consists of chain_epoch_providerAddress diff --git a/x/subscription/types/genesis.pb.go b/x/subscription/types/genesis.pb.go index 9f54aa11f5..4defbe3e97 100644 --- a/x/subscription/types/genesis.pb.go +++ b/x/subscription/types/genesis.pb.go @@ -32,6 +32,7 @@ type GenesisState struct { SubsTS types1.GenesisState `protobuf:"bytes,3,opt,name=subsTS,proto3" json:"subsTS"` CuTrackerFS types.GenesisState `protobuf:"bytes,4,opt,name=cuTrackerFS,proto3" json:"cuTrackerFS"` CuTrackerTS types1.GenesisState `protobuf:"bytes,5,opt,name=cuTrackerTS,proto3" json:"cuTrackerTS"` + Adjustments []Adjustment `protobuf:"bytes,6,rep,name=adjustments,proto3" json:"adjustments"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -102,6 +103,13 @@ func (m *GenesisState) GetCuTrackerTS() types1.GenesisState { return types1.GenesisState{} } +func (m *GenesisState) GetAdjustments() []Adjustment { + if m != nil { + return m.Adjustments + } + return nil +} + func init() { proto.RegisterType((*GenesisState)(nil), "lavanet.lava.subscription.GenesisState") } @@ -111,27 +119,29 @@ func init() { } var fileDescriptor_dc6c60f9c112fe52 = []byte{ - // 310 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xbf, 0x4a, 0xc4, 0x30, - 0x1c, 0xc7, 0xdb, 0xf3, 0xbc, 0xa1, 0xe7, 0x14, 0x1c, 0x6a, 0x87, 0xf8, 0x0f, 0xf5, 0x04, 0x49, - 0x41, 0x1f, 0x40, 0x38, 0xa1, 0x4e, 0xc2, 0x61, 0x3a, 0xb9, 0xa5, 0x25, 0xd6, 0xa0, 0x6d, 0x4a, - 0x92, 0xca, 0xf9, 0x16, 0x3e, 0xd6, 0x8d, 0x37, 0x3a, 0x89, 0xb4, 0xcf, 0x21, 0x48, 0x93, 0xaa, - 0xcd, 0xd0, 0x41, 0xa7, 0xa4, 0xf0, 0xf9, 0x7e, 0xf2, 0xfd, 0xf1, 0xab, 0x77, 0xf2, 0x44, 0x9e, - 0x49, 0x41, 0x55, 0xd8, 0x9e, 0xa1, 0xac, 0x12, 0x99, 0x0a, 0x56, 0x2a, 0xc6, 0x8b, 0x30, 0xa3, - 0x05, 0x95, 0x4c, 0xa2, 0x52, 0x70, 0xc5, 0xc1, 0x4e, 0x07, 0xa2, 0xf6, 0x44, 0x7d, 0x30, 0xd8, - 0xce, 0x78, 0xc6, 0x35, 0x15, 0xb6, 0x37, 0x13, 0x08, 0x8e, 0x87, 0xcd, 0x25, 0x11, 0x24, 0xef, - 0xc4, 0xc1, 0xa9, 0xc5, 0xdd, 0xb3, 0x25, 0x69, 0x19, 0xa9, 0xb8, 0xa0, 0x3f, 0x5f, 0x1d, 0x7a, - 0x68, 0xa1, 0x8a, 0xe5, 0x54, 0x18, 0x4e, 0x5f, 0x0d, 0x74, 0xf0, 0x39, 0xf2, 0xb6, 0xae, 0x4d, - 0x75, 0xac, 0x88, 0xa2, 0xe0, 0xd2, 0x9b, 0x98, 0x07, 0x7d, 0x77, 0xcf, 0x9d, 0x4d, 0xcf, 0xf7, - 0xd1, 0xe0, 0x28, 0x68, 0xa1, 0xc1, 0xf9, 0x78, 0xf5, 0xbe, 0xeb, 0xdc, 0x76, 0x31, 0x10, 0x79, - 0x93, 0x16, 0x8a, 0xb0, 0x3f, 0xd2, 0x82, 0x99, 0x2d, 0xb0, 0x2a, 0xa3, 0xfe, 0xd3, 0xdf, 0x1e, - 0x93, 0x06, 0x57, 0xc6, 0x13, 0x63, 0x7f, 0x43, 0x7b, 0x8e, 0x6c, 0xcf, 0xef, 0x3c, 0x83, 0x92, - 0x18, 0x83, 0x85, 0x37, 0x4d, 0xab, 0x58, 0x90, 0xf4, 0x91, 0x8a, 0x08, 0xfb, 0xe3, 0x7f, 0x35, - 0xea, 0x2b, 0xc0, 0x4d, 0xcf, 0x18, 0x63, 0x7f, 0xf3, 0xef, 0xdd, 0xfa, 0xf9, 0x79, 0xb4, 0xaa, - 0xa1, 0xbb, 0xae, 0xa1, 0xfb, 0x51, 0x43, 0xf7, 0xb5, 0x81, 0xce, 0xba, 0x81, 0xce, 0x5b, 0x03, - 0x9d, 0xbb, 0xb3, 0x8c, 0xa9, 0x87, 0x2a, 0x41, 0x29, 0xcf, 0x43, 0x6b, 0x93, 0x4b, 0xfb, 0xf7, - 0x50, 0x2f, 0x25, 0x95, 0xc9, 0x44, 0xaf, 0xf3, 0xe2, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x3b, - 0x9c, 0xd5, 0xa2, 0x02, 0x00, 0x00, + // 344 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x31, 0x4e, 0xf3, 0x30, + 0x18, 0x86, 0x93, 0xbf, 0xfd, 0x33, 0xa4, 0x4c, 0x11, 0x43, 0xe8, 0x10, 0x0a, 0x08, 0x28, 0x08, + 0x39, 0x12, 0x1c, 0x00, 0x51, 0xa4, 0x30, 0x21, 0x55, 0x24, 0x13, 0x9b, 0x1b, 0x4c, 0x30, 0x90, + 0x38, 0xb2, 0x1d, 0x54, 0x6e, 0xc1, 0xb1, 0x3a, 0x30, 0x74, 0x64, 0x42, 0xa8, 0xb9, 0x08, 0xb2, + 0x9d, 0xb6, 0xb6, 0x50, 0x06, 0x98, 0xec, 0x48, 0xcf, 0xfb, 0xe4, 0xfd, 0x92, 0xcf, 0x3d, 0x7c, + 0x86, 0x2f, 0xb0, 0x40, 0x3c, 0x14, 0x67, 0xc8, 0xaa, 0x09, 0x4b, 0x29, 0x2e, 0x39, 0x26, 0x45, + 0x98, 0xa1, 0x02, 0x31, 0xcc, 0x40, 0x49, 0x09, 0x27, 0xde, 0x56, 0x03, 0x02, 0x71, 0x02, 0x1d, + 0xec, 0x6f, 0x66, 0x24, 0x23, 0x92, 0x0a, 0xc5, 0x4d, 0x05, 0xfa, 0x07, 0xed, 0xe6, 0x12, 0x52, + 0x98, 0x37, 0xe2, 0xfe, 0x71, 0x3b, 0x07, 0xef, 0x1e, 0x2b, 0xc6, 0x73, 0x54, 0xf0, 0x86, 0x3d, + 0x32, 0xd8, 0x7b, 0x3c, 0x85, 0x82, 0x63, 0x9c, 0x50, 0xb4, 0x7a, 0x6a, 0xd0, 0x3d, 0x03, 0xe5, + 0x38, 0x47, 0x54, 0x71, 0xf2, 0xaa, 0xa0, 0xdd, 0xf7, 0x8e, 0xbb, 0x71, 0xa5, 0xc6, 0x8c, 0x39, + 0xe4, 0xc8, 0x3b, 0x77, 0x1d, 0x55, 0xce, 0xb7, 0x07, 0xf6, 0xb0, 0x77, 0xba, 0x03, 0x5a, 0xc7, + 0x06, 0x63, 0x09, 0x8e, 0xba, 0xb3, 0xcf, 0x6d, 0xeb, 0xa6, 0x89, 0x79, 0x91, 0xeb, 0x08, 0x28, + 0x8a, 0xfd, 0x7f, 0x52, 0x30, 0x34, 0x05, 0x46, 0x65, 0xa0, 0xbf, 0x7a, 0xe9, 0x51, 0x69, 0xef, + 0x52, 0x79, 0x92, 0xd8, 0xef, 0x48, 0xcf, 0xbe, 0xe9, 0x59, 0xcf, 0xd3, 0x2a, 0x49, 0x62, 0x6f, + 0xec, 0xf6, 0xd2, 0x2a, 0xa1, 0x30, 0x7d, 0x42, 0x34, 0x8a, 0xfd, 0xee, 0x9f, 0x1a, 0xe9, 0x0a, + 0xef, 0x5a, 0x33, 0x26, 0xb1, 0xff, 0xff, 0xf7, 0xdd, 0xf4, 0xbc, 0xd0, 0xad, 0xff, 0x31, 0xf3, + 0x9d, 0x41, 0xe7, 0xa7, 0xce, 0xf8, 0xe6, 0x17, 0x2b, 0x7a, 0xa9, 0xd3, 0xf2, 0xa3, 0x68, 0xb6, + 0x08, 0xec, 0xf9, 0x22, 0xb0, 0xbf, 0x16, 0x81, 0xfd, 0x56, 0x07, 0xd6, 0xbc, 0x0e, 0xac, 0x8f, + 0x3a, 0xb0, 0x6e, 0x4f, 0x32, 0xcc, 0x1f, 0xaa, 0x09, 0x48, 0x49, 0x1e, 0x1a, 0x8b, 0x31, 0x35, + 0x37, 0x8e, 0xbf, 0x96, 0x88, 0x4d, 0x1c, 0xb9, 0x1d, 0x67, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xeb, 0x1a, 0x89, 0x93, 0x1d, 0x03, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -154,6 +164,20 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Adjustments) > 0 { + for iNdEx := len(m.Adjustments) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Adjustments[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } { size, err := m.CuTrackerTS.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -234,6 +258,12 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) l = m.CuTrackerTS.Size() n += 1 + l + sovGenesis(uint64(l)) + if len(m.Adjustments) > 0 { + for _, e := range m.Adjustments { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } return n } @@ -437,6 +467,40 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Adjustments", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Adjustments = append(m.Adjustments, Adjustment{}) + if err := m.Adjustments[len(m.Adjustments)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) From 9b1ef2ac0cc17e533da8c943f5f27c1fb9d83add Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Thu, 21 Dec 2023 12:21:05 +0000 Subject: [PATCH 09/11] remove prints --- x/dualstaking/keeper/hooks_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/x/dualstaking/keeper/hooks_test.go b/x/dualstaking/keeper/hooks_test.go index 80b6165b43..83944ead49 100644 --- a/x/dualstaking/keeper/hooks_test.go +++ b/x/dualstaking/keeper/hooks_test.go @@ -1,9 +1,7 @@ package keeper_test import ( - "fmt" "math/rand" - "strconv" "testing" "time" @@ -489,11 +487,6 @@ func TestHooksRandomDelegations(t *testing.T) { delegator = prevDelegator } _, err := ts.TxDualstakingDelegate(delegator, provider, ts.spec.Index, sdk.NewCoin(ts.TokenDenom(), sdk.NewInt(int64(d)))) - if err == nil { - fmt.Printf("%v: delegated %v\n", strconv.Itoa(i), strconv.Itoa(d)) - } else { - fmt.Printf("%v: failed delegating %v. err: %s\n", strconv.Itoa(i), strconv.Itoa(d), err.Error()) - } require.Nil(t, err) _, found := ts.Keepers.StakingKeeper.GetDelegation(ts.Ctx, delegatorAcc.Addr, sdk.ValAddress(validatorAcc.Addr)) From a9f3309a8ebd7df5ab1868ef0dffa18e3b1bb73f Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Sun, 24 Dec 2023 17:12:46 +0000 Subject: [PATCH 10/11] fix --- x/dualstaking/keeper/hooks_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/dualstaking/keeper/hooks_test.go b/x/dualstaking/keeper/hooks_test.go index 83944ead49..2de18f8cd6 100644 --- a/x/dualstaking/keeper/hooks_test.go +++ b/x/dualstaking/keeper/hooks_test.go @@ -413,7 +413,7 @@ func TestValidatorAndProvidersSlash(t *testing.T) { // verify once again that the delegator's delegations balance is preserved diff, err = ts.Keepers.Dualstaking.VerifyDelegatorBalance(ts.Ctx, delegatorAcc.Addr) require.Nil(t, err) - require.True(t, diff.IsZero()) + require.Equal(t, sdk.OneInt(), diff) } // TestCancelUnbond checks that the providers-validators delegations balance is preserved when From b94df6b35b53ab2c7d6ff3da701555b85a942a17 Mon Sep 17 00:00:00 2001 From: Yarom Swisa Date: Sun, 24 Dec 2023 18:13:03 +0000 Subject: [PATCH 11/11] fix --- x/subscription/keeper/subscription.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/subscription/keeper/subscription.go b/x/subscription/keeper/subscription.go index 8d2d154a35..cbfc616c3a 100644 --- a/x/subscription/keeper/subscription.go +++ b/x/subscription/keeper/subscription.go @@ -186,7 +186,7 @@ func (k Keeper) CreateSubscription( ) } - if !found || autoRenewalFlag { + if !found || sub.AutoRenewal { expiry := uint64(utils.NextMonth(ctx.BlockTime()).UTC().Unix()) sub.MonthExpiryTime = expiry sub.Block = block