Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix some logics and sync spec to latest source code #18

Merged
merged 28 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7c0d133
sync state.md with latest implementation
zsystm Jul 23, 2023
6e03a41
update state transition diagrams
zsystm Jul 24, 2023
a9e3316
sync state transitions, messages and fix some logics
zsystm Jul 24, 2023
fc34e6e
sync latest spec with latest code and fix some core logics
zsystm Jul 24, 2023
8f5b2c4
add detailed comments for net amount and param change ante handlers
zsystm Jul 24, 2023
0d5e8ae
add missed swagger yaml
zsystm Jul 24, 2023
71fcb97
add description for 5.75%
zsystm Jul 24, 2023
d6ed3cf
remove ConservativeNetAmount and update calc method of TotalLiquidTokens
zsystm Jul 25, 2023
f74fd46
update remaining reward calc method
zsystm Jul 25, 2023
e95dedb
chore: update name of tc
zsystm Jul 25, 2023
428a82e
update spec
zsystm Jul 25, 2023
5c8ee8c
optimize operations
zsystm Jul 25, 2023
3255774
optimize redundant calls
zsystm Jul 25, 2023
81480d5
add todo
zsystm Jul 26, 2023
82516ac
update and fix spec
zsystm Jul 27, 2023
f7cb028
chore: fix grammar
zsystm Jul 27, 2023
2fd453d
update formula for calculating discount rate
zsystm Jul 27, 2023
d1733cc
add missing diagrams and security cap description
zsystm Jul 27, 2023
a7ccafb
wip: update and refactor net amount state part
zsystm Jul 28, 2023
fec0e74
fix broken tcs and refactor some functions
zsystm Jul 28, 2023
9f6deec
remove netAmountBeforeModuleFee from net amount state
zsystm Jul 28, 2023
a660689
remove un-used function
zsystm Jul 28, 2023
990c31c
add NetAmountBeforeModuleFee at spec
zsystm Jul 28, 2023
be23323
chore: fix variable name
zsystm Jul 28, 2023
6d3baec
refactor: calc functions / add: dynamic fee tcs
zsystm Jul 31, 2023
b00c468
add custom stringer for Params
zsystm Jul 31, 2023
7ffbc4d
add tc for CalcUtilizationRatio / chore: fix number expression
zsystm Jul 31, 2023
0dd2510
remove redundant func and logic
zsystm Jul 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 25 additions & 24 deletions proto/canto/liquidstaking/v1/liquidstaking.proto
Original file line number Diff line number Diff line change
Expand Up @@ -88,29 +88,29 @@ enum ChunkStatus {
// designated by the insurance provider.
CHUNK_STATUS_PAIRED = 2;
// A paired chunk enters this status when paired insurance is started to be
// withdrawn or is insufficient (meaning the insurance balance is below the
// minimum requirement to be considered valid insurance) or the validator of
// the insurance becomes tombstoned.
// withdrawn or it's balance <= 5.75%(double_sign_fraction +
// down_time_fraction) of chunk size tokens or the validator becomes
// tombstoned.
CHUNK_STATUS_UNPAIRING = 3;
// When a delegator (also known as a liquid staker) sends a MsgLiquidUnstake,
// it is queued as a PendingLiquidUnstake. At the end of the epoch,
// it is queued as a UnpairingForUnstakingChunkInfo. At the end of the epoch,
// the actual undelegation is triggered and the chunk enters this state.
// Once the unbonding period is over in next epoch, the staked tokens are
// returned to the delegator's account and the associated chunk object is
// removed.
// Once the unbonding period is over in next epoch, the tokens corresponding
// chunk size are returned to the delegator's account and the associated chunk
// object is removed.
CHUNK_STATUS_UNPAIRING_FOR_UNSTAKING = 4;
}

// Chunk defines the chunk of the module.
message Chunk {
option (gogoproto.goproto_getters) = false;

// Id of the chunk
// Unique id increased by 1
uint64 id = 1;
// Id of Paired insurance, 0 means no insurance
uint64 paired_insurance_id = 2;
// Id of Unpairing insurance, 0 means no insurance
uint64 unpairing_insurance_id = 3;
// Status of the chunk
ChunkStatus status = 4;
}

Expand All @@ -133,15 +133,16 @@ enum InsuranceStatus {
// the chunk remains same size and maximize its staking rewards.
INSURANCE_STATUS_PAIRED = 2;
// A paired insurance enters this status when it no longer has enough balance
// to cover slashing penalties, when the validator is tombstoned, or
// when the paired chunk is started to be undelegated.
// At the next epoch, unpairing will be unpaired.
// (=5.75% of chunk size tokens) to cover slashing penalties, when the
// validator is tombstoned, or when the paired chunk is started to be
// undelegated by MsgLiquidUnstake. At the next epoch, unpairing will be
// unpaired or pairing if it still valid.
INSURANCE_STATUS_UNPAIRING = 3;
// A paired insurance enters this status when there are
// queued withdrawal insurance requests created by MsgWithdrawInsurance at the
// A paired insurance enters this status when there was a
// queued WithdrawalInsuranceRequest created by MsgWithdrawInsurance at the
// epoch.
INSURANCE_STATUS_UNPAIRING_FOR_WITHDRAWAL = 4;
// Unpairing insurances from previous epoch enters this status.
// Unpairing insurances from previous epoch can enter this status.
// Unpaired insurance can be withdrawn immediately by MsgWithdrawInsurance.
INSURANCE_STATUS_UNPAIRED = 5;
}
Expand All @@ -150,13 +151,12 @@ enum InsuranceStatus {
message Insurance {
option (gogoproto.goproto_getters) = false;

// Id of the insurance
// Unique id increased by 1
uint64 id = 1;
// Address of the validator
string validator_address = 2;
// Address of the insurance provider
string provider_address = 3;
// Fee rate of the insurance
string fee_rate = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
Expand Down Expand Up @@ -191,7 +191,8 @@ message NetAmountState {

// Calculated by reward module account's native token balance + all paired
// chunk's native token balance + all delegation tokens of paired chunks
// last Epoch + all unbonding delegation tokens of unpairing chunks
// since last Epoch + all unbonding delegation tokens of unpairing chunks +
// reward module account's balance
string net_amount = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
Expand Down Expand Up @@ -246,7 +247,8 @@ message NetAmountState {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
// Total shares of all paired chunks

// Total delegation shares of all paired chunks
string total_del_shares = 12 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
Expand All @@ -267,7 +269,7 @@ message NetAmountState {
(gogoproto.nullable) = false
];

// The sum of unbonding balance of all chunks in Unpairing and
// The sum of unbonding balance of all chunks in Unpairing or
// UnpairingForUnstaking
string total_unbonding_chunks_balance = 15 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
Expand Down Expand Up @@ -303,24 +305,23 @@ message NetAmountState {
}

message UnpairingForUnstakingChunkInfo {
// The id of chunk
// Which chunk is unpairing for unstaking
uint64 chunk_id = 1;
// Address of delegator (=liquid unstaker)
string delegator_address = 2;
// Amount of ls tokens to burn
// How much lstokens will be burned when unbonding finished
string escrowed_lstokens = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin",
(gogoproto.nullable) = false
];
}

message WithdrawInsuranceRequest {
// The id of insurance
// Which insurance is requested for withdrawal
uint64 insurance_id = 1;
}

message RedelegationInfo {
// The id of chunk
uint64 chunk_id = 1;
google.protobuf.Timestamp completion_time = 2
[ (gogoproto.stdtime) = true, (gogoproto.nullable) = false ];
Expand Down
5 changes: 2 additions & 3 deletions proto/canto/liquidstaking/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ message MsgLiquidStake {
// delegator_address is the address of the user who requests the liquid
// staking.
string delegator_address = 1;
// amount is the amount of native token to be liquid staked.
// (How many chunks to liquid stake?) x ChunkSize
string amount = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin",
(gogoproto.nullable) = false
Expand All @@ -65,8 +65,7 @@ message MsgLiquidStakeResponse {}
message MsgLiquidUnstake {
// delegator_address is the address of the user who want to liquid unstaking.
string delegator_address = 1;
// amount is the number calculated by (number of chunks want to unstake) *
// chunk.size. The delegator must have corresponding ls tokens to unstake.
// (How many chunks to be unstaked?) x ChunkSize
string amount = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin",
(gogoproto.nullable) = false
Expand Down
60 changes: 31 additions & 29 deletions x/liquidstaking/keeper/invariants.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
UnpairingForUnstakingChunkInfosInvariant(k))
ir.RegisterRoute(types.ModuleName, "withdraw-insurance-requests",
WithdrawInsuranceRequestsInvariant(k))
ir.RegisterRoute(types.ModuleName, "redelegation-infos",
RedelegationInfosInvariant(k))
}

func AllInvariants(k Keeper) sdk.Invariant {
Expand Down Expand Up @@ -65,9 +67,9 @@ func ChunksInvariant(k Keeper) sdk.Invariant {
k.IterateAllChunks(ctx, func(chunk types.Chunk) bool {
switch chunk.Status {
case types.CHUNK_STATUS_PAIRING:
// must have empty paired insurance
// must have empty paired pairedIns
if chunk.HasPairedInsurance() {
msg += fmt.Sprintf("pairing chunk(id: %d) have non-empty paired insurance\n", chunk.Id)
msg += fmt.Sprintf("pairing chunk(id: %d) have non-empty paired pairedIns\n", chunk.Id)
brokenCount++
return false
}
Expand All @@ -86,27 +88,27 @@ func ChunksInvariant(k Keeper) sdk.Invariant {
brokenCount++
return false
}
insurance, found := k.GetInsurance(ctx, chunk.PairedInsuranceId)
pairedIns, found := k.GetInsurance(ctx, chunk.PairedInsuranceId)
if !found {
msg += fmt.Sprintf("not found paired insurance for paired chunk(id: %d)\n", chunk.Id)
brokenCount++
return false
}
if insurance.Status != types.INSURANCE_STATUS_PAIRED {
msg += fmt.Sprintf("paired chunk(id: %d) have paired insurance with invalid status: %s\n", chunk.Id, insurance.Status)
if pairedIns.Status != types.INSURANCE_STATUS_PAIRED {
msg += fmt.Sprintf("paired chunk(id: %d) have paired insurance with invalid status: %s\n", chunk.Id, pairedIns.Status)
brokenCount++
return false
}
// must have valid Delegation object
delegation, found := k.stakingKeeper.GetDelegation(ctx, chunk.DerivedAddress(), insurance.GetValidator())
delegation, found := k.stakingKeeper.GetDelegation(ctx, chunk.DerivedAddress(), pairedIns.GetValidator())
if !found {
msg += fmt.Sprintf("not found delegation for paired chunk(id: %d)\n", chunk.Id)
brokenCount++
return false
}
delShares := delegation.GetShares()
if delShares.LT(types.ChunkSize.ToDec()) {
msg += fmt.Sprintf("delegation tokens of paired chunk(id: %d) is less than chunk size tokens: %s\n", chunk.Id, delShares.String())
msg += fmt.Sprintf("paired chunk's delegation sharesis less than chunk size tokens: %s(chunkId: %d)\n", delShares.String(), chunk.Id)
brokenCount++
return false
}
Expand All @@ -117,7 +119,7 @@ func ChunksInvariant(k Keeper) sdk.Invariant {
brokenCount++
return false
}
insurance, found := k.GetInsurance(ctx, chunk.UnpairingInsuranceId)
unpairingIns, found := k.GetInsurance(ctx, chunk.UnpairingInsuranceId)
if !found {
msg += fmt.Sprintf("not found unpairing insurance for unpairing chunk(id: %d)\n", chunk.Id)
brokenCount++
Expand All @@ -130,7 +132,7 @@ func ChunksInvariant(k Keeper) sdk.Invariant {
return false
}
// must have unbonding delegation
ubd, found := k.stakingKeeper.GetUnbondingDelegation(ctx, chunk.DerivedAddress(), insurance.GetValidator())
ubd, found := k.stakingKeeper.GetUnbondingDelegation(ctx, chunk.DerivedAddress(), unpairingIns.GetValidator())
if !found {
msg += fmt.Sprintf("not found unbonding delegation for unpairing chunk(id: %d)\n", chunk.Id)
brokenCount++
Expand Down Expand Up @@ -162,69 +164,69 @@ func InsurancesInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
msg := ""
brokenCount := 0
k.IterateAllInsurances(ctx, func(insurance types.Insurance) bool {
switch insurance.Status {
k.IterateAllInsurances(ctx, func(ins types.Insurance) bool {
switch ins.Status {
case types.INSURANCE_STATUS_PAIRING:
// must have empty chunk
if insurance.HasChunk() {
msg += fmt.Sprintf("pairing insurance(id: %d) have non-empty paired chunk\n", insurance.Id)
if ins.HasChunk() {
msg += fmt.Sprintf("pairing insurance(id: %d) have non-empty paired chunk\n", ins.Id)
brokenCount++
return false
}
case types.INSURANCE_STATUS_PAIRED:
// must have paired chunk
if !insurance.HasChunk() {
msg += fmt.Sprintf("paired insurance(id: %d) have empty paired chunk\n", insurance.Id)
if !ins.HasChunk() {
msg += fmt.Sprintf("paired insurance(id: %d) have empty paired chunk\n", ins.Id)
brokenCount++
return false
}
chunk, found := k.GetChunk(ctx, insurance.ChunkId)
chunk, found := k.GetChunk(ctx, ins.ChunkId)
if !found {
msg += fmt.Sprintf("not found paired chunk for paired insurance(id: %d)\n", insurance.Id)
msg += fmt.Sprintf("not found paired chunk for paired insurance(id: %d)\n", ins.Id)
brokenCount++
return false
}
if chunk.Status != types.CHUNK_STATUS_PAIRED {
msg += fmt.Sprintf("paired insurance(id: %d) have invalid paired chunk status: %s\n", insurance.Id, chunk.Status)
msg += fmt.Sprintf("paired insurance(id: %d) have invalid paired chunk status: %s\n", ins.Id, chunk.Status)
brokenCount++
return false
}
case types.INSURANCE_STATUS_UNPAIRING:
// must have chunk to protect
if !insurance.HasChunk() {
msg += fmt.Sprintf("unpairing insurance(id: %d) have empty chunk to protect\n", insurance.Id)
if !ins.HasChunk() {
msg += fmt.Sprintf("unpairing insurance(id: %d) have empty chunk to protect\n", ins.Id)
brokenCount++
return false
}
_, found := k.GetChunk(ctx, insurance.ChunkId)
_, found := k.GetChunk(ctx, ins.ChunkId)
if !found {
msg += fmt.Sprintf("not found chunk to protect for unpairing insurance(id: %d)\n", insurance.Id)
msg += fmt.Sprintf("not found chunk to protect for unpairing insurance(id: %d)\n", ins.Id)
brokenCount++
return false
}

case types.INSURANCE_STATUS_UNPAIRED:
// must have empty chunk
if insurance.HasChunk() {
msg += fmt.Sprintf("unpaired insurance(id: %d) have non-empty paired chunk\n", insurance.Id)
if ins.HasChunk() {
msg += fmt.Sprintf("unpaired insurance(id: %d) have non-empty paired chunk\n", ins.Id)
brokenCount++
return false
}
case types.INSURANCE_STATUS_UNPAIRING_FOR_WITHDRAWAL:
// must have chunk to protect
if !insurance.HasChunk() {
msg += fmt.Sprintf("unpairing for withdrawal insurance(id: %d) have empty chunk to protect\n", insurance.Id)
if !ins.HasChunk() {
msg += fmt.Sprintf("unpairing for withdrawal insurance(id: %d) have empty chunk to protect\n", ins.Id)
brokenCount++
return false
}
_, found := k.GetChunk(ctx, insurance.ChunkId)
_, found := k.GetChunk(ctx, ins.ChunkId)
if !found {
msg += fmt.Sprintf("not found chunk to protect for unpairing for withdrawal insurance(id: %d)\n", insurance.Id)
msg += fmt.Sprintf("not found chunk to protect for unpairing for withdrawal insurance(id: %d)\n", ins.Id)
brokenCount++
return false
}
default:
msg += fmt.Sprintf("insurance(id: %d) have invalid status: %s\n", insurance.Id, insurance.Status)
msg += fmt.Sprintf("insurance(id: %d) have invalid status: %s\n", ins.Id, ins.Status)
brokenCount++
return false
}
Expand Down
Loading
Loading