diff --git a/proto/lavanet/lava/epochstorage/stake_entry.proto b/proto/lavanet/lava/epochstorage/stake_entry.proto index cdc5797873..5a337698ce 100644 --- a/proto/lavanet/lava/epochstorage/stake_entry.proto +++ b/proto/lavanet/lava/epochstorage/stake_entry.proto @@ -24,6 +24,8 @@ message StakeEntry { BlockReport block_report = 13; string vault = 14; cosmos.staking.v1beta1.Description description = 15 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + uint64 jails = 16; + int64 jail_end_time = 17; } // BlockReport holds the most up-to-date info regarding blocks of the provider diff --git a/testutil/keeper/keepers_init.go b/testutil/keeper/keepers_init.go index e364ca4a6d..984c93689e 100644 --- a/testutil/keeper/keepers_init.go +++ b/testutil/keeper/keepers_init.go @@ -453,16 +453,9 @@ func AdvanceToBlock(ctx context.Context, ks *Keepers, block uint64, customBlockT return ctx } - if len(customBlockTime) > 0 { - for uint64(unwrapedCtx.BlockHeight()) < block { - ctx = AdvanceBlock(ctx, ks, customBlockTime...) - unwrapedCtx = sdk.UnwrapSDKContext(ctx) - } - } else { - for uint64(unwrapedCtx.BlockHeight()) < block { - ctx = AdvanceBlock(ctx, ks) - unwrapedCtx = sdk.UnwrapSDKContext(ctx) - } + for uint64(unwrapedCtx.BlockHeight()) < block { + ctx = AdvanceBlock(ctx, ks, customBlockTime...) + unwrapedCtx = sdk.UnwrapSDKContext(ctx) } return ctx @@ -476,10 +469,7 @@ func AdvanceEpoch(ctx context.Context, ks *Keepers, customBlockTime ...time.Dura if err != nil { panic(err) } - if len(customBlockTime) > 0 { - return AdvanceToBlock(ctx, ks, nextEpochBlockNum, customBlockTime...) - } - return AdvanceToBlock(ctx, ks, nextEpochBlockNum) + return AdvanceToBlock(ctx, ks, nextEpochBlockNum, customBlockTime...) } // Make sure you save the new context diff --git a/x/dualstaking/keeper/delegate.go b/x/dualstaking/keeper/delegate.go index 58a521c3f5..965c5a1272 100644 --- a/x/dualstaking/keeper/delegate.go +++ b/x/dualstaking/keeper/delegate.go @@ -231,7 +231,7 @@ func (k Keeper) modifyStakeEntryDelegation(ctx sdk.Context, delegator, provider, details["min_spec_stake"] = k.specKeeper.GetMinStake(ctx, chainID).String() utils.LogLavaEvent(ctx, k.Logger(ctx), types.FreezeFromUnbond, details, "freezing provider due to stake below min spec stake") stakeEntry.Freeze() - } else if delegator == stakeEntry.Vault && stakeEntry.IsFrozen() { + } else if delegator == stakeEntry.Vault && stakeEntry.IsFrozen() && !stakeEntry.IsJailed(ctx.BlockTime().UTC().Unix()) { stakeEntry.UnFreeze(k.epochstorageKeeper.GetCurrentNextEpoch(ctx) + 1) } diff --git a/x/epochstorage/types/stake_entry.go b/x/epochstorage/types/stake_entry.go index 4fda988dae..dad926835c 100644 --- a/x/epochstorage/types/stake_entry.go +++ b/x/epochstorage/types/stake_entry.go @@ -33,6 +33,10 @@ func (stakeEntry *StakeEntry) IsFrozen() bool { return stakeEntry.StakeAppliedBlock == FROZEN_BLOCK } +func (stakeEntry *StakeEntry) IsJailed(time int64) bool { + return stakeEntry.JailEndTime > time +} + func (stakeEntry *StakeEntry) IsAddressVaultAndNotProvider(address string) bool { return address != stakeEntry.Address && address == stakeEntry.Vault } diff --git a/x/epochstorage/types/stake_entry.pb.go b/x/epochstorage/types/stake_entry.pb.go index ce15076cf5..1eb9867989 100644 --- a/x/epochstorage/types/stake_entry.pb.go +++ b/x/epochstorage/types/stake_entry.pb.go @@ -41,6 +41,8 @@ type StakeEntry struct { BlockReport *BlockReport `protobuf:"bytes,13,opt,name=block_report,json=blockReport,proto3" json:"block_report,omitempty"` Vault string `protobuf:"bytes,14,opt,name=vault,proto3" json:"vault,omitempty"` Description types1.Description `protobuf:"bytes,15,opt,name=description,proto3" json:"description"` + Jails uint64 `protobuf:"varint,16,opt,name=jails,proto3" json:"jails,omitempty"` + JailEndTime int64 `protobuf:"varint,17,opt,name=jail_end_time,json=jailEndTime,proto3" json:"jail_end_time,omitempty"` } func (m *StakeEntry) Reset() { *m = StakeEntry{} } @@ -174,6 +176,20 @@ func (m *StakeEntry) GetDescription() types1.Description { return types1.Description{} } +func (m *StakeEntry) GetJails() uint64 { + if m != nil { + return m.Jails + } + return 0 +} + +func (m *StakeEntry) GetJailEndTime() int64 { + if m != nil { + return m.JailEndTime + } + return 0 +} + // BlockReport holds the most up-to-date info regarding blocks of the provider // It is set in the relay payment TX logic // used by the consumer to calculate the provider's sync score @@ -239,43 +255,45 @@ func init() { } var fileDescriptor_df6302d6b53c056e = []byte{ - // 572 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcd, 0x6e, 0xd3, 0x4c, - 0x14, 0x8d, 0xdb, 0xa4, 0x4d, 0xc6, 0x69, 0xbf, 0xaf, 0xd3, 0x2e, 0xa6, 0x5d, 0xb8, 0xa6, 0x45, - 0xc8, 0x02, 0x64, 0xab, 0x45, 0x3c, 0x00, 0x09, 0x0d, 0x02, 0xb1, 0x40, 0x86, 0x15, 0x9b, 0x68, - 0x6c, 0x8f, 0x9c, 0x51, 0xec, 0x19, 0xcb, 0x33, 0x8d, 0xe8, 0x5b, 0xf0, 0x18, 0x2c, 0x79, 0x07, - 0x36, 0x5d, 0x76, 0xc9, 0x0a, 0xa1, 0x64, 0xc1, 0x6b, 0xa0, 0xf9, 0x71, 0x7e, 0x16, 0x45, 0xb0, - 0xb1, 0xe7, 0xde, 0x7b, 0xee, 0x99, 0x7b, 0xce, 0xcc, 0x80, 0x27, 0x05, 0x9e, 0x61, 0x46, 0x64, - 0xa4, 0xfe, 0x11, 0xa9, 0x78, 0x3a, 0x11, 0x92, 0xd7, 0x38, 0x27, 0x91, 0x90, 0x78, 0x4a, 0xc6, - 0x84, 0xc9, 0xfa, 0x26, 0xac, 0x6a, 0x2e, 0x39, 0x3c, 0xb6, 0xe0, 0x50, 0xfd, 0xc3, 0x75, 0xf0, - 0x49, 0x70, 0x3f, 0x0f, 0x61, 0x59, 0xc5, 0x29, 0x93, 0x86, 0xe4, 0xe4, 0x28, 0xe7, 0x39, 0xd7, - 0xcb, 0x48, 0xad, 0x6c, 0xd6, 0x4b, 0xb9, 0x28, 0xb9, 0x88, 0x12, 0x2c, 0x48, 0x34, 0xbb, 0x48, - 0x88, 0xc4, 0x17, 0x51, 0xca, 0x29, 0xb3, 0xf5, 0x87, 0xb6, 0xae, 0x86, 0xa2, 0x2c, 0x5f, 0x42, - 0x6c, 0x6c, 0x51, 0x07, 0xb8, 0xa4, 0x8c, 0x47, 0xfa, 0x6b, 0x52, 0x67, 0xdf, 0x3a, 0x00, 0xbc, - 0x57, 0x4a, 0xae, 0x94, 0x10, 0xf8, 0x1c, 0x74, 0xb4, 0x2e, 0xe4, 0xf8, 0x4e, 0xe0, 0x5e, 0x1e, - 0x87, 0x86, 0x37, 0x54, 0xfb, 0x86, 0x96, 0x34, 0x1c, 0x72, 0xca, 0x06, 0xed, 0xdb, 0x1f, 0xa7, - 0xad, 0xd8, 0xa0, 0x21, 0x02, 0xbb, 0x38, 0xcb, 0x6a, 0x22, 0x04, 0xda, 0xf2, 0x9d, 0xa0, 0x17, - 0x37, 0x21, 0x0c, 0xc1, 0xa1, 0x31, 0x0a, 0x57, 0x55, 0x41, 0x49, 0x36, 0x4e, 0x0a, 0x9e, 0x4e, - 0xd1, 0xb6, 0xef, 0x04, 0xed, 0xf8, 0x40, 0x97, 0x5e, 0x98, 0xca, 0x40, 0x15, 0xe0, 0x2b, 0xd0, - 0x6b, 0x0c, 0x11, 0xa8, 0xed, 0x6f, 0x07, 0xee, 0xe5, 0x79, 0x78, 0xaf, 0xaf, 0xe1, 0x95, 0xc5, - 0xda, 0x71, 0x56, 0xbd, 0xd0, 0x07, 0x6e, 0x4e, 0x78, 0xc1, 0x53, 0x2c, 0x29, 0x67, 0xa8, 0xe3, - 0x3b, 0x41, 0x27, 0x5e, 0x4f, 0xc1, 0x23, 0xd0, 0x49, 0x27, 0x98, 0x32, 0xb4, 0xa3, 0x47, 0x36, - 0x81, 0x92, 0x52, 0x72, 0x46, 0xa7, 0xa4, 0x46, 0x5d, 0x23, 0xc5, 0x86, 0x70, 0x04, 0xf6, 0x33, - 0x52, 0x90, 0x1c, 0x4b, 0x32, 0x96, 0x5c, 0xe2, 0x02, 0xf5, 0xfe, 0xce, 0xa4, 0xbd, 0xa6, 0xed, - 0x83, 0xea, 0xda, 0xe0, 0x29, 0x68, 0x49, 0x25, 0x02, 0xff, 0xc8, 0xf3, 0x56, 0x75, 0xc1, 0x08, - 0x1c, 0x2e, 0x79, 0x52, 0x5e, 0x96, 0x54, 0x08, 0xa5, 0xd4, 0xd5, 0xd6, 0xc2, 0xa6, 0x34, 0x5c, - 0x56, 0xe0, 0x29, 0x70, 0x0b, 0x2c, 0xe4, 0x38, 0x9d, 0x60, 0x96, 0x13, 0xd4, 0xd7, 0x40, 0xa0, - 0x52, 0x43, 0x9d, 0x81, 0xaf, 0x41, 0x5f, 0x1f, 0xcf, 0xb8, 0x26, 0x15, 0xaf, 0x25, 0xda, 0xd3, - 0x73, 0x3d, 0xfa, 0x83, 0xff, 0xfa, 0xd0, 0x62, 0x8d, 0x8e, 0xdd, 0x64, 0x15, 0x28, 0x73, 0x67, - 0xf8, 0xba, 0x90, 0x68, 0xdf, 0x98, 0xab, 0x03, 0xf8, 0x0e, 0xb8, 0x19, 0x11, 0x69, 0x4d, 0x2b, - 0x7d, 0x28, 0xff, 0x69, 0xfe, 0xf3, 0x46, 0x77, 0x73, 0x59, 0x1b, 0xe9, 0x2f, 0x57, 0xd0, 0x41, - 0x4f, 0x39, 0xf0, 0xe5, 0xd7, 0xd7, 0xc7, 0x4e, 0xbc, 0x4e, 0xf1, 0xa6, 0xdd, 0xdd, 0xfd, 0xbf, - 0x7b, 0x36, 0x02, 0xee, 0x60, 0x73, 0x73, 0x3d, 0xa5, 0xbe, 0xc5, 0xed, 0xd8, 0x04, 0xf0, 0x01, - 0xe8, 0x17, 0x58, 0x12, 0x21, 0xed, 0x1d, 0xdc, 0xd2, 0x45, 0xd7, 0xe4, 0x74, 0xfb, 0x60, 0x74, - 0x3b, 0xf7, 0x9c, 0xbb, 0xb9, 0xe7, 0xfc, 0x9c, 0x7b, 0xce, 0xe7, 0x85, 0xd7, 0xba, 0x5b, 0x78, - 0xad, 0xef, 0x0b, 0xaf, 0xf5, 0xf1, 0x69, 0x4e, 0xe5, 0xe4, 0x3a, 0x09, 0x53, 0x5e, 0x46, 0x1b, - 0x6f, 0xf9, 0xd3, 0xe6, 0x6b, 0x96, 0x37, 0x15, 0x11, 0xc9, 0x8e, 0x7e, 0x5c, 0xcf, 0x7e, 0x07, - 0x00, 0x00, 0xff, 0xff, 0xe9, 0x92, 0xff, 0xb6, 0x3f, 0x04, 0x00, 0x00, + // 607 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcf, 0x6e, 0xd3, 0x30, + 0x18, 0x6f, 0xb6, 0x76, 0x5b, 0x9d, 0x6d, 0x6c, 0xde, 0x0e, 0xde, 0x0e, 0x59, 0xe8, 0x10, 0x8a, + 0x00, 0x25, 0xda, 0x10, 0x0f, 0x40, 0x4b, 0x8b, 0x40, 0x1c, 0x50, 0xd8, 0x89, 0x4b, 0xe4, 0x24, + 0x56, 0x6a, 0x9a, 0xd8, 0x51, 0xec, 0x55, 0xec, 0x2d, 0x78, 0x0c, 0x8e, 0x3c, 0xc6, 0x8e, 0x3b, + 0x72, 0x42, 0xa8, 0x3d, 0xf0, 0x14, 0x48, 0xc8, 0x76, 0xd2, 0x3f, 0x87, 0x21, 0xb8, 0x34, 0xfe, + 0xbe, 0xef, 0xf7, 0xfd, 0xfc, 0xfd, 0x7e, 0x76, 0x0d, 0x9e, 0xe6, 0x78, 0x8a, 0x19, 0x91, 0x81, + 0xfa, 0x06, 0xa4, 0xe4, 0xc9, 0x58, 0x48, 0x5e, 0xe1, 0x8c, 0x04, 0x42, 0xe2, 0x09, 0x89, 0x08, + 0x93, 0xd5, 0x8d, 0x5f, 0x56, 0x5c, 0x72, 0x78, 0x52, 0x83, 0x7d, 0xf5, 0xf5, 0x57, 0xc1, 0xa7, + 0xde, 0xfd, 0x3c, 0x84, 0xa5, 0x25, 0xa7, 0x4c, 0x1a, 0x92, 0xd3, 0xe3, 0x8c, 0x67, 0x5c, 0x2f, + 0x03, 0xb5, 0xaa, 0xb3, 0x4e, 0xc2, 0x45, 0xc1, 0x45, 0x10, 0x63, 0x41, 0x82, 0xe9, 0x45, 0x4c, + 0x24, 0xbe, 0x08, 0x12, 0x4e, 0x59, 0x5d, 0x7f, 0x54, 0xd7, 0xd5, 0x50, 0x94, 0x65, 0x0b, 0x48, + 0x1d, 0xd7, 0xa8, 0x43, 0x5c, 0x50, 0xc6, 0x03, 0xfd, 0x6b, 0x52, 0xbd, 0xdf, 0x1d, 0x00, 0x3e, + 0x28, 0x25, 0x43, 0x25, 0x04, 0xbe, 0x00, 0x1d, 0xad, 0x0b, 0x59, 0xae, 0xe5, 0xd9, 0x97, 0x27, + 0xbe, 0xe1, 0xf5, 0xd5, 0xbe, 0x7e, 0x4d, 0xea, 0x0f, 0x38, 0x65, 0xfd, 0xf6, 0xed, 0x8f, 0xb3, + 0x56, 0x68, 0xd0, 0x10, 0x81, 0x6d, 0x9c, 0xa6, 0x15, 0x11, 0x02, 0x6d, 0xb8, 0x96, 0xd7, 0x0d, + 0x9b, 0x10, 0xfa, 0xe0, 0xc8, 0x18, 0x85, 0xcb, 0x32, 0xa7, 0x24, 0x8d, 0xe2, 0x9c, 0x27, 0x13, + 0xb4, 0xe9, 0x5a, 0x5e, 0x3b, 0x3c, 0xd4, 0xa5, 0x97, 0xa6, 0xd2, 0x57, 0x05, 0xf8, 0x1a, 0x74, + 0x1b, 0x43, 0x04, 0x6a, 0xbb, 0x9b, 0x9e, 0x7d, 0x79, 0xee, 0xdf, 0xeb, 0xab, 0x3f, 0xac, 0xb1, + 0xf5, 0x38, 0xcb, 0x5e, 0xe8, 0x02, 0x3b, 0x23, 0x3c, 0xe7, 0x09, 0x96, 0x94, 0x33, 0xd4, 0x71, + 0x2d, 0xaf, 0x13, 0xae, 0xa6, 0xe0, 0x31, 0xe8, 0x24, 0x63, 0x4c, 0x19, 0xda, 0xd2, 0x23, 0x9b, + 0x40, 0x49, 0x29, 0x38, 0xa3, 0x13, 0x52, 0xa1, 0x1d, 0x23, 0xa5, 0x0e, 0xe1, 0x08, 0xec, 0xa7, + 0x24, 0x27, 0x19, 0x96, 0x24, 0x92, 0x5c, 0xe2, 0x1c, 0x75, 0xff, 0xcd, 0xa4, 0xbd, 0xa6, 0xed, + 0x4a, 0x75, 0xad, 0xf1, 0xe4, 0xb4, 0xa0, 0x12, 0x81, 0xff, 0xe4, 0x79, 0xa7, 0xba, 0x60, 0x00, + 0x8e, 0x16, 0x3c, 0x09, 0x2f, 0x0a, 0x2a, 0x84, 0x52, 0x6a, 0x6b, 0x6b, 0x61, 0x53, 0x1a, 0x2c, + 0x2a, 0xf0, 0x0c, 0xd8, 0x39, 0x16, 0x32, 0x4a, 0xc6, 0x98, 0x65, 0x04, 0xed, 0x6a, 0x20, 0x50, + 0xa9, 0x81, 0xce, 0xc0, 0x37, 0x60, 0x57, 0x1f, 0x4f, 0x54, 0x91, 0x92, 0x57, 0x12, 0xed, 0xe9, + 0xb9, 0x1e, 0xff, 0xc5, 0x7f, 0x7d, 0x68, 0xa1, 0x46, 0x87, 0x76, 0xbc, 0x0c, 0x94, 0xb9, 0x53, + 0x7c, 0x9d, 0x4b, 0xb4, 0x6f, 0xcc, 0xd5, 0x01, 0x7c, 0x0f, 0xec, 0x94, 0x88, 0xa4, 0xa2, 0xa5, + 0x3e, 0x94, 0x07, 0x9a, 0xff, 0xbc, 0xd1, 0xdd, 0x5c, 0xd6, 0x46, 0xfa, 0xab, 0x25, 0xb4, 0xdf, + 0x55, 0x0e, 0x7c, 0xfd, 0xf5, 0xed, 0x89, 0x15, 0xae, 0x52, 0xa8, 0x7d, 0x3e, 0x61, 0x9a, 0x0b, + 0x74, 0xa0, 0xd5, 0x98, 0x00, 0xf6, 0xc0, 0x9e, 0x5a, 0x44, 0x84, 0xa5, 0x91, 0xa4, 0x05, 0x41, + 0x87, 0xae, 0xe5, 0x6d, 0x86, 0xb6, 0x4a, 0x0e, 0x59, 0x7a, 0x45, 0x0b, 0xf2, 0xb6, 0xbd, 0xb3, + 0x7d, 0xb0, 0xd3, 0x1b, 0x01, 0xbb, 0xbf, 0x3e, 0xb6, 0xd6, 0xa7, 0xef, 0x7f, 0x3b, 0x34, 0x01, + 0x7c, 0x08, 0x76, 0x73, 0x2c, 0x89, 0x90, 0xf5, 0xed, 0xdd, 0xd0, 0x45, 0xdb, 0xe4, 0x74, 0x7b, + 0x7f, 0x74, 0x3b, 0x73, 0xac, 0xbb, 0x99, 0x63, 0xfd, 0x9c, 0x39, 0xd6, 0x97, 0xb9, 0xd3, 0xba, + 0x9b, 0x3b, 0xad, 0xef, 0x73, 0xa7, 0xf5, 0xf1, 0x59, 0x46, 0xe5, 0xf8, 0x3a, 0xf6, 0x13, 0x5e, + 0x04, 0x6b, 0xaf, 0xc0, 0xe7, 0xf5, 0x77, 0x40, 0xde, 0x94, 0x44, 0xc4, 0x5b, 0xfa, 0x6f, 0xf9, + 0xfc, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0x11, 0xd6, 0x4d, 0x79, 0x04, 0x00, 0x00, } func (m *StakeEntry) Marshal() (dAtA []byte, err error) { @@ -298,6 +316,20 @@ func (m *StakeEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.JailEndTime != 0 { + i = encodeVarintStakeEntry(dAtA, i, uint64(m.JailEndTime)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.Jails != 0 { + i = encodeVarintStakeEntry(dAtA, i, uint64(m.Jails)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } { size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -511,6 +543,12 @@ func (m *StakeEntry) Size() (n int) { } l = m.Description.Size() n += 1 + l + sovStakeEntry(uint64(l)) + if m.Jails != 0 { + n += 2 + sovStakeEntry(uint64(m.Jails)) + } + if m.JailEndTime != 0 { + n += 2 + sovStakeEntry(uint64(m.JailEndTime)) + } return n } @@ -970,6 +1008,44 @@ func (m *StakeEntry) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Jails", wireType) + } + m.Jails = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStakeEntry + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Jails |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JailEndTime", wireType) + } + m.JailEndTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStakeEntry + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.JailEndTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipStakeEntry(dAtA[iNdEx:]) diff --git a/x/pairing/README.md b/x/pairing/README.md index 0dfbc72fa5..d884d67091 100644 --- a/x/pairing/README.md +++ b/x/pairing/README.md @@ -63,6 +63,8 @@ type StakeEntry struct { DelegateTotal types.Coin // total delegation to the provider (without self delegation) DelegateLimit types.Coin // delegation total limit DelegateCommission uint64 // commission from delegation rewards + Jails uint64 // number of times the provider has been jailed + JailTime int64 // the end of the jail time, after which the provider can return to service } ``` @@ -207,12 +209,18 @@ Pairing verification is used by the provider to determine whether to offer servi #### Unresponsiveness -Providers can get punished for being unresponsive to consumer requests. If a provider wishes to stop getting paired with consumers for any reason to avoid getting punished, it can freeze itself. Currently, the punishment for being unresponsive is freezing. In the future, providers will be jailed for this kind of behaviour. +Providers can get punished for being unresponsive to consumer requests. If a provider wishes to stop getting paired with consumers for any reason to avoid getting punished, it can freeze itself. Currently, the punishment for being unresponsive is jailing. When a consumer is getting paired with a provider, it sends requests for service. If provider A is unresponsive after a few tries, the consumer switches to another provider from its pairing list, provider B, and send requests to it. When communicatting with provider B, the consumer appends the address of provider A to its request, thus adding the current request's CU to provider A's "complainers CU" counter. Every epoch start, the amount of complainers CU is compared with the amount of serviced CU of each provider across a few epochs back. If the complainers CU is higher, the provider is considered unresponsive and gets punished. The number of epochs back is determined by the recommendedEpochNumToCollectPayment parameter +#### Jail + +If a provider is down and users report it, the provider will be jailed. +The first 2 instances of jailing are temporary, lasting 1 hour each, and will be automatically removed. +After 2 consecutive jailings, the provider will be jailed for 24 hours and set to a 'frozen' state. To resume activity, the provider must send an 'unfreeze' transaction after the jail time has ended. + #### Static Providers Static providers are Lava chain providers that offer services to any consumer without relying on pairing. This feature allows new consumers to communicate with the Lava chain without a centralized provider. For example, when a new consumer wants to start using Lava, it needs to obtain its pairing list from a Lava node. However, since it initially does not have a list of providers to communicate with, it can use the static providers list to obtain its initial pairing list. diff --git a/x/pairing/keeper/epoch_cu.go b/x/pairing/keeper/epoch_cu.go index 8951a4d626..c9b9135e2c 100644 --- a/x/pairing/keeper/epoch_cu.go +++ b/x/pairing/keeper/epoch_cu.go @@ -158,6 +158,12 @@ func (k Keeper) GetProviderEpochComplainerCu(ctx sdk.Context, epoch uint64, prov return val, true } +// RemoveProviderEpochComplainerCu deletes a ProviderEpochComplainerCu in the store +func (k Keeper) RemoveProviderEpochComplainerCu(ctx sdk.Context, epoch uint64, provider string, chainID string) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.ProviderEpochComplainerCuKeyPrefix()) + store.Delete(types.ProviderEpochCuKey(epoch, provider, chainID)) +} + // RemoveProviderEpochComplainerCu removes a ProviderEpochCu from the store func (k Keeper) RemoveAllProviderEpochComplainerCu(ctx sdk.Context, epoch uint64) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.ProviderEpochComplainerCuKeyPrefix()) diff --git a/x/pairing/keeper/msg_server_unfreeze.go b/x/pairing/keeper/msg_server_unfreeze.go index 2296394238..3cd2da1061 100644 --- a/x/pairing/keeper/msg_server_unfreeze.go +++ b/x/pairing/keeper/msg_server_unfreeze.go @@ -20,6 +20,11 @@ func (k msgServer) UnfreezeProvider(goCtx context.Context, msg *types.MsgUnfreez return nil, utils.LavaFormatWarning("Unfreeze_cant_get_stake_entry", types.FreezeStakeEntryNotFoundError, []utils.Attribute{{Key: "chainID", Value: chainId}, {Key: "providerAddress", Value: msg.GetCreator()}}...) } + // the provider is not frozen (active or jailed), continue to other chainIDs + if !stakeEntry.IsFrozen() { + continue + } + minStake := k.Keeper.specKeeper.GetMinStake(ctx, chainId) if stakeEntry.EffectiveStake().LT(minStake.Amount) { return nil, utils.LavaFormatWarning("Unfreeze_insufficient_stake", types.UnFreezeInsufficientStakeError, @@ -31,13 +36,21 @@ func (k msgServer) UnfreezeProvider(goCtx context.Context, msg *types.MsgUnfreez }...) } - if stakeEntry.StakeAppliedBlock > unfreezeBlock { - // unfreeze the provider by making the StakeAppliedBlock the current block. This will let the provider be added to the pairing list in the next epoch, when current entries becomes the front of epochStorage - stakeEntry.UnFreeze(unfreezeBlock) - k.epochStorageKeeper.ModifyStakeEntryCurrent(ctx, chainId, stakeEntry) - unfrozen_chains = append(unfrozen_chains, chainId) + if stakeEntry.IsJailed(ctx.BlockTime().UTC().Unix()) { + return nil, utils.LavaFormatWarning("Unfreeze_jailed_provider", types.UnFreezeJailedStakeError, + []utils.Attribute{ + {Key: "chainID", Value: chainId}, + {Key: "providerAddress", Value: msg.GetCreator()}, + {Key: "jailEnd", Value: stakeEntry.JailEndTime}, + }...) } - // else case does not throw an error because we don't want to fail unfreezing other chains + + // unfreeze the provider by making the StakeAppliedBlock the current block. This will let the provider be added to the pairing list in the next epoch, when current entries becomes the front of epochStorage + stakeEntry.UnFreeze(unfreezeBlock) + stakeEntry.JailEndTime = 0 + stakeEntry.Jails = 0 + k.epochStorageKeeper.ModifyStakeEntryCurrent(ctx, chainId, stakeEntry) + unfrozen_chains = append(unfrozen_chains, chainId) } utils.LogLavaEvent(ctx, ctx.Logger(), "unfreeze_provider", map[string]string{"providerAddress": msg.GetCreator(), "chainIDs": strings.Join(unfrozen_chains, ",")}, "Provider Unfreeze") return &types.MsgUnfreezeProviderResponse{}, nil diff --git a/x/pairing/keeper/unresponsive_provider.go b/x/pairing/keeper/unresponsive_provider.go index b0ca9f6448..1025a4529a 100644 --- a/x/pairing/keeper/unresponsive_provider.go +++ b/x/pairing/keeper/unresponsive_provider.go @@ -5,6 +5,7 @@ import ( "math" "strconv" "strings" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/utils" @@ -12,7 +13,12 @@ import ( "github.com/lavanet/lava/x/pairing/types" ) -const THRESHOLD_FACTOR = 4 +const ( + THRESHOLD_FACTOR = 4 + SOFT_JAILS = 2 + SOFT_JAIL_TIME = 1 * time.Hour / time.Second + HARD_JAIL_TIME = 24 * time.Hour / time.Second +) // PunishUnresponsiveProviders punished unresponsive providers (current punishment: freeze) func (k Keeper) PunishUnresponsiveProviders(ctx sdk.Context, epochsNumToCheckCUForUnresponsiveProvider, epochsNumToCheckCUForComplainers uint64) { @@ -78,15 +84,15 @@ func (k Keeper) PunishUnresponsiveProviders(ctx sdk.Context, epochsNumToCheckCUF // check all supported providers from all geolocations prior to making decisions existingProviders := map[string]uint64{} - stakeAppliedBlockProviders := map[string]uint64{} + stakeEntries := map[string]epochstoragetypes.StakeEntry{} for _, providerStakeStorage := range providerStakeStorageList { providerStakeEntriesForChain := providerStakeStorage.GetStakeEntries() // count providers per geolocation for _, providerStakeEntry := range providerStakeEntriesForChain { if !providerStakeEntry.IsFrozen() { existingProviders[providerStakeEntry.GetChain()]++ - stakeAppliedBlockProviders[ProviderChainID(providerStakeEntry.Address, providerStakeEntry.Chain)] = providerStakeEntry.StakeAppliedBlock } + stakeEntries[ProviderChainID(providerStakeEntry.Address, providerStakeEntry.Chain)] = providerStakeEntry } } @@ -96,9 +102,14 @@ func (k Keeper) PunishUnresponsiveProviders(ctx sdk.Context, epochsNumToCheckCUF pecsDetailed := k.GetAllProviderEpochComplainerCuStore(ctx) complainedProviders := map[string]map[uint64]types.ProviderEpochComplainerCu{} // map[provider chainID]map[epoch]ProviderEpochComplainerCu for _, pec := range pecsDetailed { - if minHistoryBlock < stakeAppliedBlockProviders[pec.Provider] { - // this staked provider has too short history (either since staking - // or since it was last unfrozen) - do not consider for jailing + entry, ok := stakeEntries[ProviderChainID(pec.Provider, pec.ChainId)] + if ok { + if minHistoryBlock < entry.StakeAppliedBlock && entry.Jails == 0 { + // this staked provider has too short history (either since staking + // or since it was last unfrozen) - do not consider for jailing + continue + } + } else { continue } @@ -135,7 +146,12 @@ func (k Keeper) PunishUnresponsiveProviders(ctx sdk.Context, epochsNumToCheckCUF // providerPaymentStorageKeyList is not empty -> provider should be punished if len(epochs) != 0 && existingProviders[chainID] > minProviders { - err = k.punishUnresponsiveProvider(ctx, epochs, provider, chainID, complaintCU, servicedCU, complainedProviders[key]) + entry, ok := stakeEntries[key] + if !ok { + utils.LavaFormatError("Jail_cant_get_stake_entry", types.FreezeStakeEntryNotFoundError, []utils.Attribute{{Key: "chainID", Value: chainID}, {Key: "providerAddress", Value: provider}}...) + continue + } + err = k.punishUnresponsiveProvider(ctx, epochs, entry, complaintCU, servicedCU) existingProviders[chainID]-- if err != nil { utils.LavaFormatError("unstake unresponsive providers failed to punish provider", err, @@ -226,40 +242,50 @@ func (k Keeper) getCurrentProviderStakeStorageList(ctx sdk.Context) []epochstora } // Function that punishes providers. Current punishment is freeze -func (k Keeper) punishUnresponsiveProvider(ctx sdk.Context, epochs []uint64, provider, chainID string, complaintCU uint64, servicedCU uint64, providerEpochCuMap map[uint64]types.ProviderEpochComplainerCu) error { - // freeze the unresponsive provider - err := k.FreezeProvider(ctx, provider, []string{chainID}, "unresponsiveness") - if err != nil { - utils.LavaFormatError("unable to freeze provider entry due to unresponsiveness", err, - utils.Attribute{Key: "provider", Value: provider}, - utils.Attribute{Key: "chainID", Value: chainID}, - ) +func (k Keeper) punishUnresponsiveProvider(ctx sdk.Context, epochs []uint64, stakeEntry epochstoragetypes.StakeEntry, complaintCU uint64, servicedCU uint64) error { + // if last jail was more than 24H ago, reset the jails counter + if !stakeEntry.IsJailed(ctx.BlockTime().UTC().Unix() - int64(HARD_JAIL_TIME)) { + stakeEntry.Jails = 0 } - utils.LogLavaEvent(ctx, k.Logger(ctx), types.ProviderJailedEventName, - map[string]string{ - "provider_address": provider, - "chain_id": chainID, - "complaint_cu": strconv.FormatUint(complaintCU, 10), - "serviced_cu": strconv.FormatUint(servicedCU, 10), - }, - "Unresponsive provider was freezed due to unresponsiveness") + stakeEntry.Jails++ + + details := map[string]string{ + "provider_address": stakeEntry.Address, + "chain_id": stakeEntry.Chain, + "complaint_cu": strconv.FormatUint(complaintCU, 10), + "serviced_cu": strconv.FormatUint(servicedCU, 10), + } + + if stakeEntry.Jails > SOFT_JAILS { + stakeEntry.Freeze() + stakeEntry.JailEndTime = ctx.BlockTime().UTC().Unix() + int64(HARD_JAIL_TIME) + + details["duration"] = HARD_JAIL_TIME.String() + details["end"] = strconv.FormatInt(stakeEntry.JailEndTime, 10) + utils.LogLavaEvent(ctx, k.Logger(ctx), types.ProviderFreezeJailedEventName, details, + "Unresponsive provider was jailed due to unresponsiveness") + } else { + stakeEntry.JailEndTime = ctx.BlockTime().UTC().Unix() + int64(SOFT_JAIL_TIME) + epochduration := k.downtimeKeeper.GetParams(ctx).EpochDuration / time.Second + epochblocks := k.epochStorageKeeper.EpochBlocksRaw(ctx) + stakeEntry.StakeAppliedBlock = uint64(ctx.BlockHeight()) + uint64(SOFT_JAIL_TIME/epochduration)*epochblocks + + details["duration"] = SOFT_JAIL_TIME.String() + details["end"] = strconv.FormatInt(stakeEntry.JailEndTime, 10) + utils.LogLavaEvent(ctx, k.Logger(ctx), types.ProviderTemporaryJailedEventName, details, + "Unresponsive provider was jailed due to unresponsiveness") + } + k.epochStorageKeeper.ModifyStakeEntryCurrent(ctx, stakeEntry.Chain, stakeEntry) // reset the provider's complainer CU (so he won't get punished for the same complaints twice) - k.resetComplainersCU(ctx, epochs, provider, chainID, providerEpochCuMap) + k.resetComplainersCU(ctx, epochs, stakeEntry.Address, stakeEntry.Chain) return nil } // resetComplainersCU resets the complainers CU for a specific provider and chain -func (k Keeper) resetComplainersCU(ctx sdk.Context, epochs []uint64, provider string, chainID string, providerEpochCuMap map[uint64]types.ProviderEpochComplainerCu) { +func (k Keeper) resetComplainersCU(ctx sdk.Context, epochs []uint64, provider string, chainID string) { for _, epoch := range epochs { - pec, ok := providerEpochCuMap[epoch] - if !ok { - continue - } - - // reset the complainer CU - pec.ComplainersCu = 0 - k.SetProviderEpochComplainerCu(ctx, epoch, provider, chainID, pec) + k.RemoveProviderEpochComplainerCu(ctx, epoch, provider, chainID) } } diff --git a/x/pairing/keeper/unresponsive_provider_test.go b/x/pairing/keeper/unresponsive_provider_test.go index 92c2c1f2a9..b57189b739 100644 --- a/x/pairing/keeper/unresponsive_provider_test.go +++ b/x/pairing/keeper/unresponsive_provider_test.go @@ -2,33 +2,35 @@ package keeper_test import ( "testing" + "time" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/lavanet/lava/testutil/common" "github.com/lavanet/lava/utils/lavaslices" "github.com/lavanet/lava/utils/rand" "github.com/lavanet/lava/utils/sigs" - epochstoragetypes "github.com/lavanet/lava/x/epochstorage/types" + "github.com/lavanet/lava/x/pairing/keeper" "github.com/lavanet/lava/x/pairing/types" "github.com/stretchr/testify/require" ) -func (ts *tester) checkProviderFreeze(provider string, shouldFreeze bool) { +func (ts *tester) checkProviderJailed(provider string, shouldFreeze bool) { stakeEntry, stakeStorageFound := ts.Keepers.Epochstorage.GetStakeEntryByAddressCurrent(ts.Ctx, ts.spec.Name, provider) require.True(ts.T, stakeStorageFound) + require.Equal(ts.T, shouldFreeze, stakeEntry.IsJailed(ts.Ctx.BlockTime().UTC().Unix())) if shouldFreeze { - require.Equal(ts.T, uint64(epochstoragetypes.FROZEN_BLOCK), stakeEntry.StakeAppliedBlock) - } else { - require.NotEqual(ts.T, uint64(epochstoragetypes.FROZEN_BLOCK), stakeEntry.StakeAppliedBlock) + jailDelta := keeper.SOFT_JAIL_TIME + if stakeEntry.Jails > keeper.SOFT_JAILS { + jailDelta = keeper.HARD_JAIL_TIME + } + require.InDelta(ts.T, stakeEntry.JailEndTime, ts.BlockTime().UTC().Unix(), float64(jailDelta)) } } func (ts *tester) checkComplainerReset(provider string, epoch uint64) { // validate the complainers CU field in the unresponsive provider's providerPaymentStorage // was reset after being punished (use the epoch from the relay - when it got reported) - pec, found := ts.Keepers.Pairing.GetProviderEpochComplainerCu(ts.Ctx, epoch, provider, ts.spec.Name) - require.Equal(ts.T, true, found) - require.Equal(ts.T, uint64(0), pec.ComplainersCu) + _, found := ts.Keepers.Pairing.GetProviderEpochComplainerCu(ts.Ctx, epoch, provider, ts.spec.Name) + require.Equal(ts.T, false, found) } func (ts *tester) checkProviderStaked(provider string) { @@ -119,10 +121,10 @@ func TestUnresponsivenessStressTest(t *testing.T) { largerConst = recommendedEpochNumToCollectPayment } - ts.AdvanceEpochs(largerConst) + ts.AdvanceEpochs(largerConst, time.Nanosecond) for i := 0; i < unresponsiveCount; i++ { - ts.checkProviderFreeze(providers[i].Addr.String(), true) + ts.checkProviderJailed(providers[i].Addr.String(), true) ts.checkComplainerReset(providers[i].Addr.String(), relayEpoch) } @@ -185,9 +187,9 @@ func TestFreezingProviderForUnresponsiveness(t *testing.T) { largerConst = recommendedEpochNumToCollectPayment } - ts.AdvanceEpochs(largerConst) + ts.AdvanceEpochs(largerConst, time.Second) - ts.checkProviderFreeze(provider1, true) + ts.checkProviderJailed(provider1, true) ts.checkComplainerReset(provider1, relayEpoch) ts.checkProviderStaked(provider0) } @@ -242,12 +244,12 @@ func TestFreezingProviderForUnresponsivenessContinueComplainingAfterFreeze(t *te largerConst = recommendedEpochNumToCollectPayment } - ts.AdvanceEpochs(largerConst) + ts.AdvanceEpochs(largerConst, time.Second) - ts.checkProviderFreeze(provider1, true) + ts.checkProviderJailed(provider1, true) ts.checkComplainerReset(provider1, relayEpoch) - ts.AdvanceEpochs(2) + ts.AdvanceEpochs(2, time.Second) // create more relay requests for provider0 that contain complaints about provider1 for clientIndex := 0; clientIndex < clientsCount; clientIndex++ { @@ -266,7 +268,7 @@ func TestFreezingProviderForUnresponsivenessContinueComplainingAfterFreeze(t *te } // test the provider is still frozen - ts.checkProviderFreeze(provider1, true) + ts.checkProviderJailed(provider1, true) } func TestNotFreezingProviderForUnresponsivenessWithMinProviders(t *testing.T) { @@ -303,14 +305,13 @@ func TestNotFreezingProviderForUnresponsivenessWithMinProviders(t *testing.T) { } // advance enough epochs so we can check punishment due to unresponsiveness // (if the epoch is too early, there's no punishment) - ts.AdvanceEpochs(largerConst + recommendedEpochNumToCollectPayment) + ts.AdvanceEpochs(largerConst+recommendedEpochNumToCollectPayment, time.Nanosecond) // find two providers in the pairing pairing, err := ts.QueryPairingGetPairing(ts.spec.Name, clients[0].Addr.String()) require.NoError(t, err) provider0Provider := pairing.Providers[0].Address provider1Provider := pairing.Providers[1].Address - provider0Vault := sdk.MustAccAddressFromBech32(pairing.Providers[0].Vault) // create unresponsive data that includes provider1 being unresponsive unresponsiveProvidersData := []*types.ReportedProvider{{Address: provider1Provider}} @@ -330,7 +331,7 @@ func TestNotFreezingProviderForUnresponsivenessWithMinProviders(t *testing.T) { Relays: lavaslices.Slice(relaySession), } - ts.payAndVerifyBalance(relayPaymentMessage, clients[clientIndex].Addr, provider0Vault, true, true, 100) + ts.relayPaymentWithoutPay(relayPaymentMessage, true) } // advance enough epochs so the unresponsive provider will be punished @@ -338,9 +339,145 @@ func TestNotFreezingProviderForUnresponsivenessWithMinProviders(t *testing.T) { largerConst = recommendedEpochNumToCollectPayment } - ts.AdvanceEpochs(largerConst) + ts.AdvanceEpochs(largerConst, time.Nanosecond) // test the unresponsive provider1 hasn't froze - ts.checkProviderFreeze(provider1Provider, play.shouldBeFrozen) + ts.checkProviderJailed(provider1Provider, play.shouldBeFrozen) + } +} + +// Test to measure the time the check for unresponsiveness every epoch start takes +func TestJailProviderForUnresponsiveness(t *testing.T) { + // setup test for unresponsiveness + clientsCount := 1 + providersCount := 10 + + ts := newTester(t) + ts.setupForPayments(providersCount, clientsCount, providersCount-1) // set providers-to-pair + + clients := ts.Accounts(common.CONSUMER) + + recommendedEpochNumToCollectPayment := ts.Keepers.Pairing.RecommendedEpochNumToCollectPayment(ts.Ctx) + + largerConst := types.EPOCHS_NUM_TO_CHECK_CU_FOR_UNRESPONSIVE_PROVIDER + if largerConst < types.EPOCHS_NUM_TO_CHECK_FOR_COMPLAINERS { + largerConst = types.EPOCHS_NUM_TO_CHECK_FOR_COMPLAINERS + } + + // advance enough epochs so we can check punishment due to unresponsiveness + // (if the epoch is too early, there's no punishment) + ts.AdvanceEpochs(largerConst + recommendedEpochNumToCollectPayment) + + // advance enough epochs so the unresponsive provider will be punished + if largerConst < recommendedEpochNumToCollectPayment { + largerConst = recommendedEpochNumToCollectPayment + } + + // find two providers in the pairing + pairing, err := ts.QueryPairingGetPairing(ts.spec.Name, clients[0].Addr.String()) + require.NoError(t, err) + provider0 := pairing.Providers[0].Address + provider1 := pairing.Providers[1].Address + + jailProvider := func() { + found := false + // make sure our provider is in the pairing + for !found { + ts.AdvanceEpoch(0) + pairing1, err := ts.QueryPairingVerifyPairing(ts.spec.Name, clients[0].Addr.String(), provider0, ts.BlockHeight()) + require.NoError(t, err) + + pairing2, err := ts.QueryPairingVerifyPairing(ts.spec.Name, clients[0].Addr.String(), provider1, ts.BlockHeight()) + require.NoError(t, err) + found = pairing1.Valid && pairing2.Valid + } + + // create relay requests for provider0 that contain complaints about provider1 + unresponsiveProvidersData := []*types.ReportedProvider{{Address: provider1}} + relayEpoch := ts.BlockHeight() + cuSum := ts.spec.ApiCollections[0].Apis[0].ComputeUnits * 10 + + relaySession := ts.newRelaySession(provider0, 0, cuSum, relayEpoch, 0) + relaySession.UnresponsiveProviders = unresponsiveProvidersData + sig, err := sigs.Sign(clients[0].SK, *relaySession) + relaySession.Sig = sig + require.NoError(t, err) + relayPaymentMessage := types.MsgRelayPayment{ + Creator: provider0, + Relays: lavaslices.Slice(relaySession), + } + ts.relayPaymentWithoutPay(relayPaymentMessage, true) + + ts.AdvanceEpochs(recommendedEpochNumToCollectPayment+1, 0) + ts.checkProviderJailed(provider1, true) + ts.checkComplainerReset(provider1, relayEpoch) + ts.checkProviderStaked(provider0) + + res, err := ts.QueryPairingVerifyPairing(ts.spec.Name, clients[0].Addr.String(), provider1, ts.BlockHeight()) + require.NoError(t, err) + require.False(t, res.Valid) } + + // jail first time + jailProvider() + // try to unfreeze with increase of self delegation + _, err = ts.TxDualstakingDelegate(provider1, provider1, ts.spec.Index, common.NewCoin(ts.TokenDenom(), 1)) + require.Nil(t, err) + + _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) + require.Nil(t, err) + + ts.checkProviderJailed(provider1, true) + + // advance epoch and one hour to leave jail + ts.AdvanceBlock(time.Hour) + ts.AdvanceEpoch(0) + ts.checkProviderJailed(provider1, false) + + // jail second time + jailProvider() + _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) + require.Nil(t, err) + + ts.checkProviderJailed(provider1, true) + + // advance epoch and one hour to leave jail + ts.AdvanceBlock(time.Hour) + ts.AdvanceEpoch(0) + ts.checkProviderJailed(provider1, false) + + // jail third time + jailProvider() + // try to unfreeze with increase of self delegation + _, err = ts.TxDualstakingDelegate(provider1, provider1, ts.spec.Index, common.NewCoin(ts.TokenDenom(), 1)) + require.Nil(t, err) + + _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) + require.NotNil(t, err) + + // advance epoch and one hour to try leave jail + ts.AdvanceBlock(time.Hour) + ts.AdvanceEpoch(0) + ts.checkProviderJailed(provider1, true) + + // advance epoch and 24 hour to try leave jail + ts.AdvanceBlock(24 * time.Hour) + ts.AdvanceEpoch(0) + ts.checkProviderJailed(provider1, false) + + _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) + require.Nil(t, err) + ts.AdvanceEpochs(largerConst + recommendedEpochNumToCollectPayment) + + // jail first time again + jailProvider() + _, err = ts.TxPairingUnfreezeProvider(provider1, ts.spec.Index) + require.Nil(t, err) + + ts.checkProviderJailed(provider1, true) + + // advance epoch and one hour to leave jail + ts.AdvanceBlock(time.Hour) + ts.AdvanceEpoch(0) + ts.checkProviderJailed(provider1, false) } diff --git a/x/pairing/types/errors.go b/x/pairing/types/errors.go index 2a4e391e94..1ee681dcc2 100644 --- a/x/pairing/types/errors.go +++ b/x/pairing/types/errors.go @@ -23,4 +23,5 @@ var ( UnFreezeInsufficientStakeError = sdkerrors.New("UnFreezeInsufficientStakeError Error", 697, "Could not unfreeze provider due to insufficient stake. Stake must be above minimum stake to unfreeze") InvalidCreatorAddressError = sdkerrors.New("InvalidCreatorAddressError Error", 698, "The creator address is invalid") AmountCoinError = sdkerrors.New("AmountCoinError Error", 699, "Amount limit coin is invalid") + UnFreezeJailedStakeError = sdkerrors.New("UnFreezeJailedStakeError Error", 700, "Could not unfreeze provider due to being jailed") ) diff --git a/x/pairing/types/expected_keepers.go b/x/pairing/types/expected_keepers.go index 601a85248f..562feb1b2b 100644 --- a/x/pairing/types/expected_keepers.go +++ b/x/pairing/types/expected_keepers.go @@ -58,6 +58,7 @@ type EpochstorageKeeper interface { AddFixationRegistry(fixationKey string, getParamFunction func(sdk.Context) any) GetDeletedEpochs(ctx sdk.Context) []uint64 EpochBlocks(ctx sdk.Context, block uint64) (res uint64, err error) + EpochBlocksRaw(ctx sdk.Context) (res uint64) GetUnstakeHoldBlocks(ctx sdk.Context, chainID string) uint64 } diff --git a/x/pairing/types/types.go b/x/pairing/types/types.go index 02daaccdf2..38cff91711 100644 --- a/x/pairing/types/types.go +++ b/x/pairing/types/types.go @@ -5,12 +5,13 @@ const ( ProviderStakeUpdateEventName = "stake_update_provider" ProviderUnstakeEventName = "provider_unstake_commit" - RelayPaymentEventName = "relay_payment" - ProviderJailedEventName = "provider_jailed" - ProviderReportedEventName = "provider_reported" - LatestBlocksReportEventName = "provider_latest_block_report" - RejectedCuEventName = "rejected_cu" - UnstakeProposalEventName = "unstake_gov_proposal" + RelayPaymentEventName = "relay_payment" + ProviderTemporaryJailedEventName = "provider_temporary_jailed" + ProviderFreezeJailedEventName = "provider_jailed" + ProviderReportedEventName = "provider_reported" + LatestBlocksReportEventName = "provider_latest_block_report" + RejectedCuEventName = "rejected_cu" + UnstakeProposalEventName = "unstake_gov_proposal" ) // unstake description strings