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