github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/topicsdb/index.go (about)

     1  package topicsdb
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/unicornultrafoundation/go-helios/native/idx"
     7  	"github.com/unicornultrafoundation/go-helios/u2udb"
     8  	"github.com/unicornultrafoundation/go-helios/u2udb/batched"
     9  	"github.com/unicornultrafoundation/go-helios/u2udb/table"
    10  	"github.com/unicornultrafoundation/go-u2u/common"
    11  	"github.com/unicornultrafoundation/go-u2u/core/types"
    12  )
    13  
    14  // index is a specialized indexes for log records storing and fetching.
    15  type index struct {
    16  	table struct {
    17  		// topic+topicN+(blockN+TxHash+logIndex) -> topic_count (where topicN=0 is for address)
    18  		Topic u2udb.Store `table:"t"`
    19  		// (blockN+TxHash+logIndex) -> ordered topic_count topics, blockHash, address, data
    20  		Logrec u2udb.Store `table:"r"`
    21  	}
    22  }
    23  
    24  func newIndex(dbs u2udb.DBProducer) *index {
    25  	tt := &index{}
    26  
    27  	err := table.OpenTables(&tt.table, dbs, "evm-logs")
    28  	if err != nil {
    29  		panic(err)
    30  	}
    31  
    32  	return tt
    33  }
    34  
    35  func (tt *index) WrapTablesAsBatched() (unwrap func()) {
    36  	origTables := tt.table
    37  	batchedTopic := batched.Wrap(tt.table.Topic)
    38  	tt.table.Topic = batchedTopic
    39  	batchedLogrec := batched.Wrap(tt.table.Logrec)
    40  	tt.table.Logrec = batchedLogrec
    41  	return func() {
    42  		_ = batchedTopic.Flush()
    43  		_ = batchedLogrec.Flush()
    44  		tt.table = origTables
    45  	}
    46  }
    47  
    48  // FindInBlocks returns all log records of block range by pattern. 1st pattern element is an address.
    49  func (tt *index) FindInBlocks(ctx context.Context, from, to idx.Block, pattern [][]common.Hash) (logs []*types.Log, err error) {
    50  	err = tt.ForEachInBlocks(
    51  		ctx,
    52  		from, to,
    53  		pattern,
    54  		func(l *types.Log) bool {
    55  			logs = append(logs, l)
    56  			return true
    57  		})
    58  
    59  	return
    60  }
    61  
    62  // ForEachInBlocks matches log records of block range by pattern. 1st pattern element is an address.
    63  func (tt *index) ForEachInBlocks(ctx context.Context, from, to idx.Block, pattern [][]common.Hash, onLog func(*types.Log) (gonext bool)) error {
    64  	if 0 < to && to < from {
    65  		return nil
    66  	}
    67  
    68  	pattern, err := limitPattern(pattern)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	onMatched := func(rec *logrec) (gonext bool, err error) {
    74  		rec.fetch(tt.table.Logrec)
    75  		if rec.err != nil {
    76  			err = rec.err
    77  			return
    78  		}
    79  		gonext = onLog(rec.result)
    80  		return
    81  	}
    82  
    83  	return tt.searchParallel(ctx, pattern, uint64(from), uint64(to), onMatched, doNothing)
    84  }
    85  
    86  func doNothing() {}
    87  
    88  // Push log record to database batch
    89  func (tt *index) Push(recs ...*types.Log) error {
    90  	for _, rec := range recs {
    91  		if len(rec.Topics) > maxTopicsCount {
    92  			return ErrTooBigTopics
    93  		}
    94  
    95  		id := NewID(rec.BlockNumber, rec.TxHash, rec.Index)
    96  
    97  		// write data
    98  		buf := make([]byte, 0, common.HashLength*len(rec.Topics)+common.HashLength+common.AddressLength+len(rec.Data))
    99  		for _, topic := range rec.Topics {
   100  			buf = append(buf, topic.Bytes()...)
   101  		}
   102  		buf = append(buf, rec.BlockHash.Bytes()...)
   103  		buf = append(buf, rec.Address.Bytes()...)
   104  		buf = append(buf, rec.Data...)
   105  		if err := tt.table.Logrec.Put(id.Bytes(), buf); err != nil {
   106  			return err
   107  		}
   108  
   109  		// write index
   110  		var (
   111  			count = posToBytes(uint8(len(rec.Topics)))
   112  			pos   uint8
   113  		)
   114  		pushIndex := func(topic common.Hash) error {
   115  			key := topicKey(topic, pos, id)
   116  			if err := tt.table.Topic.Put(key, count); err != nil {
   117  				return err
   118  			}
   119  			pos++
   120  			return nil
   121  		}
   122  
   123  		if err := pushIndex(rec.Address.Hash()); err != nil {
   124  			return err
   125  		}
   126  
   127  		for _, topic := range rec.Topics {
   128  			if err := pushIndex(topic); err != nil {
   129  				return err
   130  			}
   131  		}
   132  
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func (tt *index) Close() {
   139  	_ = tt.table.Topic.Close()
   140  	_ = tt.table.Logrec.Close()
   141  }