Skip to content

Commit

Permalink
remove archive status unbonding records (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
sampocs authored Jan 24, 2024
1 parent 6eb3be1 commit bc303a1
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 169 deletions.
4 changes: 2 additions & 2 deletions proto/stride/staketia/staketia.proto
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ enum UnbondingRecordStatus {
// CLAIMABLE indicates the unbonded tokens have been swept to stride and are
// ready to be distributed to users
CLAIMABLE = 4;
// ARCHIVE indicates the full unbonding cycle has been completed
UNBONDING_ARCHIVE = 5;
// CLAIMED indicates the full unbonding cycle has been completed
CLAIMED = 5;
}

// DelegationRecords track the aggregate liquid stakes and delegations
Expand Down
20 changes: 11 additions & 9 deletions x/staketia/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,23 @@ func (s *KeeperTestSuite) TestQueryDelegationRecords() {

func (s *KeeperTestSuite) TestQueryUnbondingRecords() {
// Create active unbondin records
activeUnbondingRecords := s.addUnbondingRecords()
initialUnbondingRecords := s.addUnbondingRecords()

// Create an archived version of each of the above records by removing
// the record (i.e. archiving it) and then recreating it
// Create an archived version of each of the above records by archiving the record
// and then recreating it in the new store
archivedUnbondingRecords := []types.UnbondingRecord{}
for _, unbondingRecord := range activeUnbondingRecords {
activeUnbondingRecords := []types.UnbondingRecord{}
for _, unbondingRecord := range initialUnbondingRecords {
// Archive (which removes from the active store, and writes to the archive store)
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, unbondingRecord.Id)
archivedRecord := unbondingRecord
archivedRecord.Status = types.CLAIMED
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, archivedRecord)
archivedUnbondingRecords = append(archivedUnbondingRecords, archivedRecord)

// Set the original record back to the active store
unbondingRecord.Status = types.UNBONDING_QUEUE
s.App.StaketiaKeeper.SetUnbondingRecord(s.Ctx, unbondingRecord)

archivedRecord := unbondingRecord
archivedRecord.Status = types.UNBONDING_ARCHIVE
archivedUnbondingRecords = append(archivedUnbondingRecords, archivedRecord)
activeUnbondingRecords = append(activeUnbondingRecords, unbondingRecord)
}
allUnbondingRecords := append(activeUnbondingRecords, archivedUnbondingRecords...)

Expand Down
5 changes: 3 additions & 2 deletions x/staketia/keeper/unbonding.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (k Keeper) PrepareUndelegation(ctx sdk.Context, epochNumber uint64) error {

// If there were no unbondings this epoch, archive the current record
if totalNativeTokens.IsZero() {
k.ArchiveUnbondingRecord(ctx, unbondingRecord.Id)
k.ArchiveUnbondingRecord(ctx, unbondingRecord)
return nil
}

Expand Down Expand Up @@ -355,7 +355,8 @@ func (k Keeper) DistributeClaims(ctx sdk.Context) error {
}

// Once all claims have been distributed for a record, archive the record
k.ArchiveUnbondingRecord(ctx, unbondingRecord.Id)
unbondingRecord.Status = types.CLAIMED
k.ArchiveUnbondingRecord(ctx, unbondingRecord)
}

return nil
Expand Down
64 changes: 29 additions & 35 deletions x/staketia/keeper/unbonding_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ import (
"github.com/Stride-Labs/stride/v17/x/staketia/types"
)

// Writes an unbonding record to the store based on the status
// If the status is archive, it writes to the archive store, otherwise it writes to the active store
// Writes an unbonding record to the active store
func (k Keeper) SetUnbondingRecord(ctx sdk.Context, unbondingRecord types.UnbondingRecord) {
activeStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsKeyPrefix)
archiveStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsArchiveKeyPrefix)
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsKeyPrefix)

if unbondingRecord.Status == types.UNBONDING_ARCHIVE {
k.setUnbondingRecord(archiveStore, unbondingRecord)
} else {
k.setUnbondingRecord(activeStore, unbondingRecord)
}
recordKey := types.IntKey(unbondingRecord.Id)
recordValue := k.cdc.MustMarshal(&unbondingRecord)

store.Set(recordKey, recordValue)
}

// Writes an unbonding record to a specific store (either active or archive)
func (k Keeper) setUnbondingRecord(store prefix.Store, unbondingRecord types.UnbondingRecord) {
// Writes an unbonding record to the archive store
func (k Keeper) SetArchivedUnbondingRecord(ctx sdk.Context, unbondingRecord types.UnbondingRecord) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsArchiveKeyPrefix)

recordKey := types.IntKey(unbondingRecord.Id)
recordValue := k.cdc.MustMarshal(&unbondingRecord)

store.Set(recordKey, recordValue)
}

Expand All @@ -42,39 +42,33 @@ func (k Keeper) GetUnbondingRecord(ctx sdk.Context, recordId uint64) (unbondingR
return unbondingRecord, true
}

// Removes an unbonding record from the store
// NOTE: This is only used for testing - records should be archived in practice
func (k Keeper) RemoveUnbondingRecord(ctx sdk.Context, recordId uint64) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsKeyPrefix)
recordKey := types.IntKey(recordId)
store.Delete(recordKey)
}

// Removes a unbonding record from the store
// To preserve history, we write it to the archive store
func (k Keeper) ArchiveUnbondingRecord(ctx sdk.Context, recordId uint64) {
activeStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsKeyPrefix)
archiveStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsArchiveKeyPrefix)
// Reads a unbonding record from the archive store
func (k Keeper) GetArchivedUnbondingRecord(ctx sdk.Context, recordId uint64) (unbondingRecord types.UnbondingRecord, found bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsArchiveKeyPrefix)

recordKey := types.IntKey(recordId)
recordBz := activeStore.Get(recordKey)
recordBz := store.Get(recordKey)

// No action necessary if the record doesn't exist
if len(recordBz) == 0 {
return
return unbondingRecord, false
}

// Update the status to ARCHIVE
var unbondingRecord types.UnbondingRecord
k.cdc.MustUnmarshal(recordBz, &unbondingRecord)
unbondingRecord.Status = types.UNBONDING_ARCHIVE
recordBz = k.cdc.MustMarshal(&unbondingRecord)
return unbondingRecord, true
}

// Write the archived record to the store
archiveStore.Set(recordKey, recordBz)
// Removes an unbonding record from the active store
func (k Keeper) RemoveUnbondingRecord(ctx sdk.Context, recordId uint64) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.UnbondingRecordsKeyPrefix)
recordKey := types.IntKey(recordId)
store.Delete(recordKey)
}

// Then remove the original record from the active store
activeStore.Delete(recordKey)
// Removes a unbonding record from the active store, and writes it to the archive store
// to preserve history
func (k Keeper) ArchiveUnbondingRecord(ctx sdk.Context, unbondingRecord types.UnbondingRecord) {
k.RemoveUnbondingRecord(ctx, unbondingRecord.Id)
k.SetArchivedUnbondingRecord(ctx, unbondingRecord)
}

// Returns all active unbonding records
Expand Down
52 changes: 13 additions & 39 deletions x/staketia/keeper/unbonding_record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,6 @@ func (s *KeeperTestSuite) addUnbondingRecords() (unbondingRecords []types.Unbond
return unbondingRecords
}

// Tests that records are written to their respective stores based on the status
func (s *KeeperTestSuite) TestSetUnbondingRecord() {
unbondingRecords := []types.UnbondingRecord{
{Id: 1, Status: types.ACCUMULATING_REDEMPTIONS},
{Id: 2, Status: types.UNBONDING_IN_PROGRESS},
{Id: 3, Status: types.UNBONDING_QUEUE},
{Id: 4, Status: types.UNBONDED},
{Id: 5, Status: types.UNBONDING_ARCHIVE},
{Id: 6, Status: types.ACCUMULATING_REDEMPTIONS},
{Id: 7, Status: types.UNBONDING_IN_PROGRESS},
{Id: 8, Status: types.UNBONDING_QUEUE},
{Id: 9, Status: types.UNBONDED},
{Id: 10, Status: types.UNBONDING_ARCHIVE},
}
for _, unbondingRecord := range unbondingRecords {
s.App.StaketiaKeeper.SetUnbondingRecord(s.Ctx, unbondingRecord)
}

// Confirm the number of records in each store
s.Require().Len(s.App.StaketiaKeeper.GetAllActiveUnbondingRecords(s.Ctx), 8, "records in acitve store")
s.Require().Len(s.App.StaketiaKeeper.GetAllArchivedUnbondingRecords(s.Ctx), 2, "records in archive store")

// Check that only the non-archvied records are found in the active store
for i, unbondingRecord := range unbondingRecords {
expectedFound := unbondingRecord.Status != types.UNBONDING_ARCHIVE
_, actualFound := s.App.StaketiaKeeper.GetUnbondingRecord(s.Ctx, unbondingRecord.Id)
s.Require().Equal(expectedFound, actualFound, "record %d found in active store", i)
}
}

func (s *KeeperTestSuite) TestGetUnbondingRecord() {
unbondingRecords := s.addUnbondingRecords()

Expand All @@ -67,19 +37,23 @@ func (s *KeeperTestSuite) TestArchiveUnbondingRecord() {
unbondingRecords := s.addUnbondingRecords()

for removedIndex := 0; removedIndex < len(unbondingRecords); removedIndex++ {
// Remove from removed index
removedId := unbondingRecords[removedIndex].Id
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, removedId)
// Archive from removed index
removedRecord := unbondingRecords[removedIndex]
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, removedRecord)

// Confirm removed from the active store
_, found := s.App.StaketiaKeeper.GetUnbondingRecord(s.Ctx, removedRecord.Id)
s.Require().False(found, "record %d should have been removed", removedRecord.Id)

// Confirm removed
_, found := s.App.StaketiaKeeper.GetUnbondingRecord(s.Ctx, removedId)
s.Require().False(found, "record %d should have been removed", removedId)
// Confirm moved to the archive store
_, found = s.App.StaketiaKeeper.GetArchivedUnbondingRecord(s.Ctx, removedRecord.Id)
s.Require().True(found, "record %d should have been moved to the archive store", removedRecord.Id)

// Check all other records are still there
for checkedIndex := removedIndex + 1; checkedIndex < len(unbondingRecords); checkedIndex++ {
checkedId := unbondingRecords[checkedIndex].Id
_, found := s.App.StaketiaKeeper.GetUnbondingRecord(s.Ctx, checkedId)
s.Require().True(found, "record %d should have been removed after %d removal", checkedId, removedId)
s.Require().True(found, "record %d should have been removed after %d removal", checkedId, removedRecord.Id)
}
}

Expand Down Expand Up @@ -128,8 +102,8 @@ func (s *KeeperTestSuite) TestGetAccumulatingUnbondingRecord() {
s.Require().ErrorContains(err, "more than one record")

// Remove the ACCUMULATING records and confirm it errors
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, expectedRecordId)
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, duplicateAccumulatingRecordId)
s.App.StaketiaKeeper.RemoveUnbondingRecord(s.Ctx, expectedRecordId)
s.App.StaketiaKeeper.RemoveUnbondingRecord(s.Ctx, duplicateAccumulatingRecordId)

_, err = s.App.StaketiaKeeper.GetAccumulatingUnbondingRecord(s.Ctx)
s.Require().ErrorContains(err, "no unbonding record")
Expand Down
7 changes: 2 additions & 5 deletions x/staketia/keeper/unbonding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ func (s *KeeperTestSuite) TestConfirmUndelegation_Failure_NoRecordWithId() {

// archive the record (this is the only way to remove it from the active store)
tc.unbondingRecord.Status = types.UNBONDING_IN_PROGRESS
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, tc.unbondingRecord.Id)
s.App.StaketiaKeeper.ArchiveUnbondingRecord(s.Ctx, tc.unbondingRecord)

err := s.App.StaketiaKeeper.ConfirmUndelegation(s.Ctx, tc.unbondingRecord.Id, ValidTxHashDefault, tc.operatorAddress)
s.Require().Error(err, "couldn't find unbonding record")
Expand Down Expand Up @@ -800,7 +800,7 @@ func (s *KeeperTestSuite) GetDefaultUnbondingRecords() []types.UnbondingRecord {
},
{
Id: 4,
Status: types.UNBONDING_ARCHIVE,
Status: types.CLAIMABLE,
StTokenAmount: sdk.NewInt(400),
NativeAmount: sdk.NewInt(800),
UnbondingCompletionTimeSeconds: 5,
Expand Down Expand Up @@ -845,9 +845,6 @@ func (s *KeeperTestSuite) SetupTestConfirmUnbondingTokens(amount int64) {
func (s *KeeperTestSuite) VerifyUnbondingRecordsAfterConfirmSweep(verifyUpdatedFieldsIdentical bool) {
defaultUnbondingRecords := s.GetDefaultUnbondingRecords()
for _, defaultUnbondingRecord := range defaultUnbondingRecords {
if defaultUnbondingRecord.Status == types.UNBONDING_ARCHIVE {
continue
}
// grab relevant record in store
loadedUnbondingRecord, found := s.App.StaketiaKeeper.GetUnbondingRecord(s.Ctx, defaultUnbondingRecord.Id)
s.Require().True(found)
Expand Down
4 changes: 2 additions & 2 deletions x/staketia/types/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ func TestMsgOverwriteUnbondingRecord_ValidateBasic(t *testing.T) {
Creator: validAddress,
UnbondingRecord: &types.UnbondingRecord{
Id: 1,
Status: types.UNBONDING_ARCHIVE, // should work
Status: types.CLAIMABLE,
StTokenAmount: sdkmath.NewInt(11),
NativeAmount: sdkmath.NewInt(10),
UnbondingCompletionTimeSeconds: 1705857114,
Expand All @@ -768,7 +768,7 @@ func TestMsgOverwriteUnbondingRecord_ValidateBasic(t *testing.T) {
Creator: validAddress,
UnbondingRecord: &types.UnbondingRecord{
Id: 1,
Status: types.UNBONDING_ARCHIVE, // should work
Status: types.CLAIMABLE,
StTokenAmount: sdkmath.NewInt(11),
NativeAmount: sdkmath.NewInt(10),
UnbondingCompletionTimeSeconds: 1705857114,
Expand Down
Loading

0 comments on commit bc303a1

Please sign in to comment.