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 }