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