github.com/onflow/flow-go@v0.33.17/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"
    25  )
    26  
    27  // ServiceEvent represents a service event, which is a special event that when
    28  // emitted from a service account smart contract, is propagated to the protocol
    29  // and included in blocks. Service events typically cause changes to the
    30  // protocol state. See EpochSetup and EpochCommit events in this package for examples.
    31  //
    32  // This type represents a generic service event and primarily exists to simplify
    33  // encoding and decoding.
    34  type ServiceEvent struct {
    35  	Type  ServiceEventType
    36  	Event interface{}
    37  }
    38  
    39  // ServiceEventList is a handy container to enable comparisons
    40  type ServiceEventList []ServiceEvent
    41  
    42  func (sel ServiceEventList) EqualTo(other ServiceEventList) (bool, error) {
    43  	if len(sel) != len(other) {
    44  		return false, nil
    45  	}
    46  
    47  	for i, se := range sel {
    48  		equalTo, err := se.EqualTo(&other[i])
    49  		if err != nil {
    50  			return false, fmt.Errorf(
    51  				"error while comparing service event index %d: %w",
    52  				i,
    53  				err,
    54  			)
    55  		}
    56  		if !equalTo {
    57  			return false, nil
    58  		}
    59  	}
    60  
    61  	return true, nil
    62  }
    63  
    64  type ServiceEventMarshaller interface {
    65  	Unmarshal(b []byte) (ServiceEvent, error)
    66  	UnmarshalWithType(
    67  		b []byte,
    68  		eventType ServiceEventType,
    69  	) (
    70  		ServiceEvent,
    71  		error,
    72  	)
    73  }
    74  
    75  type marshallerImpl struct {
    76  	MarshalFunc   func(v interface{}) ([]byte, error)
    77  	UnmarshalFunc func(data []byte, v interface{}) error
    78  }
    79  
    80  var (
    81  	ServiceEventJSONMarshaller = marshallerImpl{
    82  		MarshalFunc:   json.Marshal,
    83  		UnmarshalFunc: json.Unmarshal,
    84  	}
    85  	ServiceEventMSGPACKMarshaller = marshallerImpl{
    86  		MarshalFunc:   msgpack.Marshal,
    87  		UnmarshalFunc: msgpack.Unmarshal,
    88  	}
    89  	ServiceEventCBORMarshaller = marshallerImpl{
    90  		MarshalFunc:   cborcodec.EncMode.Marshal,
    91  		UnmarshalFunc: cbor.Unmarshal,
    92  	}
    93  )
    94  
    95  func (marshaller marshallerImpl) Unmarshal(b []byte) (
    96  	ServiceEvent,
    97  	error,
    98  ) {
    99  	var enc map[string]interface{}
   100  	err := marshaller.UnmarshalFunc(b, &enc)
   101  	if err != nil {
   102  		return ServiceEvent{}, err
   103  	}
   104  
   105  	tp, ok := enc["Type"].(string)
   106  	if !ok {
   107  		return ServiceEvent{}, fmt.Errorf("missing type key")
   108  	}
   109  	ev, ok := enc["Event"]
   110  	if !ok {
   111  		return ServiceEvent{}, fmt.Errorf("missing event key")
   112  	}
   113  
   114  	// re-marshal the event, we'll unmarshal it into the appropriate type
   115  	evb, err := marshaller.MarshalFunc(ev)
   116  	if err != nil {
   117  		return ServiceEvent{}, err
   118  	}
   119  
   120  	return marshaller.UnmarshalWithType(evb, ServiceEventType(tp))
   121  }
   122  
   123  func (marshaller marshallerImpl) UnmarshalWithType(
   124  	b []byte,
   125  	eventType ServiceEventType,
   126  ) (ServiceEvent, error) {
   127  	var event interface{}
   128  	switch eventType {
   129  	case ServiceEventSetup:
   130  		event = new(EpochSetup)
   131  	case ServiceEventCommit:
   132  		event = new(EpochCommit)
   133  	case ServiceEventVersionBeacon:
   134  		event = new(VersionBeacon)
   135  	default:
   136  		return ServiceEvent{}, fmt.Errorf("invalid type: %s", eventType)
   137  	}
   138  
   139  	err := marshaller.UnmarshalFunc(b, event)
   140  	if err != nil {
   141  		return ServiceEvent{},
   142  			fmt.Errorf(
   143  				"failed to unmarshal to service event ot type %s: %w",
   144  				eventType,
   145  				err,
   146  			)
   147  	}
   148  
   149  	return ServiceEvent{
   150  		Type:  eventType,
   151  		Event: event,
   152  	}, nil
   153  }
   154  
   155  func (se *ServiceEvent) UnmarshalJSON(b []byte) error {
   156  	e, err := ServiceEventJSONMarshaller.Unmarshal(b)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	*se = e
   161  	return nil
   162  }
   163  
   164  func (se *ServiceEvent) UnmarshalMsgpack(b []byte) error {
   165  	e, err := ServiceEventMSGPACKMarshaller.Unmarshal(b)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	*se = e
   170  	return nil
   171  }
   172  
   173  func (se *ServiceEvent) UnmarshalCBOR(b []byte) error {
   174  	e, err := ServiceEventCBORMarshaller.Unmarshal(b)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	*se = e
   179  	return nil
   180  }
   181  
   182  func (se *ServiceEvent) EqualTo(other *ServiceEvent) (bool, error) {
   183  	if se.Type != other.Type {
   184  		return false, nil
   185  	}
   186  	switch se.Type {
   187  	case ServiceEventSetup:
   188  		setup, ok := se.Event.(*EpochSetup)
   189  		if !ok {
   190  			return false, fmt.Errorf(
   191  				"internal invalid type for ServiceEventSetup: %T",
   192  				se.Event,
   193  			)
   194  		}
   195  		otherSetup, ok := other.Event.(*EpochSetup)
   196  		if !ok {
   197  			return false, fmt.Errorf(
   198  				"internal invalid type for ServiceEventSetup: %T",
   199  				other.Event,
   200  			)
   201  		}
   202  		return setup.EqualTo(otherSetup), nil
   203  
   204  	case ServiceEventCommit:
   205  		commit, ok := se.Event.(*EpochCommit)
   206  		if !ok {
   207  			return false, fmt.Errorf(
   208  				"internal invalid type for ServiceEventCommit: %T",
   209  				se.Event,
   210  			)
   211  		}
   212  		otherCommit, ok := other.Event.(*EpochCommit)
   213  		if !ok {
   214  			return false, fmt.Errorf(
   215  				"internal invalid type for ServiceEventCommit: %T",
   216  				other.Event,
   217  			)
   218  		}
   219  		return commit.EqualTo(otherCommit), nil
   220  
   221  	case ServiceEventVersionBeacon:
   222  		version, ok := se.Event.(*VersionBeacon)
   223  		if !ok {
   224  			return false, fmt.Errorf(
   225  				"internal invalid type for ServiceEventVersionBeacon: %T",
   226  				se.Event,
   227  			)
   228  		}
   229  		otherVersion, ok := other.Event.(*VersionBeacon)
   230  		if !ok {
   231  			return false,
   232  				fmt.Errorf(
   233  					"internal invalid type for ServiceEventVersionBeacon: %T",
   234  					other.Event,
   235  				)
   236  		}
   237  		return version.EqualTo(otherVersion), nil
   238  
   239  	default:
   240  		return false, fmt.Errorf("unknown serice event type: %s", se.Type)
   241  	}
   242  }