github.com/cosmos/cosmos-sdk@v0.50.10/x/group/internal/orm/index.go (about)

     1  package orm
     2  
     3  import (
     4  	"bytes"
     5  
     6  	"github.com/cosmos/gogoproto/proto"
     7  
     8  	errorsmod "cosmossdk.io/errors"
     9  	"cosmossdk.io/store/prefix"
    10  	"cosmossdk.io/store/types"
    11  
    12  	"github.com/cosmos/cosmos-sdk/types/query"
    13  	"github.com/cosmos/cosmos-sdk/x/group/errors"
    14  )
    15  
    16  // indexer creates and modifies the second MultiKeyIndex based on the operations and changes on the primary object.
    17  type indexer interface {
    18  	OnCreate(store types.KVStore, rowID RowID, value interface{}) error
    19  	OnDelete(store types.KVStore, rowID RowID, value interface{}) error
    20  	OnUpdate(store types.KVStore, rowID RowID, newValue, oldValue interface{}) error
    21  }
    22  
    23  var _ Index = &MultiKeyIndex{}
    24  
    25  // MultiKeyIndex is an index where multiple entries can point to the same underlying object as opposite to a unique index
    26  // where only one entry is allowed.
    27  type MultiKeyIndex struct {
    28  	prefix      byte
    29  	rowGetter   RowGetter
    30  	indexer     indexer
    31  	indexerFunc IndexerFunc
    32  	indexKey    interface{}
    33  }
    34  
    35  // NewIndex builds a MultiKeyIndex.
    36  // Only single-field indexes are supported and `indexKey` represents such a field value,
    37  // which can be []byte, string or uint64.
    38  func NewIndex(tb Indexable, prefix byte, indexerF IndexerFunc, indexKey interface{}) (MultiKeyIndex, error) {
    39  	indexer, err := NewIndexer(indexerF)
    40  	if err != nil {
    41  		return MultiKeyIndex{}, err
    42  	}
    43  	return newIndex(tb, prefix, indexer, indexer.IndexerFunc(), indexKey)
    44  }
    45  
    46  func newIndex(tb Indexable, prefix byte, indexer *Indexer, indexerF IndexerFunc, indexKey interface{}) (MultiKeyIndex, error) {
    47  	rowGetter := tb.RowGetter()
    48  	if rowGetter == nil {
    49  		return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("rowGetter must not be nil")
    50  	}
    51  	if indexKey == nil {
    52  		return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("indexKey must not be nil")
    53  	}
    54  
    55  	// Verify indexKey type is bytes, string or uint64
    56  	switch indexKey.(type) {
    57  	case []byte, string, uint64:
    58  	default:
    59  		return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("indexKey must be []byte, string or uint64")
    60  	}
    61  
    62  	idx := MultiKeyIndex{
    63  		prefix:      prefix,
    64  		rowGetter:   rowGetter,
    65  		indexer:     indexer,
    66  		indexerFunc: indexerF,
    67  		indexKey:    indexKey,
    68  	}
    69  	tb.AddAfterSetInterceptor(idx.onSet)
    70  	tb.AddAfterDeleteInterceptor(idx.onDelete)
    71  	return idx, nil
    72  }
    73  
    74  // Has checks if a key exists. Returns an error on nil key.
    75  func (i MultiKeyIndex) Has(store types.KVStore, key interface{}) (bool, error) {
    76  	pStore := prefix.NewStore(store, []byte{i.prefix})
    77  	encodedKey, err := keyPartBytes(key, false)
    78  	if err != nil {
    79  		return false, err
    80  	}
    81  	it := pStore.Iterator(PrefixRange(encodedKey))
    82  	defer it.Close()
    83  	return it.Valid(), nil
    84  }
    85  
    86  // Get returns a result iterator for the searchKey. Parameters must not be nil.
    87  func (i MultiKeyIndex) Get(store types.KVStore, searchKey interface{}) (Iterator, error) {
    88  	pStore := prefix.NewStore(store, []byte{i.prefix})
    89  	encodedKey, err := keyPartBytes(searchKey, false)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	it := pStore.Iterator(PrefixRange(encodedKey))
    94  	return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
    95  }
    96  
    97  // GetPaginated creates an iterator for the searchKey
    98  // starting from pageRequest.Key if provided.
    99  // The pageRequest.Key is the rowID while searchKey is a MultiKeyIndex key.
   100  func (i MultiKeyIndex) GetPaginated(store types.KVStore, searchKey interface{}, pageRequest *query.PageRequest) (Iterator, error) {
   101  	pStore := prefix.NewStore(store, []byte{i.prefix})
   102  	encodedKey, err := keyPartBytes(searchKey, false)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	start, end := PrefixRange(encodedKey)
   107  
   108  	if pageRequest != nil && len(pageRequest.Key) != 0 {
   109  		var err error
   110  		start, err = buildKeyFromParts([]interface{}{searchKey, pageRequest.Key})
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  	}
   115  	it := pStore.Iterator(start, end)
   116  	return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
   117  }
   118  
   119  // PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
   120  // Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
   121  // Iterator must be closed by caller.
   122  // To iterate over entire domain, use PrefixScan(nil, nil)
   123  //
   124  // WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
   125  // this as an endpoint to the public without further limits.
   126  // Example:
   127  //
   128  //	it, err := idx.PrefixScan(ctx, start, end)
   129  //	if err !=nil {
   130  //		return err
   131  //	}
   132  //	const defaultLimit = 20
   133  //	it = LimitIterator(it, defaultLimit)
   134  //
   135  // CONTRACT: No writes may happen within a domain while an iterator exists over it.
   136  func (i MultiKeyIndex) PrefixScan(store types.KVStore, startI, endI interface{}) (Iterator, error) {
   137  	start, end, err := getStartEndBz(startI, endI)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	pStore := prefix.NewStore(store, []byte{i.prefix})
   143  	it := pStore.Iterator(start, end)
   144  	return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
   145  }
   146  
   147  // ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
   148  // Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid  and error is returned.
   149  // Iterator must be closed by caller.
   150  // To iterate over entire domain, use PrefixScan(nil, nil)
   151  //
   152  // WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
   153  // this as an endpoint to the public without further limits. See `LimitIterator`
   154  //
   155  // CONTRACT: No writes may happen within a domain while an iterator exists over it.
   156  func (i MultiKeyIndex) ReversePrefixScan(store types.KVStore, startI, endI interface{}) (Iterator, error) {
   157  	start, end, err := getStartEndBz(startI, endI)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	pStore := prefix.NewStore(store, []byte{i.prefix})
   163  	it := pStore.ReverseIterator(start, end)
   164  	return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
   165  }
   166  
   167  // getStartEndBz gets the start and end bytes to be passed into the SDK store
   168  // iterator.
   169  func getStartEndBz(startI, endI interface{}) ([]byte, []byte, error) {
   170  	start, err := getPrefixScanKeyBytes(startI)
   171  	if err != nil {
   172  		return nil, nil, err
   173  	}
   174  	end, err := getPrefixScanKeyBytes(endI)
   175  	if err != nil {
   176  		return nil, nil, err
   177  	}
   178  
   179  	if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
   180  		return nil, nil, errorsmod.Wrap(errors.ErrORMInvalidArgument, "start must be less than end")
   181  	}
   182  
   183  	return start, end, nil
   184  }
   185  
   186  func getPrefixScanKeyBytes(keyI interface{}) ([]byte, error) {
   187  	var (
   188  		key []byte
   189  		err error
   190  	)
   191  	// nil value are accepted in the context of PrefixScans
   192  	if keyI == nil {
   193  		return nil, nil
   194  	}
   195  	key, err = keyPartBytes(keyI, false)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	return key, nil
   200  }
   201  
   202  func (i MultiKeyIndex) onSet(store types.KVStore, rowID RowID, newValue, oldValue proto.Message) error {
   203  	pStore := prefix.NewStore(store, []byte{i.prefix})
   204  	if oldValue == nil {
   205  		return i.indexer.OnCreate(pStore, rowID, newValue)
   206  	}
   207  	return i.indexer.OnUpdate(pStore, rowID, newValue, oldValue)
   208  }
   209  
   210  func (i MultiKeyIndex) onDelete(store types.KVStore, rowID RowID, oldValue proto.Message) error {
   211  	pStore := prefix.NewStore(store, []byte{i.prefix})
   212  	return i.indexer.OnDelete(pStore, rowID, oldValue)
   213  }
   214  
   215  type UniqueIndex struct {
   216  	MultiKeyIndex
   217  }
   218  
   219  // NewUniqueIndex create a new Index object where duplicate keys are prohibited.
   220  func NewUniqueIndex(tb Indexable, prefix byte, uniqueIndexerFunc UniqueIndexerFunc, indexKey interface{}) (UniqueIndex, error) {
   221  	uniqueIndexer, err := NewUniqueIndexer(uniqueIndexerFunc)
   222  	if err != nil {
   223  		return UniqueIndex{}, err
   224  	}
   225  	multiKeyIndex, err := newIndex(tb, prefix, uniqueIndexer, uniqueIndexer.IndexerFunc(), indexKey)
   226  	if err != nil {
   227  		return UniqueIndex{}, err
   228  	}
   229  	return UniqueIndex{
   230  		MultiKeyIndex: multiKeyIndex,
   231  	}, nil
   232  }
   233  
   234  // indexIterator uses rowGetter to lazy load new model values on request.
   235  type indexIterator struct {
   236  	store     types.KVStore
   237  	rowGetter RowGetter
   238  	it        types.Iterator
   239  	indexKey  interface{}
   240  }
   241  
   242  // LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
   243  // are no more items the errors.ErrORMIteratorDone error is returned
   244  // The key is the rowID and not any MultiKeyIndex key.
   245  func (i indexIterator) LoadNext(dest proto.Message) (RowID, error) {
   246  	if !i.it.Valid() {
   247  		return nil, errors.ErrORMIteratorDone
   248  	}
   249  	indexPrefixKey := i.it.Key()
   250  	rowID, err := stripRowID(indexPrefixKey, i.indexKey)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	i.it.Next()
   255  	return rowID, i.rowGetter(i.store, rowID, dest)
   256  }
   257  
   258  // Close releases the iterator and should be called at the end of iteration
   259  func (i indexIterator) Close() error {
   260  	return i.it.Close()
   261  }
   262  
   263  // PrefixRange turns a prefix into a (start, end) range. The start is the given prefix value and
   264  // the end is calculated by adding 1 bit to the start value. Nil is not allowed as prefix.
   265  //
   266  //	Example: []byte{1, 3, 4} becomes []byte{1, 3, 5}
   267  //			 []byte{15, 42, 255, 255} becomes []byte{15, 43, 0, 0}
   268  //
   269  // In case of an overflow the end is set to nil.
   270  //
   271  //	Example: []byte{255, 255, 255, 255} becomes nil
   272  func PrefixRange(prefix []byte) ([]byte, []byte) {
   273  	if prefix == nil {
   274  		panic("nil key not allowed")
   275  	}
   276  	// special case: no prefix is whole range
   277  	if len(prefix) == 0 {
   278  		return nil, nil
   279  	}
   280  
   281  	// copy the prefix and update last byte
   282  	end := make([]byte, len(prefix))
   283  	copy(end, prefix)
   284  	l := len(end) - 1
   285  	end[l]++
   286  
   287  	// wait, what if that overflowed?....
   288  	for end[l] == 0 && l > 0 {
   289  		l--
   290  		end[l]++
   291  	}
   292  
   293  	// okay, funny guy, you gave us FFF, no end to this range...
   294  	if l == 0 && end[0] == 0 {
   295  		end = nil
   296  	}
   297  	return prefix, end
   298  }