github.com/cosmos/cosmos-sdk@v0.50.10/x/slashing/migrations/v4/migrate.go (about)

     1  package v4
     2  
     3  import (
     4  	"github.com/bits-and-blooms/bitset"
     5  	gogotypes "github.com/cosmos/gogoproto/types"
     6  
     7  	"cosmossdk.io/errors"
     8  	storetypes "cosmossdk.io/store/types"
     9  
    10  	"github.com/cosmos/cosmos-sdk/codec"
    11  	sdk "github.com/cosmos/cosmos-sdk/types"
    12  	"github.com/cosmos/cosmos-sdk/x/slashing/types"
    13  )
    14  
    15  // Migrate migrates state to consensus version 4. Specifically, the migration
    16  // deletes all existing validator bitmap entries and replaces them with a real
    17  // "chunked" bitmap.
    18  func Migrate(ctx sdk.Context, cdc codec.BinaryCodec, store storetypes.KVStore, params types.Params) error {
    19  	// Get all the missed blocks for each validator, based on the existing signing
    20  	// info.
    21  	var missedBlocks []types.ValidatorMissedBlocks
    22  	iterateValidatorSigningInfos(ctx, cdc, store, func(addr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) {
    23  		bechAddr := addr.String()
    24  		localMissedBlocks := GetValidatorMissedBlocks(ctx, cdc, store, addr, params)
    25  
    26  		missedBlocks = append(missedBlocks, types.ValidatorMissedBlocks{
    27  			Address:      bechAddr,
    28  			MissedBlocks: localMissedBlocks,
    29  		})
    30  
    31  		return false
    32  	})
    33  
    34  	// For each missed blocks entry, of which there should only be one per validator,
    35  	// we clear all the old entries and insert the new chunked entry.
    36  	for _, mb := range missedBlocks {
    37  		addr, err := sdk.ConsAddressFromBech32(mb.Address)
    38  		if err != nil {
    39  			return err
    40  		}
    41  
    42  		deleteValidatorMissedBlockBitArray(ctx, store, addr)
    43  
    44  		for _, b := range mb.MissedBlocks {
    45  			// Note: It is not necessary to store entries with missed=false, i.e. where
    46  			// the bit is zero, since when the bitmap is initialized, all non-set bits
    47  			// are already zero.
    48  			if b.Missed {
    49  				if err := setMissedBlockBitmapValue(ctx, store, addr, b.Index, true); err != nil {
    50  					return err
    51  				}
    52  			}
    53  		}
    54  	}
    55  
    56  	return nil
    57  }
    58  
    59  func iterateValidatorSigningInfos(
    60  	ctx sdk.Context,
    61  	cdc codec.BinaryCodec,
    62  	store storetypes.KVStore,
    63  	cb func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool),
    64  ) {
    65  	iter := storetypes.KVStorePrefixIterator(store, ValidatorSigningInfoKeyPrefix)
    66  	defer iter.Close()
    67  
    68  	for ; iter.Valid(); iter.Next() {
    69  		address := ValidatorSigningInfoAddress(iter.Key())
    70  		var info types.ValidatorSigningInfo
    71  		cdc.MustUnmarshal(iter.Value(), &info)
    72  
    73  		if cb(address, info) {
    74  			break
    75  		}
    76  	}
    77  }
    78  
    79  func iterateValidatorMissedBlockBitArray(
    80  	ctx sdk.Context,
    81  	cdc codec.BinaryCodec,
    82  	store storetypes.KVStore,
    83  	addr sdk.ConsAddress,
    84  	params types.Params,
    85  	cb func(index int64, missed bool) (stop bool),
    86  ) {
    87  	for i := int64(0); i < params.SignedBlocksWindow; i++ {
    88  		var missed gogotypes.BoolValue
    89  		bz := store.Get(ValidatorMissedBlockBitArrayKey(addr, i))
    90  		if bz == nil {
    91  			continue
    92  		}
    93  
    94  		cdc.MustUnmarshal(bz, &missed)
    95  		if cb(i, missed.Value) {
    96  			break
    97  		}
    98  	}
    99  }
   100  
   101  func GetValidatorMissedBlocks(
   102  	ctx sdk.Context,
   103  	cdc codec.BinaryCodec,
   104  	store storetypes.KVStore,
   105  	addr sdk.ConsAddress,
   106  	params types.Params,
   107  ) []types.MissedBlock {
   108  	var missedBlocks []types.MissedBlock
   109  	iterateValidatorMissedBlockBitArray(ctx, cdc, store, addr, params, func(index int64, missed bool) (stop bool) {
   110  		missedBlocks = append(missedBlocks, types.NewMissedBlock(index, missed))
   111  		return false
   112  	})
   113  
   114  	return missedBlocks
   115  }
   116  
   117  func deleteValidatorMissedBlockBitArray(ctx sdk.Context, store storetypes.KVStore, addr sdk.ConsAddress) {
   118  	iter := storetypes.KVStorePrefixIterator(store, validatorMissedBlockBitArrayPrefixKey(addr))
   119  	defer iter.Close()
   120  
   121  	for ; iter.Valid(); iter.Next() {
   122  		store.Delete(iter.Key())
   123  	}
   124  }
   125  
   126  func setMissedBlockBitmapValue(ctx sdk.Context, store storetypes.KVStore, addr sdk.ConsAddress, index int64, missed bool) error {
   127  	// get the chunk or "word" in the logical bitmap
   128  	chunkIndex := index / MissedBlockBitmapChunkSize
   129  	key := ValidatorMissedBlockBitmapKey(addr, chunkIndex)
   130  
   131  	bs := bitset.New(uint(MissedBlockBitmapChunkSize))
   132  	chunk := store.Get(key)
   133  	if chunk != nil {
   134  		if err := bs.UnmarshalBinary(chunk); err != nil {
   135  			return errors.Wrapf(err, "failed to decode bitmap chunk; index: %d", index)
   136  		}
   137  	}
   138  
   139  	// get the bit position in the chunk of the logical bitmap
   140  	bitIndex := uint(index % MissedBlockBitmapChunkSize)
   141  	if missed {
   142  		bs.Set(bitIndex)
   143  	} else {
   144  		bs.Clear(bitIndex)
   145  	}
   146  
   147  	updatedChunk, err := bs.MarshalBinary()
   148  	if err != nil {
   149  		return errors.Wrapf(err, "failed to encode bitmap chunk; index: %d", index)
   150  	}
   151  
   152  	store.Set(key, updatedChunk)
   153  	return nil
   154  }