-
Notifications
You must be signed in to change notification settings - Fork 180
/
Copy pathversion_beacon.go
199 lines (174 loc) · 6.11 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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 executing upcoming blocks. It ensures that Execution and Verification Nodes are
// using consistent versions of Cadence when executing the same 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()
}
// ProtocolStateVersionUpgrade is a service event emitted by the FlowServiceAccount
// to signal an upgrade to the Protocol State version. `NewProtocolStateVersion`
// must be strictly greater than the currently active Protocol State Version,
// otherwise the service event is ignored.
// If the node software supports `NewProtocolStateVersion`, then it uses the
// specified Protocol State Version, beginning with the first block `X` where BOTH:
// 1. The `ProtocolStateVersionUpgrade` service event has been sealed in X's ancestry
// 2. X.view >= `ActiveView`
//
// NOTE: A ProtocolStateVersionUpgrade event `E` is accepted while processing block `B`
// which seals `E` if and only if E.ActiveView > B.View + SafetyThreshold.
// SafetyThreshold is a protocol parameter set so that it is overwhelmingly likely that
// block `B` is finalized (ergo the protocol version switch at the specified view `E.ActiveView`)
// within any stretch of SafetyThreshold-many views.
// TODO: This concept mirrors `EpochCommitSafetyThreshold` and `versionBoundaryFreezePeriod`
// These parameters should be consolidated.
//
// Otherwise, the node software stops processing blocks, until it is manually updated
// to a compatible software version.
// The Protocol State version must be incremented when:
// - a change is made to the Protocol State Machine
// - a new key is added or removed from the Protocol State Key-Value Store
type ProtocolStateVersionUpgrade struct {
NewProtocolStateVersion uint64
ActiveView uint64
}
// EqualTo returns true if the two events are equivalent.
func (u *ProtocolStateVersionUpgrade) EqualTo(other *ProtocolStateVersionUpgrade) bool {
return u.NewProtocolStateVersion == other.NewProtocolStateVersion &&
u.ActiveView == other.ActiveView
}
// ServiceEvent returns the event as a generic ServiceEvent type.
func (u *ProtocolStateVersionUpgrade) ServiceEvent() ServiceEvent {
return ServiceEvent{
Type: ServiceEventProtocolStateVersionUpgrade,
Event: u,
}
}