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  }