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  }