github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/version_beacon.go (about) 1 package flow 2 3 import ( 4 "bytes" 5 "fmt" 6 7 "github.com/coreos/go-semver/semver" 8 ) 9 10 // VersionBoundary represents a boundary between semver versions. 11 // BlockHeight is the first block height that must be run by the given Version (inclusive). 12 // Version is a semver string. 13 type VersionBoundary struct { 14 BlockHeight uint64 15 Version string 16 } 17 18 func (v VersionBoundary) Semver() (*semver.Version, error) { 19 return semver.NewVersion(v.Version) 20 } 21 22 // VersionBeacon represents a service event specifying the required software versions 23 // for executing upcoming blocks. It ensures that Execution and Verification Nodes are 24 // using consistent versions of Cadence when executing the same blocks. 25 // 26 // It contains a VersionBoundaries field, which is an ordered list of VersionBoundary 27 // (sorted by VersionBoundary.BlockHeight). While heights are strictly 28 // increasing, versions must be equal or greater when compared using semver semantics. 29 // It must contain at least one entry. The first entry is for a past block height. 30 // The remaining entries are for all future block heights. Future version boundaries 31 // can be removed, in which case the emitted event will not contain the removed version 32 // boundaries. 33 // VersionBeacon is produced by the NodeVersionBeacon smart contract. 34 // 35 // Sequence is the event sequence number, which can be used to verify that no event has been 36 // skipped by the follower. Every time the smart contract emits a new event, it increments 37 // the sequence number by one. 38 type VersionBeacon struct { 39 VersionBoundaries []VersionBoundary 40 Sequence uint64 41 } 42 43 // SealedVersionBeacon is a VersionBeacon with a SealHeight field. 44 // Version beacons are effective only after the results containing the version beacon 45 // are sealed. 46 type SealedVersionBeacon struct { 47 *VersionBeacon 48 SealHeight uint64 49 } 50 51 func (v *VersionBeacon) ServiceEvent() ServiceEvent { 52 return ServiceEvent{ 53 Type: ServiceEventVersionBeacon, 54 Event: v, 55 } 56 } 57 58 // EqualTo returns true if two VersionBeacons are equal. 59 // If any of the VersionBeacons has a malformed version, it will return false. 60 func (v *VersionBeacon) EqualTo(other *VersionBeacon) bool { 61 62 if v.Sequence != other.Sequence { 63 return false 64 } 65 66 if len(v.VersionBoundaries) != len(other.VersionBoundaries) { 67 return false 68 } 69 70 for i, v := range v.VersionBoundaries { 71 other := other.VersionBoundaries[i] 72 73 if v.BlockHeight != other.BlockHeight { 74 return false 75 } 76 77 v1, err := v.Semver() 78 if err != nil { 79 return false 80 } 81 v2, err := other.Semver() 82 if err != nil { 83 return false 84 } 85 if !v1.Equal(*v2) { 86 return false 87 } 88 } 89 90 return true 91 } 92 93 // Validate validates the internal structure of a flow.VersionBeacon. 94 // An error with an appropriate message is returned 95 // if any validation fails. 96 func (v *VersionBeacon) Validate() error { 97 eventError := func(format string, args ...interface{}) error { 98 args = append([]interface{}{v.Sequence}, args...) 99 return fmt.Errorf( 100 "version beacon (sequence=%d) error: "+format, 101 args..., 102 ) 103 } 104 105 if len(v.VersionBoundaries) == 0 { 106 return eventError("required version boundaries empty") 107 } 108 109 var previousHeight uint64 110 var previousVersion *semver.Version 111 for i, boundary := range v.VersionBoundaries { 112 version, err := boundary.Semver() 113 if err != nil { 114 return eventError( 115 "invalid semver %s for version boundary (height=%d) (index=%d): %w", 116 boundary.Version, 117 boundary.BlockHeight, 118 i, 119 err, 120 ) 121 } 122 123 if i != 0 && previousHeight >= boundary.BlockHeight { 124 return eventError( 125 "higher requirement (index=%d) height %d "+ 126 "at or below previous height (index=%d) %d", 127 i, 128 boundary.BlockHeight, 129 i-1, 130 previousHeight, 131 ) 132 } 133 134 if i != 0 && version.LessThan(*previousVersion) { 135 return eventError( 136 "higher requirement (index=%d) semver %s "+ 137 "lower than previous (index=%d) %s", 138 i, 139 version, 140 i-1, 141 previousVersion, 142 ) 143 } 144 145 previousVersion = version 146 previousHeight = boundary.BlockHeight 147 } 148 149 return nil 150 } 151 152 func (v *VersionBeacon) String() string { 153 var buffer bytes.Buffer 154 for _, boundary := range v.VersionBoundaries { 155 buffer.WriteString(fmt.Sprintf("%d:%s ", boundary.BlockHeight, boundary.Version)) 156 } 157 return buffer.String() 158 } 159 160 // ProtocolStateVersionUpgrade is a service event emitted by the FlowServiceAccount 161 // to signal an upgrade to the Protocol State version. `NewProtocolStateVersion` 162 // must be strictly greater than the currently active Protocol State Version, 163 // otherwise the service event is ignored. 164 // If the node software supports `NewProtocolStateVersion`, then it uses the 165 // specified Protocol State Version, beginning with the first block `X` where BOTH: 166 // 1. The `ProtocolStateVersionUpgrade` service event has been sealed in X's ancestry 167 // 2. X.view >= `ActiveView` 168 // 169 // NOTE: A ProtocolStateVersionUpgrade event `E` is accepted while processing block `B` 170 // which seals `E` if and only if E.ActiveView > B.View + SafetyThreshold. 171 // SafetyThreshold is a protocol parameter set so that it is overwhelmingly likely that 172 // block `B` is finalized (ergo the protocol version switch at the specified view `E.ActiveView`) 173 // within any stretch of SafetyThreshold-many views. 174 // TODO: This concept mirrors `EpochCommitSafetyThreshold` and `versionBoundaryFreezePeriod` 175 // These parameters should be consolidated. 176 // 177 // Otherwise, the node software stops processing blocks, until it is manually updated 178 // to a compatible software version. 179 // The Protocol State version must be incremented when: 180 // - a change is made to the Protocol State Machine 181 // - a new key is added or removed from the Protocol State Key-Value Store 182 type ProtocolStateVersionUpgrade struct { 183 NewProtocolStateVersion uint64 184 ActiveView uint64 185 } 186 187 // EqualTo returns true if the two events are equivalent. 188 func (u *ProtocolStateVersionUpgrade) EqualTo(other *ProtocolStateVersionUpgrade) bool { 189 return u.NewProtocolStateVersion == other.NewProtocolStateVersion && 190 u.ActiveView == other.ActiveView 191 } 192 193 // ServiceEvent returns the event as a generic ServiceEvent type. 194 func (u *ProtocolStateVersionUpgrade) ServiceEvent() ServiceEvent { 195 return ServiceEvent{ 196 Type: ServiceEventProtocolStateVersionUpgrade, 197 Event: u, 198 } 199 }