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  }