Handle record and authority expiry in registry module #7

Merged
ashwin merged 5 commits from pm-record-expiry into main 2024-02-26 06:16:10 +00:00
5 changed files with 174 additions and 17 deletions
Showing only changes of commit b3b92c7955 - Show all commits

View File

@ -22,11 +22,13 @@ func (k *Keeper) InitGenesis(ctx sdk.Context, data *registry.GenesisState) error
// Add to record expiry queue if expiry time is in the future. // Add to record expiry queue if expiry time is in the future.
expiryTime, err := time.Parse(time.RFC3339, record.ExpiryTime) expiryTime, err := time.Parse(time.RFC3339, record.ExpiryTime)
if err != nil { if err != nil {
panic(err) return err
} }
if expiryTime.After(ctx.BlockTime()) { if expiryTime.After(ctx.BlockTime()) {
k.insertRecordExpiryQueue(ctx, record) if err := k.insertRecordExpiryQueue(ctx, record); err != nil {
return err
}
} }
} }
@ -37,9 +39,10 @@ func (k *Keeper) InitGenesis(ctx sdk.Context, data *registry.GenesisState) error
return err return err
} }
// TODO
// Add authority name to expiry queue. // Add authority name to expiry queue.
// k.InsertAuthorityExpiryQueue(ctx, authority.Name, authority.Entry.ExpiryTime) if err := k.insertAuthorityExpiryQueue(ctx, authority.Name, authority.Entry.ExpiryTime); err != nil {
return err
}
// TODO // TODO
// Note: Bond genesis runs first, so bonds will already be present. // Note: Bond genesis runs first, so bonds will already be present.

View File

@ -92,12 +92,13 @@ type Keeper struct {
auctionKeeper auctionkeeper.Keeper auctionKeeper auctionkeeper.Keeper
// state management // state management
Schema collections.Schema Schema collections.Schema
Params collections.Item[registrytypes.Params] Params collections.Item[registrytypes.Params]
Records *collections.IndexedMap[string, registrytypes.Record, RecordsIndexes] Records *collections.IndexedMap[string, registrytypes.Record, RecordsIndexes]
Authorities *collections.IndexedMap[string, registrytypes.NameAuthority, AuthoritiesIndexes] Authorities *collections.IndexedMap[string, registrytypes.NameAuthority, AuthoritiesIndexes]
NameRecords *collections.IndexedMap[string, registrytypes.NameRecord, NameRecordsIndexes] NameRecords *collections.IndexedMap[string, registrytypes.NameRecord, NameRecordsIndexes]
RecordExpiryQueue collections.Map[time.Time, registrytypes.ExpiryQueue] RecordExpiryQueue collections.Map[time.Time, registrytypes.ExpiryQueue]
AuthorityExpiryQueue collections.Map[time.Time, registrytypes.ExpiryQueue]
} }
// NewKeeper creates a new Keeper instance // NewKeeper creates a new Keeper instance
@ -138,6 +139,10 @@ func NewKeeper(
sb, registrytypes.RecordExpiryQueuePrefix, "record_expiry_queue", sb, registrytypes.RecordExpiryQueuePrefix, "record_expiry_queue",
sdk.TimeKey, codec.CollValue[registrytypes.ExpiryQueue](cdc), sdk.TimeKey, codec.CollValue[registrytypes.ExpiryQueue](cdc),
), ),
AuthorityExpiryQueue: collections.NewMap(
sb, registrytypes.AuthorityExpiryQueuePrefix, "authority_expiry_queue",
sdk.TimeKey, codec.CollValue[registrytypes.ExpiryQueue](cdc),
),
} }
schema, err := sb.Build() schema, err := sb.Build()

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"strings" "strings"
"time"
"cosmossdk.io/collections" "cosmossdk.io/collections"
errorsmod "cosmossdk.io/errors" errorsmod "cosmossdk.io/errors"
@ -318,10 +319,7 @@ func (k Keeper) createAuthority(ctx sdk.Context, name string, owner string, isRo
return err return err
} }
// TODO return k.insertAuthorityExpiryQueue(ctx, name, authority.ExpiryTime)
// k.InsertAuthorityExpiryQueue(ctx, name, authority.ExpiryTime)
return nil
} }
func (k Keeper) SetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthorityBond) error { func (k Keeper) SetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthorityBond) error {
@ -520,6 +518,149 @@ func (k Keeper) checkCRNAccess(ctx sdk.Context, signer sdk.AccAddress, crn strin
return nil return nil
} }
// ProcessAuthorityExpiryQueue tries to renew expiring authorities (by collecting rent) else marks them as expired.
func (k Keeper) ProcessAuthorityExpiryQueue(ctx sdk.Context) error {
names, err := k.getAllExpiredAuthorities(ctx, ctx.BlockHeader().Time)
if err != nil {
return err
}
for _, name := range names {
authority, err := k.GetNameAuthority(ctx, name)
if err != nil {
return err
}
bondExists := false
if authority.BondId != "" {
bondExists, err = k.bondKeeper.HasBond(ctx, authority.BondId)
if err != nil {
return err
}
}
// If authority doesn't have an associated bond or if bond no longer exists, mark it expired.
if !bondExists {
authority.Status = registrytypes.AuthorityExpired
if err = k.SaveNameAuthority(ctx, name, &authority); err != nil {
return err
}
if err = k.deleteAuthorityExpiryQueue(ctx, name, authority); err != nil {
return err
}
// TODO: Setup logger
// k.Logger(ctx).Info(fmt.Sprintf("Marking authority expired as no bond present: %s", name))
return nil
}
// Try to renew the authority by taking rent.
if err := k.tryTakeAuthorityRent(ctx, name, authority); err != nil {
return err
}
}
return nil
}
// getAllExpiredAuthorities returns a concatenated list of all the timeslices before currTime.
func (k Keeper) getAllExpiredAuthorities(ctx sdk.Context, currTime time.Time) ([]string, error) {
var expiredAuthorityNames []string
// Get all the authorities with expiry time until currTime
rng := new(collections.Range[time.Time]).EndInclusive(currTime)
err := k.AuthorityExpiryQueue.Walk(ctx, rng, func(key time.Time, value registrytypes.ExpiryQueue) (stop bool, err error) {
expiredAuthorityNames = append(expiredAuthorityNames, value.Value...)
return false, nil
})
if err != nil {
return []string{}, err
}
return expiredAuthorityNames, nil
}
func (k Keeper) insertAuthorityExpiryQueue(ctx sdk.Context, name string, expiryTime time.Time) error {
existingNamesList, err := k.AuthorityExpiryQueue.Get(ctx, expiryTime)
if err != nil {
if errors.Is(err, collections.ErrNotFound) {
existingNamesList = registrytypes.ExpiryQueue{
Id: expiryTime.String(),
Value: []string{},
}
} else {
return err
}
}
existingNamesList.Value = append(existingNamesList.Value, name)
return k.AuthorityExpiryQueue.Set(ctx, expiryTime, existingNamesList)
}
// deleteAuthorityExpiryQueue deletes an authority name from the authority expiry queue.
func (k Keeper) deleteAuthorityExpiryQueue(ctx sdk.Context, name string, authority registrytypes.NameAuthority) error {
expiryTime := authority.ExpiryTime
existingNamesList, err := k.AuthorityExpiryQueue.Get(ctx, expiryTime)
if err != nil {
return err
}
newNamesSlice := []string{}
for _, id := range existingNamesList.Value {
if id != name {
newNamesSlice = append(newNamesSlice, id)
}
}
if len(existingNamesList.Value) == 0 {
return k.AuthorityExpiryQueue.Remove(ctx, expiryTime)
} else {
existingNamesList.Value = newNamesSlice
return k.AuthorityExpiryQueue.Set(ctx, expiryTime, existingNamesList)
}
}
// tryTakeAuthorityRent tries to take rent from the authority bond.
func (k Keeper) tryTakeAuthorityRent(ctx sdk.Context, name string, authority registrytypes.NameAuthority) error {
// k.Logger(ctx).Info(fmt.Sprintf("Trying to take rent for authority: %s", name))
params, err := k.GetParams(ctx)
if err != nil {
return err
}
rent := params.AuthorityRent
sdkErr := k.bondKeeper.TransferCoinsToModuleAccount(ctx, authority.BondId, registrytypes.AuthorityRentModuleAccountName, sdk.NewCoins(rent))
if sdkErr != nil {
// Insufficient funds, mark authority as expired.
authority.Status = registrytypes.AuthorityExpired
if err := k.SaveNameAuthority(ctx, name, &authority); err != nil {
return err
}
// k.Logger(ctx).Info(fmt.Sprintf("Insufficient funds in owner account to pay authority rent, marking as expired: %s", name))
return k.deleteAuthorityExpiryQueue(ctx, name, authority)
}
// Delete old expiry queue entry, create new one.
if err := k.deleteAuthorityExpiryQueue(ctx, name, authority); err != nil {
return err
}
authority.ExpiryTime = ctx.BlockTime().Add(params.AuthorityRentDuration)
if err := k.insertAuthorityExpiryQueue(ctx, name, authority.ExpiryTime); err != nil {
return err
}
// Save authority.
authority.Status = registrytypes.AuthorityActive
// k.Logger(ctx).Info(fmt.Sprintf("Authority rent paid successfully: %s", name))
return k.SaveNameAuthority(ctx, name, &authority)
}
func getAuthorityPubKey(pubKey cryptotypes.PubKey) string { func getAuthorityPubKey(pubKey cryptotypes.PubKey) string {
if pubKey == nil { if pubKey == nil {
return "" return ""

View File

@ -28,5 +28,6 @@ var (
NameRecordsPrefix = collections.NewPrefix(6) NameRecordsPrefix = collections.NewPrefix(6)
NameRecordsByCidIndexPrefix = collections.NewPrefix(7) NameRecordsByCidIndexPrefix = collections.NewPrefix(7)
RecordExpiryQueuePrefix = collections.NewPrefix(8) RecordExpiryQueuePrefix = collections.NewPrefix(8)
AuthorityExpiryQueuePrefix = collections.NewPrefix(9)
) )

View File

@ -13,6 +13,13 @@ func EndBlocker(ctx context.Context, k keeper.Keeper) error {
// TODO: Implement // TODO: Implement
sdkCtx := sdk.UnwrapSDKContext(ctx) sdkCtx := sdk.UnwrapSDKContext(ctx)
return k.ProcessRecordExpiryQueue(sdkCtx) if err := k.ProcessRecordExpiryQueue(sdkCtx); err != nil {
// k.ProcessAuthorityExpiryQueue(ctx) return err
}
if err := k.ProcessAuthorityExpiryQueue(sdkCtx); err != nil {
return err
}
return nil
} }