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 }