github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/meter/interaction_meter.go (about) 1 package meter 2 3 import ( 4 "math" 5 6 "github.com/rs/zerolog/log" 7 8 "github.com/onflow/flow-go/fvm/errors" 9 "github.com/onflow/flow-go/model/flow" 10 ) 11 12 type MeteredStorageInteractionMap map[flow.RegisterID]uint64 13 14 type InteractionMeterParameters struct { 15 storageInteractionLimit uint64 16 } 17 18 func DefaultInteractionMeterParameters() InteractionMeterParameters { 19 return InteractionMeterParameters{ 20 storageInteractionLimit: math.MaxUint64, 21 } 22 } 23 24 func (params MeterParameters) WithStorageInteractionLimit( 25 maxStorageInteractionLimit uint64, 26 ) MeterParameters { 27 newParams := params 28 newParams.storageInteractionLimit = maxStorageInteractionLimit 29 return newParams 30 } 31 32 // InteractionMeter is a meter that tracks storage interaction 33 // Only the first read of a given key is counted 34 // Only the last write of a given key is counted 35 type InteractionMeter struct { 36 params InteractionMeterParameters 37 38 reads map[flow.RegisterID]uint64 39 writes map[flow.RegisterID]uint64 40 41 totalStorageBytesRead uint64 42 totalStorageBytesWritten uint64 43 } 44 45 func NewInteractionMeter(params InteractionMeterParameters) InteractionMeter { 46 return InteractionMeter{ 47 params: params, 48 reads: make(map[flow.RegisterID]uint64), 49 writes: make(map[flow.RegisterID]uint64), 50 } 51 } 52 53 // MeterStorageRead captures storage read bytes count and returns an error 54 // if it goes beyond the total interaction limit and limit is enforced 55 func (m *InteractionMeter) MeterStorageRead( 56 storageKey flow.RegisterID, 57 value flow.RegisterValue, 58 enforceLimit bool, 59 ) error { 60 61 // all reads are on a View which only read from storage at the first read of a given key 62 if _, ok := m.reads[storageKey]; !ok { 63 readByteSize := getStorageKeyValueSize(storageKey, value) 64 m.totalStorageBytesRead += readByteSize 65 m.reads[storageKey] = readByteSize 66 } 67 68 return m.checkStorageInteractionLimit(enforceLimit) 69 } 70 71 // MeterStorageWrite captures storage written bytes count and returns an error 72 // if it goes beyond the total interaction limit and limit is enforced. 73 // If a key is written multiple times, only the last write is counted. 74 // If a key is written before it has been read, next time it will be read it will be from the view, 75 // not from storage, so count it as read 0. 76 func (m *InteractionMeter) MeterStorageWrite( 77 storageKey flow.RegisterID, 78 value flow.RegisterValue, 79 enforceLimit bool, 80 ) error { 81 updateSize := getStorageKeyValueSize(storageKey, value) 82 m.replaceWrite(storageKey, updateSize) 83 84 if _, ok := m.reads[storageKey]; !ok { 85 // write without read, count as read 0 because next time you read it the written value 86 // will be returned from cache, so no interaction with storage will happen. 87 m.reads[storageKey] = 0 88 } 89 90 return m.checkStorageInteractionLimit(enforceLimit) 91 } 92 93 // replaceWrite replaces the write size of a given key with the new size, because 94 // only the last write of a given key is counted towards the total interaction limit. 95 // These are the only write usages of `m.totalStorageBytesWritten` and `m.writes`, 96 // which means that `m.totalStorageBytesWritten` can never become negative. 97 // oldSize is always <= m.totalStorageBytesWritten. 98 func (m *InteractionMeter) replaceWrite( 99 k flow.RegisterID, 100 newSize uint64, 101 ) { 102 totalBefore := m.totalStorageBytesWritten 103 104 // remove old write 105 oldSize := m.writes[k] 106 m.totalStorageBytesWritten -= oldSize 107 108 // sanity check 109 // this should never happen, but if it does, it should be fatal 110 if m.totalStorageBytesWritten > totalBefore { 111 log.Fatal(). 112 Str("component", "interaction_meter"). 113 Uint64("total", totalBefore). 114 Uint64("subtract", oldSize). 115 Msg("totalStorageBytesWritten would have become negative") 116 } 117 118 // add new write 119 m.writes[k] = newSize 120 m.totalStorageBytesWritten += newSize 121 } 122 123 func (m *InteractionMeter) checkStorageInteractionLimit(enforceLimit bool) error { 124 if enforceLimit && 125 m.TotalBytesOfStorageInteractions() > m.params.storageInteractionLimit { 126 return errors.NewLedgerInteractionLimitExceededError( 127 m.TotalBytesOfStorageInteractions(), m.params.storageInteractionLimit) 128 } 129 return nil 130 } 131 132 // TotalBytesReadFromStorage returns total number of byte read from storage 133 func (m *InteractionMeter) TotalBytesReadFromStorage() uint64 { 134 return m.totalStorageBytesRead 135 } 136 137 // TotalBytesWrittenToStorage returns total number of byte written to storage 138 func (m *InteractionMeter) TotalBytesWrittenToStorage() uint64 { 139 return m.totalStorageBytesWritten 140 } 141 142 // TotalBytesOfStorageInteractions returns total number of byte read and written from/to storage 143 func (m *InteractionMeter) TotalBytesOfStorageInteractions() uint64 { 144 return m.TotalBytesReadFromStorage() + m.TotalBytesWrittenToStorage() 145 } 146 147 func getStorageKeyValueSize( 148 storageKey flow.RegisterID, 149 value flow.RegisterValue, 150 ) uint64 { 151 return uint64(len(storageKey.Owner) + len(storageKey.Key) + len(value)) 152 } 153 154 func GetStorageKeyValueSizeForTesting( 155 storageKey flow.RegisterID, 156 value flow.RegisterValue, 157 ) uint64 { 158 return getStorageKeyValueSize(storageKey, value) 159 } 160 161 func (m *InteractionMeter) GetStorageRWSizeMapForTesting() ( 162 reads MeteredStorageInteractionMap, 163 writes MeteredStorageInteractionMap, 164 ) { 165 return m.reads, m.writes 166 } 167 168 // Merge merges the child interaction meter into the parent interaction meter 169 // Prioritise parent reads because they happened first 170 // Prioritise child writes because they happened last 171 func (m *InteractionMeter) Merge(child InteractionMeter) { 172 for key, value := range child.reads { 173 _, parentRead := m.reads[key] 174 if parentRead { 175 // avoid metering the same read more than once, because a second read 176 // is from the cache 177 continue 178 } 179 180 m.reads[key] = value 181 m.totalStorageBytesRead += value 182 } 183 184 for key, value := range child.writes { 185 m.replaceWrite(key, value) 186 } 187 }