github.com/onflow/flow-go@v0.33.17/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 upcoming blocks. 24 // 25 // It contains a VersionBoundaries field, which is an ordered list of VersionBoundary 26 // (sorted by VersionBoundary.BlockHeight). While heights are strictly 27 // increasing, versions must be equal or greater when compared using semver semantics. 28 // It must contain at least one entry. The first entry is for a past block height. 29 // The remaining entries are for all future block heights. Future version boundaries 30 // can be removed, in which case the emitted event will not contain the removed version 31 // boundaries. 32 // VersionBeacon is produced by the NodeVersionBeacon smart contract. 33 // 34 // Sequence is the event sequence number, which can be used to verify that no event has been 35 // skipped by the follower. Every time the smart contract emits a new event, it increments 36 // the sequence number by one. 37 type VersionBeacon struct { 38 VersionBoundaries []VersionBoundary 39 Sequence uint64 40 } 41 42 // SealedVersionBeacon is a VersionBeacon with a SealHeight field. 43 // Version beacons are effective only after the results containing the version beacon 44 // are sealed. 45 type SealedVersionBeacon struct { 46 *VersionBeacon 47 SealHeight uint64 48 } 49 50 func (v *VersionBeacon) ServiceEvent() ServiceEvent { 51 return ServiceEvent{ 52 Type: ServiceEventVersionBeacon, 53 Event: v, 54 } 55 } 56 57 // EqualTo returns true if two VersionBeacons are equal. 58 // If any of the VersionBeacons has a malformed version, it will return false. 59 func (v *VersionBeacon) EqualTo(other *VersionBeacon) bool { 60 61 if v.Sequence != other.Sequence { 62 return false 63 } 64 65 if len(v.VersionBoundaries) != len(other.VersionBoundaries) { 66 return false 67 } 68 69 for i, v := range v.VersionBoundaries { 70 other := other.VersionBoundaries[i] 71 72 if v.BlockHeight != other.BlockHeight { 73 return false 74 } 75 76 v1, err := v.Semver() 77 if err != nil { 78 return false 79 } 80 v2, err := other.Semver() 81 if err != nil { 82 return false 83 } 84 if !v1.Equal(*v2) { 85 return false 86 } 87 } 88 89 return true 90 } 91 92 // Validate validates the internal structure of a flow.VersionBeacon. 93 // An error with an appropriate message is returned 94 // if any validation fails. 95 func (v *VersionBeacon) Validate() error { 96 eventError := func(format string, args ...interface{}) error { 97 args = append([]interface{}{v.Sequence}, args...) 98 return fmt.Errorf( 99 "version beacon (sequence=%d) error: "+format, 100 args..., 101 ) 102 } 103 104 if len(v.VersionBoundaries) == 0 { 105 return eventError("required version boundaries empty") 106 } 107 108 var previousHeight uint64 109 var previousVersion *semver.Version 110 for i, boundary := range v.VersionBoundaries { 111 version, err := boundary.Semver() 112 if err != nil { 113 return eventError( 114 "invalid semver %s for version boundary (height=%d) (index=%d): %w", 115 boundary.Version, 116 boundary.BlockHeight, 117 i, 118 err, 119 ) 120 } 121 122 if i != 0 && previousHeight >= boundary.BlockHeight { 123 return eventError( 124 "higher requirement (index=%d) height %d "+ 125 "at or below previous height (index=%d) %d", 126 i, 127 boundary.BlockHeight, 128 i-1, 129 previousHeight, 130 ) 131 } 132 133 if i != 0 && version.LessThan(*previousVersion) { 134 return eventError( 135 "higher requirement (index=%d) semver %s "+ 136 "lower than previous (index=%d) %s", 137 i, 138 version, 139 i-1, 140 previousVersion, 141 ) 142 } 143 144 previousVersion = version 145 previousHeight = boundary.BlockHeight 146 } 147 148 return nil 149 } 150 151 func (v *VersionBeacon) String() string { 152 var buffer bytes.Buffer 153 for _, boundary := range v.VersionBoundaries { 154 buffer.WriteString(fmt.Sprintf("%d:%s ", boundary.BlockHeight, boundary.Version)) 155 } 156 return buffer.String() 157 }