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 }