github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/service_event.go (about)

     1  package flow
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  
     7  	"github.com/fxamacker/cbor/v2"
     8  	"github.com/vmihailenco/msgpack/v4"
     9  
    10  	cborcodec "github.com/onflow/flow-go/model/encoding/cbor"
    11  )
    12  
    13  type ServiceEventType string
    14  
    15  // String returns the string representation of the service event type.
    16  // TODO: this should not be needed. We should use ServiceEventType directly everywhere.
    17  func (set ServiceEventType) String() string {
    18  	return string(set)
    19  }
    20  
    21  const (
    22  	ServiceEventSetup                       ServiceEventType = "setup"
    23  	ServiceEventCommit                      ServiceEventType = "commit"
    24  	ServiceEventVersionBeacon               ServiceEventType = "version-beacon"                 // VersionBeacon only controls version of ENs, describing software compatability via semantic versioning
    25  	ServiceEventProtocolStateVersionUpgrade ServiceEventType = "protocol-state-version-upgrade" // Protocol State version applies to all nodes and uses an _integer version_ of the _protocol_
    26  )
    27  
    28  // ServiceEvent represents a service event, which is a special event that when
    29  // emitted from a service account smart contract, is propagated to the protocol
    30  // and included in blocks. Service events typically cause changes to the
    31  // protocol state. See EpochSetup and EpochCommit events in this package for examples.
    32  //
    33  // This type represents a generic service event and primarily exists to simplify
    34  // encoding and decoding.
    35  type ServiceEvent struct {
    36  	Type  ServiceEventType
    37  	Event interface{}
    38  }
    39  
    40  // ServiceEventList is a handy container to enable comparisons
    41  type ServiceEventList []ServiceEvent
    42  
    43  func (sel ServiceEventList) EqualTo(other ServiceEventList) (bool, error) {
    44  	if len(sel) != len(other) {
    45  		return false, nil
    46  	}
    47  
    48  	for i, se := range sel {
    49  		equalTo, err := se.EqualTo(&other[i])
    50  		if err != nil {
    51  			return false, fmt.Errorf(
    52  				"error while comparing service event index %d: %w",
    53  				i,
    54  				err,
    55  			)
    56  		}
    57  		if !equalTo {
    58  			return false, nil
    59  		}
    60  	}
    61  
    62  	return true, nil
    63  }
    64  
    65  // ServiceEventMarshaller marshals and unmarshals all types of service events.
    66  type ServiceEventMarshaller interface {
    67  	// UnmarshalWrapped unmarshals the service event and returns it as a wrapped ServiceEvent type.
    68  	// The input bytes must be encoded as a generic wrapped ServiceEvent type.
    69  	// Forwards errors from the underlying marshaller (treat errors as you would from eg. json.Unmarshal)
    70  	UnmarshalWrapped(b []byte) (ServiceEvent, error)
    71  	// UnmarshalWithType unmarshals the service event and returns it as a wrapped ServiceEvent type.
    72  	// The input bytes must be encoded as a specific event type (for example, EpochSetup).
    73  	// Forwards errors from the underlying marshaller (treat errors as you would from eg. json.Unmarshal)
    74  	UnmarshalWithType(b []byte, eventType ServiceEventType) (ServiceEvent, error)
    75  }
    76  
    77  type marshallerImpl struct {
    78  	marshalFunc   func(v interface{}) ([]byte, error)
    79  	unmarshalFunc func(data []byte, v interface{}) error
    80  }
    81  
    82  var _ ServiceEventMarshaller = (*marshallerImpl)(nil)
    83  
    84  var (
    85  	// CAUTION: Json and MsgPack are to be used only for trusted data sources
    86  	ServiceEventJSONMarshaller = marshallerImpl{
    87  		marshalFunc:   json.Marshal,
    88  		unmarshalFunc: json.Unmarshal,
    89  	}
    90  	// CAUTION: Json and MsgPack are to be used only for trusted data sources
    91  	ServiceEventMSGPACKMarshaller = marshallerImpl{
    92  		marshalFunc:   msgpack.Marshal,
    93  		unmarshalFunc: msgpack.Unmarshal,
    94  	}
    95  	ServiceEventCBORMarshaller = marshallerImpl{
    96  		marshalFunc:   cborcodec.EncMode.Marshal,
    97  		unmarshalFunc: cbor.Unmarshal,
    98  	}
    99  )
   100  
   101  // UnmarshalWrapped unmarshals the service event `b` and returns it as a wrapped ServiceEvent type.
   102  // The input bytes must be encoded as a generic wrapped ServiceEvent type.
   103  // Forwards errors from the underlying marshaller (treat errors as you would from eg. json.Unmarshal)
   104  func (marshaller marshallerImpl) UnmarshalWrapped(b []byte) (ServiceEvent, error) {
   105  	var eventTypeWrapper struct {
   106  		Type ServiceEventType
   107  	}
   108  	err := marshaller.unmarshalFunc(b, &eventTypeWrapper)
   109  	if err != nil {
   110  		return ServiceEvent{}, err
   111  	}
   112  	eventType := eventTypeWrapper.Type
   113  
   114  	var event any
   115  	switch eventType {
   116  	case ServiceEventSetup:
   117  		event, err = unmarshalWrapped[EpochSetup](b, marshaller)
   118  	case ServiceEventCommit:
   119  		event, err = unmarshalWrapped[EpochCommit](b, marshaller)
   120  	case ServiceEventVersionBeacon:
   121  		event, err = unmarshalWrapped[VersionBeacon](b, marshaller)
   122  	case ServiceEventProtocolStateVersionUpgrade:
   123  		event, err = unmarshalWrapped[ProtocolStateVersionUpgrade](b, marshaller)
   124  	default:
   125  		return ServiceEvent{}, fmt.Errorf("invalid type: %s", eventType)
   126  	}
   127  	if err != nil {
   128  		return ServiceEvent{}, fmt.Errorf("failed to unmarshal to service event to type %s: %w", eventType, err)
   129  	}
   130  	return ServiceEvent{
   131  		Type:  eventType,
   132  		Event: event,
   133  	}, nil
   134  }
   135  
   136  // unmarshalWrapped is a helper function for UnmarshalWrapped which unmarshals the
   137  // Event portion of a ServiceEvent into a specific typed structure.
   138  // Forwards errors from the underlying marshaller (treat errors as you would from eg. json.Unmarshal)
   139  func unmarshalWrapped[E any](b []byte, marshaller marshallerImpl) (*E, error) {
   140  	wrapper := struct {
   141  		Type  ServiceEventType
   142  		Event E
   143  	}{}
   144  	err := marshaller.unmarshalFunc(b, &wrapper)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return &wrapper.Event, nil
   150  }
   151  
   152  // UnmarshalWithType unmarshals the service event and returns it as a wrapped ServiceEvent type.
   153  // The input bytes must be encoded as a specific event type (for example, EpochSetup).
   154  // Forwards errors from the underlying marshaller (treat errors as you would from eg. json.Unmarshal)
   155  func (marshaller marshallerImpl) UnmarshalWithType(b []byte, eventType ServiceEventType) (ServiceEvent, error) {
   156  	var event interface{}
   157  	switch eventType {
   158  	case ServiceEventSetup:
   159  		event = new(EpochSetup)
   160  	case ServiceEventCommit:
   161  		event = new(EpochCommit)
   162  	case ServiceEventVersionBeacon:
   163  		event = new(VersionBeacon)
   164  	case ServiceEventProtocolStateVersionUpgrade:
   165  		event = new(ProtocolStateVersionUpgrade)
   166  	default:
   167  		return ServiceEvent{}, fmt.Errorf("invalid type: %s", eventType)
   168  	}
   169  
   170  	err := marshaller.unmarshalFunc(b, event)
   171  	if err != nil {
   172  		return ServiceEvent{},
   173  			fmt.Errorf(
   174  				"failed to unmarshal to service event ot type %s: %w",
   175  				eventType,
   176  				err,
   177  			)
   178  	}
   179  
   180  	return ServiceEvent{
   181  		Type:  eventType,
   182  		Event: event,
   183  	}, nil
   184  }
   185  
   186  func (se *ServiceEvent) UnmarshalJSON(b []byte) error {
   187  	e, err := ServiceEventJSONMarshaller.UnmarshalWrapped(b)
   188  	if err != nil {
   189  		return err
   190  	}
   191  	*se = e
   192  	return nil
   193  }
   194  
   195  func (se *ServiceEvent) UnmarshalMsgpack(b []byte) error {
   196  	e, err := ServiceEventMSGPACKMarshaller.UnmarshalWrapped(b)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	*se = e
   201  	return nil
   202  }
   203  
   204  func (se *ServiceEvent) UnmarshalCBOR(b []byte) error {
   205  	e, err := ServiceEventCBORMarshaller.UnmarshalWrapped(b)
   206  	if err != nil {
   207  		return err
   208  	}
   209  	*se = e
   210  	return nil
   211  }
   212  
   213  // EqualTo checks whether two service events are equal, as defined by the underlying Event type.
   214  // Inputs must have already been independently validated and well-formed.
   215  // No errors are expected during normal operation.
   216  func (se *ServiceEvent) EqualTo(other *ServiceEvent) (bool, error) {
   217  	if se.Type != other.Type {
   218  		return false, nil
   219  	}
   220  	switch se.Type {
   221  	case ServiceEventSetup:
   222  		setup, ok := se.Event.(*EpochSetup)
   223  		if !ok {
   224  			return false, fmt.Errorf(
   225  				"internal invalid type for ServiceEventSetup: %T",
   226  				se.Event,
   227  			)
   228  		}
   229  		otherSetup, ok := other.Event.(*EpochSetup)
   230  		if !ok {
   231  			return false, fmt.Errorf(
   232  				"internal invalid type for ServiceEventSetup: %T",
   233  				other.Event,
   234  			)
   235  		}
   236  		return setup.EqualTo(otherSetup), nil
   237  
   238  	case ServiceEventCommit:
   239  		commit, ok := se.Event.(*EpochCommit)
   240  		if !ok {
   241  			return false, fmt.Errorf(
   242  				"internal invalid type for ServiceEventCommit: %T",
   243  				se.Event,
   244  			)
   245  		}
   246  		otherCommit, ok := other.Event.(*EpochCommit)
   247  		if !ok {
   248  			return false, fmt.Errorf(
   249  				"internal invalid type for ServiceEventCommit: %T",
   250  				other.Event,
   251  			)
   252  		}
   253  		return commit.EqualTo(otherCommit), nil
   254  
   255  	case ServiceEventVersionBeacon:
   256  		version, ok := se.Event.(*VersionBeacon)
   257  		if !ok {
   258  			return false, fmt.Errorf(
   259  				"internal invalid type for ServiceEventVersionBeacon: %T",
   260  				se.Event,
   261  			)
   262  		}
   263  		otherVersion, ok := other.Event.(*VersionBeacon)
   264  		if !ok {
   265  			return false,
   266  				fmt.Errorf(
   267  					"internal invalid type for ServiceEventVersionBeacon: %T",
   268  					other.Event,
   269  				)
   270  		}
   271  		return version.EqualTo(otherVersion), nil
   272  	case ServiceEventProtocolStateVersionUpgrade:
   273  		version, ok := se.Event.(*ProtocolStateVersionUpgrade)
   274  		if !ok {
   275  			return false, fmt.Errorf(
   276  				"internal invalid type for ProtocolStateVersionUpgrade: %T",
   277  				se.Event,
   278  			)
   279  		}
   280  		otherVersion, ok := other.Event.(*ProtocolStateVersionUpgrade)
   281  		if !ok {
   282  			return false,
   283  				fmt.Errorf(
   284  					"internal invalid type for ProtocolStateVersionUpgrade: %T",
   285  					other.Event,
   286  				)
   287  		}
   288  		return version.EqualTo(otherVersion), nil
   289  
   290  	default:
   291  		return false, fmt.Errorf("unknown serice event type: %s", se.Type)
   292  	}
   293  }