github.com/status-im/status-go@v1.1.0/services/wallet/balance/nonce_range.go (about) 1 package balance 2 3 import ( 4 "math/big" 5 "reflect" 6 "sort" 7 "sync" 8 9 "github.com/ethereum/go-ethereum/common" 10 ) 11 12 type nonceRange struct { 13 nonce int64 14 max *big.Int 15 min *big.Int 16 } 17 18 type sortedNonceRangesCacheType addressChainMap[[]nonceRange] // address->chainID->[]nonceRange 19 20 type nonceRangeCache[T cacheIface[int64, nonceRange]] struct { 21 nonceRanges addressChainMap[T] 22 sortedRanges sortedNonceRangesCacheType 23 rw sync.RWMutex 24 } 25 26 func newNonceRangeCache[T cacheIface[int64, nonceRange]]() *nonceRangeCache[T] { 27 return &nonceRangeCache[T]{ 28 nonceRanges: make(addressChainMap[T]), 29 sortedRanges: make(sortedNonceRangesCacheType), 30 } 31 } 32 33 func (b *nonceRangeCache[T]) updateNonceRange(account common.Address, chainID uint64, blockNumber *big.Int, nonce *int64) { 34 b.rw.Lock() 35 defer b.rw.Unlock() 36 37 _, exists := b.nonceRanges[account] 38 if !exists { 39 b.nonceRanges[account] = make(map[uint64]T) 40 } 41 _, exists = b.nonceRanges[account][chainID] 42 if !exists { 43 b.nonceRanges[account][chainID] = reflect.New(reflect.TypeOf(b.nonceRanges[account][chainID]).Elem()).Interface().(T) 44 b.nonceRanges[account][chainID].init() 45 } 46 47 nr := b.nonceRanges[account][chainID].get(*nonce) 48 if nr == reflect.Zero(reflect.TypeOf(nr)).Interface() { 49 nr = nonceRange{ 50 max: big.NewInt(0).Set(blockNumber), 51 min: big.NewInt(0).Set(blockNumber), 52 nonce: *nonce, 53 } 54 } else { 55 if nr.max.Cmp(blockNumber) == -1 { 56 nr.max.Set(blockNumber) 57 } 58 59 if nr.min.Cmp(blockNumber) == 1 { 60 nr.min.Set(blockNumber) 61 } 62 } 63 64 b.nonceRanges[account][chainID].set(*nonce, nr) 65 b.sortRanges(account, chainID) 66 } 67 68 func (b *nonceRangeCache[_]) findNonceInRange(account common.Address, chainID uint64, block *big.Int) *int64 { 69 b.rw.RLock() 70 defer b.rw.RUnlock() 71 72 for k := range b.sortedRanges[account][chainID] { 73 nr := b.sortedRanges[account][chainID][k] 74 cmpMin := nr.min.Cmp(block) 75 if cmpMin == 1 { 76 return nil 77 } else if cmpMin == 0 { 78 return &nr.nonce 79 } else { 80 cmpMax := nr.max.Cmp(block) 81 if cmpMax >= 0 { 82 return &nr.nonce 83 } 84 } 85 } 86 87 return nil 88 } 89 90 func (b *nonceRangeCache[T]) sortRanges(account common.Address, chainID uint64) { 91 // DO NOT LOCK HERE - this function is called from a locked function 92 93 keys := b.nonceRanges[account][chainID].keys() 94 95 sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) 96 97 ranges := []nonceRange{} 98 for _, k := range keys { 99 r := b.nonceRanges[account][chainID].get(k) 100 ranges = append(ranges, r) 101 } 102 103 _, exists := b.sortedRanges[account] 104 if !exists { 105 b.sortedRanges[account] = make(map[uint64][]nonceRange) 106 } 107 108 b.sortedRanges[account][chainID] = ranges 109 } 110 111 func (b *nonceRangeCache[T]) clear() { 112 b.rw.Lock() 113 defer b.rw.Unlock() 114 115 b.nonceRanges = make(addressChainMap[T]) 116 b.sortedRanges = make(sortedNonceRangesCacheType) 117 } 118 119 func (b *nonceRangeCache[T]) size(account common.Address, chainID uint64) int { 120 b.rw.RLock() 121 defer b.rw.RUnlock() 122 123 _, exists := b.nonceRanges[account] 124 if !exists { 125 return 0 126 } 127 128 _, exists = b.nonceRanges[account][chainID] 129 if !exists { 130 return 0 131 } 132 133 return b.nonceRanges[account][chainID].len() 134 }