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  }