github.com/onflow/flow-go@v0.33.17/fvm/environment/event_emitter.go (about) 1 package environment 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/cadence" 7 8 "github.com/onflow/flow-go/fvm/errors" 9 "github.com/onflow/flow-go/fvm/storage/state" 10 "github.com/onflow/flow-go/fvm/systemcontracts" 11 "github.com/onflow/flow-go/fvm/tracing" 12 "github.com/onflow/flow-go/model/convert" 13 "github.com/onflow/flow-go/model/flow" 14 "github.com/onflow/flow-go/module/trace" 15 ) 16 17 const ( 18 DefaultEventCollectionByteSizeLimit = 256_000 // 256KB 19 ) 20 21 type EventEmitterParams struct { 22 ServiceEventCollectionEnabled bool 23 EventCollectionByteSizeLimit uint64 24 EventEncoder EventEncoder 25 } 26 27 func DefaultEventEmitterParams() EventEmitterParams { 28 return EventEmitterParams{ 29 ServiceEventCollectionEnabled: false, 30 EventCollectionByteSizeLimit: DefaultEventCollectionByteSizeLimit, 31 EventEncoder: NewCadenceEventEncoder(), 32 } 33 } 34 35 // EventEmitter collect events, separates out service events, and enforces 36 // event size limits. 37 // 38 // Note that scripts do not emit events, but must expose the API in compliance 39 // with the runtime environment interface. 40 type EventEmitter interface { 41 // EmitEvent satisfies Cadence's runtime API. 42 // This will encode the cadence event 43 // 44 // Note that the script variant will return OperationNotSupportedError. 45 EmitEvent(event cadence.Event) error 46 47 Events() flow.EventsList 48 ServiceEvents() flow.EventsList 49 ConvertedServiceEvents() flow.ServiceEventList 50 51 Reset() 52 } 53 54 type ParseRestrictedEventEmitter struct { 55 txnState state.NestedTransactionPreparer 56 impl EventEmitter 57 } 58 59 func NewParseRestrictedEventEmitter( 60 txnState state.NestedTransactionPreparer, 61 impl EventEmitter, 62 ) EventEmitter { 63 return ParseRestrictedEventEmitter{ 64 txnState: txnState, 65 impl: impl, 66 } 67 } 68 69 func (emitter ParseRestrictedEventEmitter) EmitEvent(event cadence.Event) error { 70 return parseRestrict1Arg( 71 emitter.txnState, 72 trace.FVMEnvEmitEvent, 73 emitter.impl.EmitEvent, 74 event) 75 } 76 77 func (emitter ParseRestrictedEventEmitter) Events() flow.EventsList { 78 return emitter.impl.Events() 79 } 80 81 func (emitter ParseRestrictedEventEmitter) ServiceEvents() flow.EventsList { 82 return emitter.impl.ServiceEvents() 83 } 84 85 func (emitter ParseRestrictedEventEmitter) ConvertedServiceEvents() flow.ServiceEventList { 86 return emitter.impl.ConvertedServiceEvents() 87 } 88 89 func (emitter ParseRestrictedEventEmitter) Reset() { 90 emitter.impl.Reset() 91 } 92 93 var _ EventEmitter = NoEventEmitter{} 94 95 // NoEventEmitter is usually used in the environment for script execution, 96 // where emitting an event does nothing. 97 type NoEventEmitter struct{} 98 99 func (NoEventEmitter) EmitEvent(cadence.Event) error { 100 return nil 101 } 102 103 func (NoEventEmitter) Events() flow.EventsList { 104 return flow.EventsList{} 105 } 106 107 func (NoEventEmitter) ServiceEvents() flow.EventsList { 108 return flow.EventsList{} 109 } 110 111 func (NoEventEmitter) ConvertedServiceEvents() flow.ServiceEventList { 112 return flow.ServiceEventList{} 113 } 114 115 func (NoEventEmitter) Reset() { 116 } 117 118 type eventEmitter struct { 119 tracer tracing.TracerSpan 120 meter Meter 121 122 chain flow.Chain 123 txID flow.Identifier 124 txIndex uint32 125 payer flow.Address 126 127 EventEmitterParams 128 eventCollection *EventCollection 129 } 130 131 // NewEventEmitter constructs a new eventEmitter 132 func NewEventEmitter( 133 tracer tracing.TracerSpan, 134 meter Meter, 135 chain flow.Chain, 136 txInfo TransactionInfoParams, 137 params EventEmitterParams, 138 ) EventEmitter { 139 emitter := &eventEmitter{ 140 tracer: tracer, 141 meter: meter, 142 chain: chain, 143 txID: txInfo.TxId, 144 txIndex: txInfo.TxIndex, 145 payer: txInfo.TxBody.Payer, 146 EventEmitterParams: params, 147 } 148 149 emitter.Reset() 150 return emitter 151 } 152 153 func (emitter *eventEmitter) Reset() { 154 // TODO: for now we are not resetting meter here because we don't check meter 155 // metrics after the first metering failure and when limit is disabled. 156 emitter.eventCollection = NewEventCollection(emitter.meter) 157 } 158 159 func (emitter *eventEmitter) EventCollection() *EventCollection { 160 return emitter.eventCollection 161 } 162 163 func (emitter *eventEmitter) EmitEvent(event cadence.Event) error { 164 err := emitter.meter.MeterComputation(ComputationKindEncodeEvent, 1) 165 if err != nil { 166 return fmt.Errorf("emit event, event encoding failed: %w", err) 167 } 168 169 payload, err := emitter.EventEncoder.Encode(event) 170 if err != nil { 171 return errors.NewEventEncodingError(err) 172 } 173 emitter.tracer.StartExtensiveTracingChildSpan(trace.FVMEnvEncodeEvent).End() 174 defer emitter.tracer.StartExtensiveTracingChildSpan(trace.FVMEnvEmitEvent).End() 175 176 payloadSize := len(payload) 177 err = emitter.meter.MeterComputation(ComputationKindEmitEvent, uint(payloadSize)) 178 if err != nil { 179 return fmt.Errorf("emit event failed: %w", err) 180 } 181 182 eventType := flow.EventType(event.EventType.ID()) 183 flowEvent := flow.Event{ 184 Type: eventType, 185 TransactionID: emitter.txID, 186 TransactionIndex: emitter.txIndex, 187 EventIndex: emitter.eventCollection.TotalEventCounter(), 188 Payload: payload, 189 } 190 191 // TODO: to set limit to maximum when it is service account and get rid of this flag 192 isServiceAccount := emitter.payer == emitter.chain.ServiceAddress() 193 194 if emitter.ServiceEventCollectionEnabled { 195 ok, err := IsServiceEvent(eventType, emitter.chain.ChainID()) 196 if err != nil { 197 return fmt.Errorf("unable to check service event: %w", err) 198 } 199 if ok { 200 eventEmitError := emitter.eventCollection.AppendServiceEvent( 201 emitter.chain, 202 flowEvent, 203 uint64(payloadSize)) 204 205 // skip limit if payer is service account 206 // TODO skip only limit-related errors 207 if !isServiceAccount && eventEmitError != nil { 208 return eventEmitError 209 } 210 } 211 // We don't return and append the service event into event collection 212 // as well. 213 } 214 215 eventEmitError := emitter.eventCollection.AppendEvent(flowEvent, uint64(payloadSize)) 216 // skip limit if payer is service account 217 if !isServiceAccount { 218 return eventEmitError 219 } 220 221 return nil 222 } 223 224 func (emitter *eventEmitter) Events() flow.EventsList { 225 return emitter.eventCollection.events 226 } 227 228 func (emitter *eventEmitter) ServiceEvents() flow.EventsList { 229 return emitter.eventCollection.serviceEvents 230 } 231 232 func (emitter *eventEmitter) ConvertedServiceEvents() flow.ServiceEventList { 233 return emitter.eventCollection.convertedServiceEvents 234 } 235 236 type EventCollection struct { 237 events flow.EventsList 238 serviceEvents flow.EventsList 239 convertedServiceEvents flow.ServiceEventList 240 eventCounter uint32 241 meter Meter 242 } 243 244 func NewEventCollection(meter Meter) *EventCollection { 245 return &EventCollection{ 246 events: make(flow.EventsList, 0, 10), 247 serviceEvents: make(flow.EventsList, 0, 10), 248 convertedServiceEvents: make(flow.ServiceEventList, 0, 10), 249 eventCounter: uint32(0), 250 meter: meter, 251 } 252 } 253 254 func (collection *EventCollection) Events() flow.EventsList { 255 return collection.events 256 } 257 258 func (collection *EventCollection) AppendEvent(event flow.Event, size uint64) error { 259 collection.events = append(collection.events, event) 260 collection.eventCounter++ 261 return collection.meter.MeterEmittedEvent(size) 262 } 263 264 func (collection *EventCollection) ServiceEvents() flow.EventsList { 265 return collection.serviceEvents 266 } 267 268 func (collection *EventCollection) ConvertedServiceEvents() flow.ServiceEventList { 269 return collection.convertedServiceEvents 270 } 271 272 func (collection *EventCollection) AppendServiceEvent( 273 chain flow.Chain, 274 event flow.Event, 275 size uint64, 276 ) error { 277 convertedEvent, err := convert.ServiceEvent(chain.ChainID(), event) 278 if err != nil { 279 return fmt.Errorf("could not convert service event: %w", err) 280 } 281 282 collection.serviceEvents = append(collection.serviceEvents, event) 283 collection.convertedServiceEvents = append( 284 collection.convertedServiceEvents, 285 *convertedEvent) 286 collection.eventCounter++ 287 return collection.meter.MeterEmittedEvent(size) 288 } 289 290 func (collection *EventCollection) TotalByteSize() uint64 { 291 return collection.meter.TotalEmittedEventBytes() 292 } 293 294 func (collection *EventCollection) TotalEventCounter() uint32 { 295 return collection.eventCounter 296 } 297 298 // IsServiceEvent determines whether or not an emitted Cadence event is 299 // considered a service event for the given chain. 300 func IsServiceEvent(eventType flow.EventType, chain flow.ChainID) (bool, error) { 301 302 // retrieve the service event information for this chain 303 events := systemcontracts.ServiceEventsForChain(chain) 304 305 for _, serviceEvent := range events.All() { 306 if serviceEvent.EventType() == eventType { 307 return true, nil 308 } 309 } 310 311 return false, nil 312 }