github.com/number571/tendermint@v0.34.11-gost/state/indexer/tx/kv/kv.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/gogo/protobuf/proto"
    11  	"github.com/google/orderedcode"
    12  	dbm "github.com/tendermint/tm-db"
    13  
    14  	abci "github.com/number571/tendermint/abci/types"
    15  	"github.com/number571/tendermint/libs/pubsub/query"
    16  	indexer "github.com/number571/tendermint/state/indexer"
    17  	"github.com/number571/tendermint/types"
    18  )
    19  
    20  var _ indexer.TxIndexer = (*TxIndex)(nil)
    21  
    22  // TxIndex is the simplest possible indexer
    23  // It is backed by two kv stores:
    24  // 1. txhash - result  (primary key)
    25  // 2. event - txhash   (secondary key)
    26  type TxIndex struct {
    27  	store dbm.DB
    28  }
    29  
    30  // NewTxIndex creates new KV indexer.
    31  func NewTxIndex(store dbm.DB) *TxIndex {
    32  	return &TxIndex{
    33  		store: store,
    34  	}
    35  }
    36  
    37  // Get gets transaction from the TxIndex storage and returns it or nil if the
    38  // transaction is not found.
    39  func (txi *TxIndex) Get(hash []byte) (*abci.TxResult, error) {
    40  	if len(hash) == 0 {
    41  		return nil, indexer.ErrorEmptyHash
    42  	}
    43  
    44  	rawBytes, err := txi.store.Get(primaryKey(hash))
    45  	if err != nil {
    46  		panic(err)
    47  	}
    48  	if rawBytes == nil {
    49  		return nil, nil
    50  	}
    51  
    52  	txResult := new(abci.TxResult)
    53  	err = proto.Unmarshal(rawBytes, txResult)
    54  	if err != nil {
    55  		return nil, fmt.Errorf("error reading TxResult: %v", err)
    56  	}
    57  
    58  	return txResult, nil
    59  }
    60  
    61  // Index indexes transactions using the given list of events. Each key
    62  // that indexed from the tx's events is a composite of the event type and the
    63  // respective attribute's key delimited by a "." (eg. "account.number").
    64  // Any event with an empty type is not indexed.
    65  func (txi *TxIndex) Index(results []*abci.TxResult) error {
    66  	b := txi.store.NewBatch()
    67  	defer b.Close()
    68  
    69  	for _, result := range results {
    70  		hash := types.Tx(result.Tx).Hash()
    71  
    72  		// index tx by events
    73  		err := txi.indexEvents(result, hash, b)
    74  		if err != nil {
    75  			return err
    76  		}
    77  
    78  		// index by height (always)
    79  		err = b.Set(KeyFromHeight(result), hash)
    80  		if err != nil {
    81  			return err
    82  		}
    83  
    84  		rawBytes, err := proto.Marshal(result)
    85  		if err != nil {
    86  			return err
    87  		}
    88  		// index by hash (always)
    89  		err = b.Set(primaryKey(hash), rawBytes)
    90  		if err != nil {
    91  			return err
    92  		}
    93  	}
    94  
    95  	return b.WriteSync()
    96  }
    97  
    98  func (txi *TxIndex) indexEvents(result *abci.TxResult, hash []byte, store dbm.Batch) error {
    99  	for _, event := range result.Result.Events {
   100  		// only index events with a non-empty type
   101  		if len(event.Type) == 0 {
   102  			continue
   103  		}
   104  
   105  		for _, attr := range event.Attributes {
   106  			if len(attr.Key) == 0 {
   107  				continue
   108  			}
   109  
   110  			// index if `index: true` is set
   111  			compositeTag := fmt.Sprintf("%s.%s", event.Type, attr.Key)
   112  			// ensure event does not conflict with a reserved prefix key
   113  			if compositeTag == types.TxHashKey || compositeTag == types.TxHeightKey {
   114  				return fmt.Errorf("event type and attribute key \"%s\" is reserved; please use a different key", compositeTag)
   115  			}
   116  			if attr.GetIndex() {
   117  				err := store.Set(keyFromEvent(compositeTag, attr.Value, result), hash)
   118  				if err != nil {
   119  					return err
   120  				}
   121  			}
   122  		}
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // Search performs a search using the given query.
   129  //
   130  // It breaks the query into conditions (like "tx.height > 5"). For each
   131  // condition, it queries the DB index. One special use cases here: (1) if
   132  // "tx.hash" is found, it returns tx result for it (2) for range queries it is
   133  // better for the client to provide both lower and upper bounds, so we are not
   134  // performing a full scan. Results from querying indexes are then intersected
   135  // and returned to the caller, in no particular order.
   136  //
   137  // Search will exit early and return any result fetched so far,
   138  // when a message is received on the context chan.
   139  func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*abci.TxResult, error) {
   140  	select {
   141  	case <-ctx.Done():
   142  		return make([]*abci.TxResult, 0), nil
   143  
   144  	default:
   145  	}
   146  
   147  	var hashesInitialized bool
   148  	filteredHashes := make(map[string][]byte)
   149  
   150  	// get a list of conditions (like "tx.height > 5")
   151  	conditions, err := q.Conditions()
   152  	if err != nil {
   153  		return nil, fmt.Errorf("error during parsing conditions from query: %w", err)
   154  	}
   155  
   156  	// if there is a hash condition, return the result immediately
   157  	hash, ok, err := lookForHash(conditions)
   158  	if err != nil {
   159  		return nil, fmt.Errorf("error during searching for a hash in the query: %w", err)
   160  	} else if ok {
   161  		res, err := txi.Get(hash)
   162  		switch {
   163  		case err != nil:
   164  			return []*abci.TxResult{}, fmt.Errorf("error while retrieving the result: %w", err)
   165  		case res == nil:
   166  			return []*abci.TxResult{}, nil
   167  		default:
   168  			return []*abci.TxResult{res}, nil
   169  		}
   170  	}
   171  
   172  	// conditions to skip because they're handled before "everything else"
   173  	skipIndexes := make([]int, 0)
   174  
   175  	// extract ranges
   176  	// if both upper and lower bounds exist, it's better to get them in order not
   177  	// no iterate over kvs that are not within range.
   178  	ranges, rangeIndexes := indexer.LookForRanges(conditions)
   179  	if len(ranges) > 0 {
   180  		skipIndexes = append(skipIndexes, rangeIndexes...)
   181  
   182  		for _, qr := range ranges {
   183  			if !hashesInitialized {
   184  				filteredHashes = txi.matchRange(ctx, qr, prefixFromCompositeKey(qr.Key), filteredHashes, true)
   185  				hashesInitialized = true
   186  
   187  				// Ignore any remaining conditions if the first condition resulted
   188  				// in no matches (assuming implicit AND operand).
   189  				if len(filteredHashes) == 0 {
   190  					break
   191  				}
   192  			} else {
   193  				filteredHashes = txi.matchRange(ctx, qr, prefixFromCompositeKey(qr.Key), filteredHashes, false)
   194  			}
   195  		}
   196  	}
   197  
   198  	// if there is a height condition ("tx.height=3"), extract it
   199  	height := lookForHeight(conditions)
   200  
   201  	// for all other conditions
   202  	for i, c := range conditions {
   203  		if intInSlice(i, skipIndexes) {
   204  			continue
   205  		}
   206  
   207  		if !hashesInitialized {
   208  			filteredHashes = txi.match(ctx, c, prefixForCondition(c, height), filteredHashes, true)
   209  			hashesInitialized = true
   210  
   211  			// Ignore any remaining conditions if the first condition resulted
   212  			// in no matches (assuming implicit AND operand).
   213  			if len(filteredHashes) == 0 {
   214  				break
   215  			}
   216  		} else {
   217  			filteredHashes = txi.match(ctx, c, prefixForCondition(c, height), filteredHashes, false)
   218  		}
   219  	}
   220  
   221  	results := make([]*abci.TxResult, 0, len(filteredHashes))
   222  	for _, h := range filteredHashes {
   223  		res, err := txi.Get(h)
   224  		if err != nil {
   225  			return nil, fmt.Errorf("failed to get Tx{%X}: %w", h, err)
   226  		}
   227  		results = append(results, res)
   228  
   229  		// Potentially exit early.
   230  		select {
   231  		case <-ctx.Done():
   232  			break
   233  		default:
   234  		}
   235  	}
   236  
   237  	return results, nil
   238  }
   239  
   240  func lookForHash(conditions []query.Condition) (hash []byte, ok bool, err error) {
   241  	for _, c := range conditions {
   242  		if c.CompositeKey == types.TxHashKey {
   243  			decoded, err := hex.DecodeString(c.Operand.(string))
   244  			return decoded, true, err
   245  		}
   246  	}
   247  	return
   248  }
   249  
   250  // lookForHeight returns a height if there is an "height=X" condition.
   251  func lookForHeight(conditions []query.Condition) (height int64) {
   252  	for _, c := range conditions {
   253  		if c.CompositeKey == types.TxHeightKey && c.Op == query.OpEqual {
   254  			return c.Operand.(int64)
   255  		}
   256  	}
   257  	return 0
   258  }
   259  
   260  // match returns all matching txs by hash that meet a given condition and start
   261  // key. An already filtered result (filteredHashes) is provided such that any
   262  // non-intersecting matches are removed.
   263  //
   264  // NOTE: filteredHashes may be empty if no previous condition has matched.
   265  func (txi *TxIndex) match(
   266  	ctx context.Context,
   267  	c query.Condition,
   268  	startKeyBz []byte,
   269  	filteredHashes map[string][]byte,
   270  	firstRun bool,
   271  ) map[string][]byte {
   272  	// A previous match was attempted but resulted in no matches, so we return
   273  	// no matches (assuming AND operand).
   274  	if !firstRun && len(filteredHashes) == 0 {
   275  		return filteredHashes
   276  	}
   277  
   278  	tmpHashes := make(map[string][]byte)
   279  
   280  	switch {
   281  	case c.Op == query.OpEqual:
   282  		it, err := dbm.IteratePrefix(txi.store, startKeyBz)
   283  		if err != nil {
   284  			panic(err)
   285  		}
   286  		defer it.Close()
   287  
   288  		for ; it.Valid(); it.Next() {
   289  			tmpHashes[string(it.Value())] = it.Value()
   290  
   291  			// Potentially exit early.
   292  			select {
   293  			case <-ctx.Done():
   294  				break
   295  			default:
   296  			}
   297  		}
   298  		if err := it.Error(); err != nil {
   299  			panic(err)
   300  		}
   301  
   302  	case c.Op == query.OpExists:
   303  		// XXX: can't use startKeyBz here because c.Operand is nil
   304  		// (e.g. "account.owner/<nil>/" won't match w/ a single row)
   305  		it, err := dbm.IteratePrefix(txi.store, prefixFromCompositeKey(c.CompositeKey))
   306  		if err != nil {
   307  			panic(err)
   308  		}
   309  		defer it.Close()
   310  
   311  		for ; it.Valid(); it.Next() {
   312  			tmpHashes[string(it.Value())] = it.Value()
   313  
   314  			// Potentially exit early.
   315  			select {
   316  			case <-ctx.Done():
   317  				break
   318  			default:
   319  			}
   320  		}
   321  		if err := it.Error(); err != nil {
   322  			panic(err)
   323  		}
   324  
   325  	case c.Op == query.OpContains:
   326  		// XXX: startKey does not apply here.
   327  		// For example, if startKey = "account.owner/an/" and search query = "account.owner CONTAINS an"
   328  		// we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/"
   329  		it, err := dbm.IteratePrefix(txi.store, prefixFromCompositeKey(c.CompositeKey))
   330  		if err != nil {
   331  			panic(err)
   332  		}
   333  		defer it.Close()
   334  
   335  		for ; it.Valid(); it.Next() {
   336  			value, err := parseValueFromKey(it.Key())
   337  			if err != nil {
   338  				continue
   339  			}
   340  			if strings.Contains(value, c.Operand.(string)) {
   341  				tmpHashes[string(it.Value())] = it.Value()
   342  			}
   343  
   344  			// Potentially exit early.
   345  			select {
   346  			case <-ctx.Done():
   347  				break
   348  			default:
   349  			}
   350  		}
   351  		if err := it.Error(); err != nil {
   352  			panic(err)
   353  		}
   354  	default:
   355  		panic("other operators should be handled already")
   356  	}
   357  
   358  	if len(tmpHashes) == 0 || firstRun {
   359  		// Either:
   360  		//
   361  		// 1. Regardless if a previous match was attempted, which may have had
   362  		// results, but no match was found for the current condition, then we
   363  		// return no matches (assuming AND operand).
   364  		//
   365  		// 2. A previous match was not attempted, so we return all results.
   366  		return tmpHashes
   367  	}
   368  
   369  	// Remove/reduce matches in filteredHashes that were not found in this
   370  	// match (tmpHashes).
   371  	for k := range filteredHashes {
   372  		if tmpHashes[k] == nil {
   373  			delete(filteredHashes, k)
   374  
   375  			// Potentially exit early.
   376  			select {
   377  			case <-ctx.Done():
   378  				break
   379  			default:
   380  			}
   381  		}
   382  	}
   383  
   384  	return filteredHashes
   385  }
   386  
   387  // matchRange returns all matching txs by hash that meet a given queryRange and
   388  // start key. An already filtered result (filteredHashes) is provided such that
   389  // any non-intersecting matches are removed.
   390  //
   391  // NOTE: filteredHashes may be empty if no previous condition has matched.
   392  func (txi *TxIndex) matchRange(
   393  	ctx context.Context,
   394  	qr indexer.QueryRange,
   395  	startKey []byte,
   396  	filteredHashes map[string][]byte,
   397  	firstRun bool,
   398  ) map[string][]byte {
   399  	// A previous match was attempted but resulted in no matches, so we return
   400  	// no matches (assuming AND operand).
   401  	if !firstRun && len(filteredHashes) == 0 {
   402  		return filteredHashes
   403  	}
   404  
   405  	tmpHashes := make(map[string][]byte)
   406  	lowerBound := qr.LowerBoundValue()
   407  	upperBound := qr.UpperBoundValue()
   408  
   409  	it, err := dbm.IteratePrefix(txi.store, startKey)
   410  	if err != nil {
   411  		panic(err)
   412  	}
   413  	defer it.Close()
   414  
   415  LOOP:
   416  	for ; it.Valid(); it.Next() {
   417  		value, err := parseValueFromKey(it.Key())
   418  		if err != nil {
   419  			continue
   420  		}
   421  		if _, ok := qr.AnyBound().(int64); ok {
   422  			v, err := strconv.ParseInt(value, 10, 64)
   423  			if err != nil {
   424  				continue LOOP
   425  			}
   426  
   427  			include := true
   428  			if lowerBound != nil && v < lowerBound.(int64) {
   429  				include = false
   430  			}
   431  
   432  			if upperBound != nil && v > upperBound.(int64) {
   433  				include = false
   434  			}
   435  
   436  			if include {
   437  				tmpHashes[string(it.Value())] = it.Value()
   438  			}
   439  
   440  			// XXX: passing time in a ABCI Events is not yet implemented
   441  			// case time.Time:
   442  			// 	v := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64)
   443  			// 	if v == r.upperBound {
   444  			// 		break
   445  			// 	}
   446  		}
   447  
   448  		// Potentially exit early.
   449  		select {
   450  		case <-ctx.Done():
   451  			break
   452  		default:
   453  		}
   454  	}
   455  	if err := it.Error(); err != nil {
   456  		panic(err)
   457  	}
   458  
   459  	if len(tmpHashes) == 0 || firstRun {
   460  		// Either:
   461  		//
   462  		// 1. Regardless if a previous match was attempted, which may have had
   463  		// results, but no match was found for the current condition, then we
   464  		// return no matches (assuming AND operand).
   465  		//
   466  		// 2. A previous match was not attempted, so we return all results.
   467  		return tmpHashes
   468  	}
   469  
   470  	// Remove/reduce matches in filteredHashes that were not found in this
   471  	// match (tmpHashes).
   472  	for k := range filteredHashes {
   473  		if tmpHashes[k] == nil {
   474  			delete(filteredHashes, k)
   475  
   476  			// Potentially exit early.
   477  			select {
   478  			case <-ctx.Done():
   479  				break
   480  			default:
   481  			}
   482  		}
   483  	}
   484  
   485  	return filteredHashes
   486  }
   487  
   488  // ##########################  Keys  #############################
   489  //
   490  // The indexer has two types of kv stores:
   491  // 1. txhash - result  (primary key)
   492  // 2. event - txhash   (secondary key)
   493  //
   494  // The event key can be decomposed into 4 parts.
   495  // 1. A composite key which can be any string.
   496  // Usually something like "tx.height" or "account.owner"
   497  // 2. A value. That corresponds to the key. In the above
   498  // example the value could be "5" or "Ivan"
   499  // 3. The height of the Tx that aligns with the key and value.
   500  // 4. The index of the Tx that aligns with the key and value
   501  
   502  // the hash/primary key
   503  func primaryKey(hash []byte) []byte {
   504  	key, err := orderedcode.Append(
   505  		nil,
   506  		types.TxHashKey,
   507  		string(hash),
   508  	)
   509  	if err != nil {
   510  		panic(err)
   511  	}
   512  	return key
   513  }
   514  
   515  // The event/secondary key
   516  func secondaryKey(compositeKey, value string, height int64, index uint32) []byte {
   517  	key, err := orderedcode.Append(
   518  		nil,
   519  		compositeKey,
   520  		value,
   521  		height,
   522  		int64(index),
   523  	)
   524  	if err != nil {
   525  		panic(err)
   526  	}
   527  	return key
   528  }
   529  
   530  // parseValueFromKey parses an event key and extracts out the value, returning an error if one arises.
   531  // This will also involve ensuring that the key has the correct format.
   532  // CONTRACT: function doesn't check that the prefix is correct. This should have already been done by the iterator
   533  func parseValueFromKey(key []byte) (string, error) {
   534  	var (
   535  		compositeKey, value string
   536  		height, index       int64
   537  	)
   538  	remaining, err := orderedcode.Parse(string(key), &compositeKey, &value, &height, &index)
   539  	if err != nil {
   540  		return "", err
   541  	}
   542  	if len(remaining) != 0 {
   543  		return "", fmt.Errorf("unexpected remainder in key: %s", remaining)
   544  	}
   545  	return value, nil
   546  }
   547  
   548  func keyFromEvent(compositeKey string, value string, result *abci.TxResult) []byte {
   549  	return secondaryKey(compositeKey, value, result.Height, result.Index)
   550  }
   551  
   552  func KeyFromHeight(result *abci.TxResult) []byte {
   553  	return secondaryKey(types.TxHeightKey, fmt.Sprintf("%d", result.Height), result.Height, result.Index)
   554  }
   555  
   556  // Prefixes: these represent an initial part of the key and are used by iterators to iterate over a small
   557  // section of the kv store during searches.
   558  
   559  func prefixFromCompositeKey(compositeKey string) []byte {
   560  	key, err := orderedcode.Append(nil, compositeKey)
   561  	if err != nil {
   562  		panic(err)
   563  	}
   564  	return key
   565  }
   566  
   567  func prefixFromCompositeKeyAndValue(compositeKey, value string) []byte {
   568  	key, err := orderedcode.Append(nil, compositeKey, value)
   569  	if err != nil {
   570  		panic(err)
   571  	}
   572  	return key
   573  }
   574  
   575  // a small utility function for getting a keys prefix based on a condition and a height
   576  func prefixForCondition(c query.Condition, height int64) []byte {
   577  	key := prefixFromCompositeKeyAndValue(c.CompositeKey, fmt.Sprintf("%v", c.Operand))
   578  	if height > 0 {
   579  		var err error
   580  		key, err = orderedcode.Append(key, height)
   581  		if err != nil {
   582  			panic(err)
   583  		}
   584  	}
   585  	return key
   586  }