github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/state/stategen/epoch_boundary_state_cache.go (about) 1 package stategen 2 3 import ( 4 "errors" 5 "strconv" 6 "sync" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 10 "k8s.io/client-go/tools/cache" 11 ) 12 13 var ( 14 // maxCacheSize is 8. That means 8 epochs and roughly an hour 15 // of no finality can be endured. 16 maxCacheSize = uint64(8) 17 errNotSlotRootInfo = errors.New("not slot root info type") 18 errNotRootStateInfo = errors.New("not root state info type") 19 ) 20 21 // slotRootInfo specifies the slot root info in the epoch boundary state cache. 22 type slotRootInfo struct { 23 slot types.Slot 24 root [32]byte 25 } 26 27 // slotKeyFn takes the string representation of the slot to be used as key 28 // to retrieve root. 29 func slotKeyFn(obj interface{}) (string, error) { 30 s, ok := obj.(*slotRootInfo) 31 if !ok { 32 return "", errNotSlotRootInfo 33 } 34 return slotToString(s.slot), nil 35 } 36 37 // rootStateInfo specifies the root state info in the epoch boundary state cache. 38 type rootStateInfo struct { 39 root [32]byte 40 state iface.BeaconState 41 } 42 43 // rootKeyFn takes the string representation of the block root to be used as key 44 // to retrieve epoch boundary state. 45 func rootKeyFn(obj interface{}) (string, error) { 46 s, ok := obj.(*rootStateInfo) 47 if !ok { 48 return "", errNotRootStateInfo 49 } 50 return string(s.root[:]), nil 51 } 52 53 // epochBoundaryState struct with two queues by looking up beacon state by slot or root. 54 type epochBoundaryState struct { 55 rootStateCache *cache.FIFO 56 slotRootCache *cache.FIFO 57 lock sync.RWMutex 58 } 59 60 // newBoundaryStateCache creates a new block newBoundaryStateCache for storing and accessing epoch boundary states from 61 // memory. 62 func newBoundaryStateCache() *epochBoundaryState { 63 return &epochBoundaryState{ 64 rootStateCache: cache.NewFIFO(rootKeyFn), 65 slotRootCache: cache.NewFIFO(slotKeyFn), 66 } 67 } 68 69 // get epoch boundary state by its block root. Returns copied state in state info object if exists. Otherwise returns nil. 70 func (e *epochBoundaryState) getByRoot(r [32]byte) (*rootStateInfo, bool, error) { 71 e.lock.RLock() 72 defer e.lock.RUnlock() 73 74 obj, exists, err := e.rootStateCache.GetByKey(string(r[:])) 75 if err != nil { 76 return nil, false, err 77 } 78 if !exists { 79 return nil, false, nil 80 } 81 s, ok := obj.(*rootStateInfo) 82 if !ok { 83 return nil, false, errNotRootStateInfo 84 } 85 86 return &rootStateInfo{ 87 root: r, 88 state: s.state.Copy(), 89 }, true, nil 90 } 91 92 // get epoch boundary state by its slot. Returns copied state in state info object if exists. Otherwise returns nil. 93 func (e *epochBoundaryState) getBySlot(s types.Slot) (*rootStateInfo, bool, error) { 94 e.lock.RLock() 95 defer e.lock.RUnlock() 96 97 obj, exists, err := e.slotRootCache.GetByKey(slotToString(s)) 98 if err != nil { 99 return nil, false, err 100 } 101 if !exists { 102 return nil, false, nil 103 } 104 info, ok := obj.(*slotRootInfo) 105 if !ok { 106 return nil, false, errNotSlotRootInfo 107 } 108 109 return e.getByRoot(info.root) 110 } 111 112 // put adds a state to the epoch boundary state cache. This method also trims the 113 // least recently added state info if the cache size has reached the max cache 114 // size limit. 115 func (e *epochBoundaryState) put(r [32]byte, s iface.BeaconState) error { 116 e.lock.Lock() 117 defer e.lock.Unlock() 118 119 if err := e.slotRootCache.AddIfNotPresent(&slotRootInfo{ 120 slot: s.Slot(), 121 root: r, 122 }); err != nil { 123 return err 124 } 125 if err := e.rootStateCache.AddIfNotPresent(&rootStateInfo{ 126 root: r, 127 state: s.Copy(), 128 }); err != nil { 129 return err 130 } 131 132 trim(e.rootStateCache, maxCacheSize) 133 trim(e.slotRootCache, maxCacheSize) 134 135 return nil 136 } 137 138 // trim the FIFO queue to the maxSize. 139 func trim(queue *cache.FIFO, maxSize uint64) { 140 for s := uint64(len(queue.ListKeys())); s > maxSize; s-- { 141 if _, err := queue.Pop(popProcessNoopFunc); err != nil { // This never returns an error, but we'll handle anyway for sanity. 142 panic(err) 143 } 144 } 145 } 146 147 // popProcessNoopFunc is a no-op function that never returns an error. 148 func popProcessNoopFunc(_ interface{}) error { 149 return nil 150 } 151 152 // Converts input uint64 to string. To be used as key for slot to get root. 153 func slotToString(s types.Slot) string { 154 return strconv.FormatUint(uint64(s), 10) 155 }